From bc2a29834a3633f0b5e67eb0171c89484cca0ccc Mon Sep 17 00:00:00 2001 From: Eike Middell Date: Tue, 24 Jun 2025 16:52:12 +0200 Subject: [PATCH 1/4] don't include docs and .github in sdist --- pyproject.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 78c952d..4142748 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,6 +80,11 @@ requires = [ "hatch-conda>=0.5.2", ] +[tool.hatch.build.targets.sdist] +exclude = [ + "/.github", + "/docs", +] [tool.hatch.envs.default] type = "conda" From 59362d8a029e8d3e6aab5c276c4d32535cf963e7 Mon Sep 17 00:00:00 2001 From: Eike Middell Date: Tue, 24 Jun 2025 16:52:51 +0200 Subject: [PATCH 2/4] Describe version updates in installation docs --- docs/getting_started/installation.md | 43 ++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/docs/getting_started/installation.md b/docs/getting_started/installation.md index c08e117..7467384 100644 --- a/docs/getting_started/installation.md +++ b/docs/getting_started/installation.md @@ -31,6 +31,12 @@ this checked-out directory and run: $ conda env create -n cedalion -f environment_dev.yml ``` +Select a descriptive name for the environment. Keep in mind that over time you +may want to have multiple environments in parallel, for example when you are working +on two projects that use different versions of cedalion. A naming scheme that +includes the current date ('cedalion_YYMMDD') is practical but you are free to choose +whatever works best for you. + Afterwards activate the environment and add an editable install of `cedalion` to it: ``` $ conda activate cedalion @@ -93,6 +99,43 @@ $ hatch run build_docs The same procedure as above applies. However, make sure to use a released version from the main branch. +## Updating + +### Updating between releases + +In the past, you cloned the git repository to a local directory using the last released +version on the main branch. During installation, you created a conda environment and +added cedalion from that directory to the environment. + +Updating to a newer version is easiest done by cloning the git repository again to a +different folder and creating a new environment. This way, the installed version remains +usable. It also guarantees that the new environment contains any updated dependencies. + +The following example uses the version suffix in the directory and environment name. + +``` +$ git clone git@github.com:ibs-lab/cedalion.git path/to/cedalion_v25.1.0 +$ cd path/to/cedalion_v25.1.0 +$ conda env create -n cedalion_v25.1.0 -f environment_dev.yml +$ conda activate cedalion_v25.1.0 +$ pip install -e . +``` + +Switching between the different cedalion versions is then possible by activating the +corresponding environment. + +### During development + +Cedalion's development happens in the dev branch. The cloned git repository contains the +complete development history and maintains the connection to our main repository at +GitHub. By pulling the recent changes from there or by checking out a commit from the past +the cedalion directory can be brought to any desired version. The conda environment +will then use the checked out version. + +Keep in mind that the cedalion's dependencies changed over time. When pulling recent +changes from dev you might need to update or recreate the environment. + + ## Container Environments From 825fbb4353672330a9bc023bfd1bc30706ae77f4 Mon Sep 17 00:00:00 2001 From: Eike Middell Date: Wed, 25 Jun 2025 11:59:07 +0200 Subject: [PATCH 3/4] fix pmcx version inconsistency and update docs to use pip --no-deps --- docs/getting_started/installation.md | 4 ++-- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/getting_started/installation.md b/docs/getting_started/installation.md index 7467384..710bf8f 100644 --- a/docs/getting_started/installation.md +++ b/docs/getting_started/installation.md @@ -40,7 +40,7 @@ whatever works best for you. Afterwards activate the environment and add an editable install of `cedalion` to it: ``` $ conda activate cedalion -$ pip install -e . +$ pip install -e . --no-deps ``` This will also install Jupyter Notebook to run the example notebooks. @@ -118,7 +118,7 @@ $ git clone git@github.com:ibs-lab/cedalion.git path/to/cedalion_v25.1.0 $ cd path/to/cedalion_v25.1.0 $ conda env create -n cedalion_v25.1.0 -f environment_dev.yml $ conda activate cedalion_v25.1.0 -$ pip install -e . +$ pip install -e . --no-deps ``` Switching between the different cedalion versions is then possible by activating the diff --git a/pyproject.toml b/pyproject.toml index 4142748..877ca95 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ dependencies = [ "pywavefront==1.3.*", "setuptools-scm", "snirf==0.8.*", - "pmcx==0.4.2", + "pmcx>=0.3.3", "pmcxcl==0.3.1", "pyxdf==1.17.0", ] From 27773aad5fe3d9c9a097e7f3fa56322e7fee97ec Mon Sep 17 00:00:00 2001 From: "[shankell212]" <[shankell212@gmail.com]> Date: Thu, 26 Jun 2025 10:10:48 -0400 Subject: [PATCH 4/4] added function to save plot probe images withina script --- src/cedalion/vis/plot_probe.py | 83 ++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/src/cedalion/vis/plot_probe.py b/src/cedalion/vis/plot_probe.py index 56d06a3..50407db 100644 --- a/src/cedalion/vis/plot_probe.py +++ b/src/cedalion/vis/plot_probe.py @@ -18,6 +18,7 @@ from matplotlib.backends.backend_qtagg import NavigationToolbar2QT as NavigationToolbar from matplotlib.backends.qt_compat import QtWidgets from matplotlib.figure import Figure +import matplotlib.pyplot as plt import cedalion import cedalion.typing as cdt @@ -669,3 +670,85 @@ def run_vis( main_gui = _MAIN_GUI(snirfData=blockaverage, geo2d=geo2d, geo3d=geo3d) main_gui.show() sys.exit(app.exec()) + + +def save_plot_probe_image( + blockaverage, + geo2d, + geo3d, + out_file="probe_plot.png", + xscale=1.0, + yscale=1.0, + title=None, + show_optode_labels=True, + show_meas_lines=False, +): + + # Extract probe layout + sPos = geo2d.sel(label=["S" in str(l) for l in geo2d.label.values]) + dPos = geo2d.sel(label=["D" in str(l) for l in geo2d.label.values]) + sourcePos3D = geo3d.sel(label=["S" in str(l) for l in geo3d.label.values]) + detectorPos3D = geo3d.sel(label=["D" in str(l) for l in geo3d.label.values]) + + sPosVal = sPos.values + dPosVal = dPos.values + + src_idx = [np.where(sPos.label == s)[0][0] for s in blockaverage.source.values] + det_idx = [np.where(dPos.label == d)[0][0] for d in blockaverage.detector.values] + + chan_dist = np.linalg.norm(sourcePos3D.values[src_idx] - detectorPos3D.values[det_idx], axis=1) + + # Normalize positions for plotting + all_xy = np.vstack((sPosVal, dPosVal)) + scale = max(all_xy[:, 0].ptp(), all_xy[:, 1].ptp()) + sxy = (sPosVal - all_xy.mean(axis=0)) / scale + dxy = (dPosVal - all_xy.mean(axis=0)) / scale + + sx, sy = sxy[:, 0], sxy[:, 1] + dx, dy = dxy[:, 0], dxy[:, 1] + + # Midpoints of channels + mx = (sx[src_idx] + dx[det_idx]) / 2 + my = (sy[src_idx] + dy[det_idx]) / 2 + + # Time and HRF + t = blockaverage.reltime.values + hrf = blockaverage.values + trial_idx = 0 # First condition only + chrom_colors = [[0.862, 0.078, 0.235], [0, 0, 0.8]] # HbO and HbR + + fig, ax = plt.subplots(figsize=(12, 12)) + + for i_ch in range(len(mx)): + for i_col in range(hrf.shape[2]): + x = mx[i_ch] + xscale * 0.1 * (t - t[0]) / (t[-1] - t[0]) + y = my[i_ch] + yscale * 0.1 * (hrf[trial_idx, i_ch, i_col, :] - hrf.min()) / (hrf.max() - hrf.min()) + ax.plot(x, y, color=chrom_colors[i_col], lw=0.7) + + # Optodes + ax.plot(sx, sy, 'ro', label='Sources', alpha=0.6) + ax.plot(dx, dy, 'bo', label='Detectors', alpha=0.6) + + # # Measurement lines + # for si, di in zip(src_idx, det_idx): + # ax.plot([sx[si], dx[di]], [sy[si], dy[di]], linestyle='--', color='gray', alpha=0.3) + + if show_optode_labels: + for i, label in enumerate(sPos.label.values): + ax.text(sx[i], sy[i], str(label), color="r", fontsize=8, ha="center", va="center") + for i, label in enumerate(dPos.label.values): + ax.text(dx[i], dy[i], str(label), color="b", fontsize=8, ha="center", va="center") + + if show_meas_lines: + for si, di in zip(src_idx, det_idx): + ax.plot([sx[si], dx[di]], [sy[si], dy[di]], linestyle='--', color='gray', alpha=0.3) + + if title: + ax.set_title(title) + + ax.axis('off') + ax.set_aspect('equal') + plt.tight_layout() + plt.savefig(out_file, dpi=300) + plt.close() + print(f"Saved probe plot to: {out_file}")