diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..82d27b5 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,18 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..2a41b1e --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..accb17d --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/other.xml b/.idea/other.xml new file mode 100644 index 0000000..a708ec7 --- /dev/null +++ b/.idea/other.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/pypi.iml b/.idea/pypi.iml new file mode 100644 index 0000000..4c8c7b3 --- /dev/null +++ b/.idea/pypi.iml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/docs/.DS_Store b/docs/.DS_Store new file mode 100644 index 0000000..b8af6b3 Binary files /dev/null and b/docs/.DS_Store differ diff --git a/docs/examples/aif/plot_aif_parker.py b/docs/examples/aif/plot_aif_parker.py index 9e30887..4f3a82d 100644 --- a/docs/examples/aif/plot_aif_parker.py +++ b/docs/examples/aif/plot_aif_parker.py @@ -29,7 +29,7 @@ plt.show() # %% -# The bolus arrival time (BAT) defaults to 30s. What happens if we change it? Let's try, by changing it in steps of 30s: +# The bolus arrival time (BAT) defaults to 0s. What happens if we change it? Let's try, by changing it in steps of 30s: ca = osipi.aif_parker(t, BAT=0) plt.plot(t, ca, 'b-', label='BAT = 0s') @@ -44,21 +44,5 @@ plt.legend() plt.show() -# %% -# the dose defaults to 0.1- what happens if we change it too? - -ca = osipi.aif_parker(t, BAT=0, dose=0.05) -plt.plot(t, ca, 'b-', label='BAT = 0s, dose = 0.05') -ca = osipi.aif_parker(t, BAT=30, dose=0.1) -plt.plot(t, ca, 'r-', label='BAT = 30s, dose = 0.1') -ca = osipi.aif_parker(t, BAT=60, dose=0.2) -plt.plot(t, ca, 'g-', label='BAT = 60s, dose = 0.2') -ca = osipi.aif_parker(t, BAT=90, dose=0.3) -plt.plot(t, ca, 'm-', label='BAT = 90s, dose = 0.3') -plt.xlabel('Time (sec)') -plt.ylabel('Plasma concentration (mM)') -plt.legend() -plt.show() - # Choose the last image as a thumbnail for the gallery # sphinx_gallery_thumbnail_number = -1 diff --git a/docs/examples/aif/plot_dummy.py b/docs/examples/aif/plot_dummy.py index 9069e41..1f4d326 100644 --- a/docs/examples/aif/plot_dummy.py +++ b/docs/examples/aif/plot_dummy.py @@ -44,21 +44,5 @@ plt.legend() plt.show() -# %% -# the dose defaults to 0.1- what happens if we change it too? - -ca = osipi.aif_parker(t, BAT=0, dose=0.05) -plt.plot(t, ca, 'b-', label='BAT = 0s, dose = 0.05') -ca = osipi.aif_parker(t, BAT=30, dose=0.1) -plt.plot(t, ca, 'r-', label='BAT = 30s, dose = 0.1') -ca = osipi.aif_parker(t, BAT=60, dose=0.2) -plt.plot(t, ca, 'g-', label='BAT = 60s, dose = 0.2') -ca = osipi.aif_parker(t, BAT=90, dose=0.3) -plt.plot(t, ca, 'm-', label='BAT = 90s, dose = 0.3') -plt.xlabel('Time (sec)') -plt.ylabel('Plasma concentration (mM)') -plt.legend() -plt.show() - # Choose the last image as a thumbnail for the gallery # sphinx_gallery_thumbnail_number = -1 diff --git a/docs/examples/tissue/plot_dummy.py b/docs/examples/tissue/plot_dummy.py deleted file mode 100644 index 6cde113..0000000 --- a/docs/examples/tissue/plot_dummy.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -==================== -Dummy script as demo -==================== - -Dummy script to show the examples structure. -""" - -import numpy as np -import matplotlib.pyplot as plt -import osipi - -# %% -# Generate synthetic AIF with default settings and plot the result. - -# Define time points in units of seconds - in this case we use a time resolution of 0.5 sec and a total duration of 6 minutes. -t = np.arange(0, 6*60, 0.5) - -# Create an AIF with default settings -ca = osipi.aif_parker(t) - -# Plot the AIF over the full range -plt.plot(t, ca) -plt.show() - - -# Choose the last image as a thumbnail for the gallery -# sphinx_gallery_thumbnail_number = -1 diff --git a/docs/examples/tissue/plot_tofts.py b/docs/examples/tissue/plot_tofts.py new file mode 100644 index 0000000..dc22974 --- /dev/null +++ b/docs/examples/tissue/plot_tofts.py @@ -0,0 +1,52 @@ +""" +==================== +The Tofts model +==================== + +Simulating tissue concentrations from Tofts model with different settings. +""" + +# %% +# Import necessary packages +import numpy as np +import matplotlib.pyplot as plt +import osipi + +# %% +# Generate Parker AIF with default settings. + +# Define time points in units of seconds - in this case we use a time resolution of 1 sec and a total duration of 6 minutes. +t = np.arange(0, 6*60, 1) + +# Create an AIF with default settings +ca = osipi.aif_parker(t) + +# %% +# Plot the tissue concentrations for an extracellular volume fraction of 0.2 and 3 different transfer rate constants of 0.05, 0.2 and 0.6 /min +Ktrans = [0.05, 0.2, 0.6] # in units of 1/min +ve = 0.2 # volume fraction between 0 and 1 +ct = osipi.tofts(t, ca, Ktrans=Ktrans[0], ve=ve) +plt.plot(t, ct, 'b-', label=f'Ktrans = {Ktrans[0]} /min') +ct = osipi.tofts(t, ca, Ktrans[1], ve) +plt.plot(t, ct, 'g-', label=f'Ktrans = {Ktrans[1]} /min') +ct = osipi.tofts(t, ca, Ktrans[2], ve) +plt.plot(t, ct, 'm-', label=f'Ktrans = {Ktrans[2]} /min') +plt.xlabel('Time (sec)') +plt.ylabel('Tissue concentration (mM)') +plt.legend() +plt.show() + +# %% +# Comparing different discretization methods for an extracellular volume fraction of 0.2 and Ktrans of 0.2 /min +ct = osipi.tofts(t, ca, Ktrans=Ktrans[1], ve=ve) # Defaults to Convolution +plt.plot(t, ct, 'b-', label='Convolution') +ct = osipi.tofts(t, ca, Ktrans=Ktrans[1], ve=ve, discretization_method='exp') +plt.plot(t, ct, 'g-', label='Exponential Convolution') +plt.title(f'Ktrans = {Ktrans[1]} /min') +plt.xlabel('Time (sec)') +plt.ylabel('Tissue concentration (mM)') +plt.legend() +plt.show() + +# Choose the last image as a thumbnail for the gallery +# sphinx_gallery_thumbnail_number = -1 diff --git a/docs/source/.DS_Store b/docs/source/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/docs/source/.DS_Store differ diff --git a/docs/source/developers_guide/index.rst b/docs/source/developers_guide/index.rst index b2515a7..bf2c080 100644 --- a/docs/source/developers_guide/index.rst +++ b/docs/source/developers_guide/index.rst @@ -36,7 +36,7 @@ If you want to edit the documentation of a specific function, then you need to f How to contribute tests *********************** -Beyond documentation and functionality, solid testing is equally critical for ensuring long-term stability of a package. `osipi` uses a continuous integration model where all tests are run before each push to the central respository. This is important because often changes at one part of the code, even if tested well locally, can have unintended consequences at other parts. The testing prevents that these errors propagate and destablize parts of the package. +Beyond documentation and functionality, solid testing is equally critical for ensuring long-term stability of a package. `osipi` uses a continuous integration model where all tests are run before each push to the central repository. This is important because often changes at one part of the code, even if tested well locally, can have unintended consequences at other parts. The testing prevents that these errors propagate and destablize parts of the package. If you find a bug in any part of the code, this obviously points to a flaw in the code, but it also reveals a gap in the testing. It is critical when this happens that both the code AND the tests are reviewed to ensure that in future a scenario of this type is picked up during testing. @@ -47,7 +47,7 @@ The tests are defined in the folder `osipi-tests`. How to contribute functionality ******************************* -OSIPI is always happy to recieve new functionality for inclusion in the `osipi` package. This can be code that addresses a gap in the current functionality, or it can be code that improves the performance of a current implementation. Improvements can consist of extending the functionality (e.g. with new optional arguments), user friendliness or consistency, improvement of the accuracy or precision in the results, computation time, or platform independence, or improved documentation or code structure. +OSIPI is always happy to receive new functionality for inclusion in the `osipi` package. This can be code that addresses a gap in the current functionality, or it can be code that improves the performance of a current implementation. Improvements can consist of extending the functionality (e.g. with new optional arguments), user friendliness or consistency, improvement of the accuracy or precision in the results, computation time, or platform independence, or improved documentation or code structure. Contribution of functionality generally proceeds in two steps. In the first step you submit your code to the primary *contributions* repository as explained in its `wiki `_. The task force will catalogue your code in the contributions repository and test it as explained in the `guidance `_. Afterwards, if it is found to address a gap in `osipi` and/or improve existing functionality, you will be invited to submit a pull request to `osipi` containing your contribution formatted as required by the package. diff --git a/docs/source/generated/.DS_Store b/docs/source/generated/.DS_Store new file mode 100644 index 0000000..e717588 Binary files /dev/null and b/docs/source/generated/.DS_Store differ diff --git a/docs/source/generated/api/osipi.extended_tofts.rst b/docs/source/generated/api/osipi.extended_tofts.rst new file mode 100644 index 0000000..48a2d6e --- /dev/null +++ b/docs/source/generated/api/osipi.extended_tofts.rst @@ -0,0 +1,18 @@ +osipi.extended\_tofts +===================== + + +.. currentmodule:: osipi + + + +.. autofunction:: extended_tofts + + + + + +.. minigallery:: osipi.extended_tofts + :add-heading: + + diff --git a/docs/source/generated/api/osipi.tofts.rst b/docs/source/generated/api/osipi.tofts.rst new file mode 100644 index 0000000..69f53b7 --- /dev/null +++ b/docs/source/generated/api/osipi.tofts.rst @@ -0,0 +1,18 @@ +osipi.tofts +=========== + + +.. currentmodule:: osipi + + + +.. autofunction:: tofts + + + + + +.. minigallery:: osipi.tofts + :add-heading: + + diff --git a/docs/source/generated/backreferences/osipi.aif_parker.examples b/docs/source/generated/backreferences/osipi.aif_parker.examples index c88cde9..955d535 100644 --- a/docs/source/generated/backreferences/osipi.aif_parker.examples +++ b/docs/source/generated/backreferences/osipi.aif_parker.examples @@ -56,24 +56,24 @@ Examples using ``osipi.aif_parker`` .. raw:: html -
+
.. only:: html - .. image:: /generated/examples/tissue/images/thumb/sphx_glr_plot_dummy_thumb.png + .. image:: /generated/examples/tissue/images/thumb/sphx_glr_plot_tofts_thumb.png :alt: - :ref:`sphx_glr_generated_examples_tissue_plot_dummy.py` + :ref:`sphx_glr_generated_examples_tissue_plot_tofts.py` .. raw:: html -
Dummy script as demo
+
The Tofts model
.. only:: not html - * :ref:`sphx_glr_generated_examples_tissue_plot_dummy.py` + * :ref:`sphx_glr_generated_examples_tissue_plot_tofts.py` .. raw:: html diff --git a/docs/source/generated/backreferences/osipi.extended_tofts.examples b/docs/source/generated/backreferences/osipi.extended_tofts.examples new file mode 100644 index 0000000..e69de29 diff --git a/docs/source/generated/backreferences/osipi.tofts.examples b/docs/source/generated/backreferences/osipi.tofts.examples new file mode 100644 index 0000000..307717e --- /dev/null +++ b/docs/source/generated/backreferences/osipi.tofts.examples @@ -0,0 +1,39 @@ + + +Examples using ``osipi.tofts`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +.. start-sphx-glr-thumbnails + + +.. raw:: html + +
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /generated/examples/tissue/images/thumb/sphx_glr_plot_tofts_thumb.png + :alt: + + :ref:`sphx_glr_generated_examples_tissue_plot_tofts.py` + +.. raw:: html + +
The Tofts model
+
+ + +.. only:: not html + + * :ref:`sphx_glr_generated_examples_tissue_plot_tofts.py` + +.. raw:: html + +
+ diff --git a/docs/source/generated/examples/aif/images/sphx_glr_plot_aif_parker_001.png b/docs/source/generated/examples/aif/images/sphx_glr_plot_aif_parker_001.png index 2592fdf..ebf46b3 100644 Binary files a/docs/source/generated/examples/aif/images/sphx_glr_plot_aif_parker_001.png and b/docs/source/generated/examples/aif/images/sphx_glr_plot_aif_parker_001.png differ diff --git a/docs/source/generated/examples/aif/images/sphx_glr_plot_aif_parker_002.png b/docs/source/generated/examples/aif/images/sphx_glr_plot_aif_parker_002.png index 89f2262..90f32a6 100644 Binary files a/docs/source/generated/examples/aif/images/sphx_glr_plot_aif_parker_002.png and b/docs/source/generated/examples/aif/images/sphx_glr_plot_aif_parker_002.png differ diff --git a/docs/source/generated/examples/aif/images/sphx_glr_plot_aif_parker_003.png b/docs/source/generated/examples/aif/images/sphx_glr_plot_aif_parker_003.png deleted file mode 100644 index 6812775..0000000 Binary files a/docs/source/generated/examples/aif/images/sphx_glr_plot_aif_parker_003.png and /dev/null differ diff --git a/docs/source/generated/examples/aif/images/sphx_glr_plot_dummy_001.png b/docs/source/generated/examples/aif/images/sphx_glr_plot_dummy_001.png index 2592fdf..ebf46b3 100644 Binary files a/docs/source/generated/examples/aif/images/sphx_glr_plot_dummy_001.png and b/docs/source/generated/examples/aif/images/sphx_glr_plot_dummy_001.png differ diff --git a/docs/source/generated/examples/aif/images/sphx_glr_plot_dummy_002.png b/docs/source/generated/examples/aif/images/sphx_glr_plot_dummy_002.png index 89f2262..90f32a6 100644 Binary files a/docs/source/generated/examples/aif/images/sphx_glr_plot_dummy_002.png and b/docs/source/generated/examples/aif/images/sphx_glr_plot_dummy_002.png differ diff --git a/docs/source/generated/examples/aif/images/sphx_glr_plot_dummy_003.png b/docs/source/generated/examples/aif/images/sphx_glr_plot_dummy_003.png deleted file mode 100644 index 6812775..0000000 Binary files a/docs/source/generated/examples/aif/images/sphx_glr_plot_dummy_003.png and /dev/null differ diff --git a/docs/source/generated/examples/aif/images/thumb/sphx_glr_plot_aif_parker_thumb.png b/docs/source/generated/examples/aif/images/thumb/sphx_glr_plot_aif_parker_thumb.png index 0062151..5fb3446 100644 Binary files a/docs/source/generated/examples/aif/images/thumb/sphx_glr_plot_aif_parker_thumb.png and b/docs/source/generated/examples/aif/images/thumb/sphx_glr_plot_aif_parker_thumb.png differ diff --git a/docs/source/generated/examples/aif/images/thumb/sphx_glr_plot_dummy_thumb.png b/docs/source/generated/examples/aif/images/thumb/sphx_glr_plot_dummy_thumb.png index 0062151..5fb3446 100644 Binary files a/docs/source/generated/examples/aif/images/thumb/sphx_glr_plot_dummy_thumb.png and b/docs/source/generated/examples/aif/images/thumb/sphx_glr_plot_dummy_thumb.png differ diff --git a/docs/source/generated/examples/aif/plot_aif_parker.ipynb b/docs/source/generated/examples/aif/plot_aif_parker.ipynb index a08fdb1..121a62b 100644 --- a/docs/source/generated/examples/aif/plot_aif_parker.ipynb +++ b/docs/source/generated/examples/aif/plot_aif_parker.ipynb @@ -47,7 +47,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The bolus arrival time (BAT) defaults to 30s. What happens if we change it? Let's try, by changing it in steps of 30s:\n\n" + "The bolus arrival time (BAT) defaults to 0s. What happens if we change it? Let's try, by changing it in steps of 30s:\n\n" ] }, { @@ -58,25 +58,7 @@ }, "outputs": [], "source": [ - "ca = osipi.aif_parker(t, BAT=0)\nplt.plot(t, ca, 'b-', label='BAT = 0s')\nca = osipi.aif_parker(t, BAT=30)\nplt.plot(t, ca, 'r-', label='BAT = 30s')\nca = osipi.aif_parker(t, BAT=60)\nplt.plot(t, ca, 'g-', label='BAT = 60s')\nca = osipi.aif_parker(t, BAT=90)\nplt.plot(t, ca, 'm-', label='BAT = 90s')\nplt.xlabel('Time (sec)')\nplt.ylabel('Plasma concentration (mM)')\nplt.legend()\nplt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "the dose defaults to 0.1- what happens if we change it too?\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "ca = osipi.aif_parker(t, BAT=0, dose=0.05)\nplt.plot(t, ca, 'b-', label='BAT = 0s, dose = 0.05')\nca = osipi.aif_parker(t, BAT=30, dose=0.1)\nplt.plot(t, ca, 'r-', label='BAT = 30s, dose = 0.1')\nca = osipi.aif_parker(t, BAT=60, dose=0.2)\nplt.plot(t, ca, 'g-', label='BAT = 60s, dose = 0.2')\nca = osipi.aif_parker(t, BAT=90, dose=0.3)\nplt.plot(t, ca, 'm-', label='BAT = 90s, dose = 0.3')\nplt.xlabel('Time (sec)')\nplt.ylabel('Plasma concentration (mM)')\nplt.legend()\nplt.show()\n\n# Choose the last image as a thumbnail for the gallery\n# sphinx_gallery_thumbnail_number = -1" + "ca = osipi.aif_parker(t, BAT=0)\nplt.plot(t, ca, 'b-', label='BAT = 0s')\nca = osipi.aif_parker(t, BAT=30)\nplt.plot(t, ca, 'r-', label='BAT = 30s')\nca = osipi.aif_parker(t, BAT=60)\nplt.plot(t, ca, 'g-', label='BAT = 60s')\nca = osipi.aif_parker(t, BAT=90)\nplt.plot(t, ca, 'm-', label='BAT = 90s')\nplt.xlabel('Time (sec)')\nplt.ylabel('Plasma concentration (mM)')\nplt.legend()\nplt.show()\n\n# Choose the last image as a thumbnail for the gallery\n# sphinx_gallery_thumbnail_number = -1" ] } ], @@ -96,7 +78,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.10" + "version": "3.9.16" } }, "nbformat": 4, diff --git a/docs/source/generated/examples/aif/plot_aif_parker.py b/docs/source/generated/examples/aif/plot_aif_parker.py index 9e30887..4f3a82d 100644 --- a/docs/source/generated/examples/aif/plot_aif_parker.py +++ b/docs/source/generated/examples/aif/plot_aif_parker.py @@ -29,7 +29,7 @@ plt.show() # %% -# The bolus arrival time (BAT) defaults to 30s. What happens if we change it? Let's try, by changing it in steps of 30s: +# The bolus arrival time (BAT) defaults to 0s. What happens if we change it? Let's try, by changing it in steps of 30s: ca = osipi.aif_parker(t, BAT=0) plt.plot(t, ca, 'b-', label='BAT = 0s') @@ -44,21 +44,5 @@ plt.legend() plt.show() -# %% -# the dose defaults to 0.1- what happens if we change it too? - -ca = osipi.aif_parker(t, BAT=0, dose=0.05) -plt.plot(t, ca, 'b-', label='BAT = 0s, dose = 0.05') -ca = osipi.aif_parker(t, BAT=30, dose=0.1) -plt.plot(t, ca, 'r-', label='BAT = 30s, dose = 0.1') -ca = osipi.aif_parker(t, BAT=60, dose=0.2) -plt.plot(t, ca, 'g-', label='BAT = 60s, dose = 0.2') -ca = osipi.aif_parker(t, BAT=90, dose=0.3) -plt.plot(t, ca, 'm-', label='BAT = 90s, dose = 0.3') -plt.xlabel('Time (sec)') -plt.ylabel('Plasma concentration (mM)') -plt.legend() -plt.show() - # Choose the last image as a thumbnail for the gallery # sphinx_gallery_thumbnail_number = -1 diff --git a/docs/source/generated/examples/aif/plot_aif_parker.py.md5 b/docs/source/generated/examples/aif/plot_aif_parker.py.md5 index e103f85..756e853 100644 --- a/docs/source/generated/examples/aif/plot_aif_parker.py.md5 +++ b/docs/source/generated/examples/aif/plot_aif_parker.py.md5 @@ -1 +1 @@ -27f8a42f83c3ca01acbf898703895e18 \ No newline at end of file +2ad42a0f8a1b8aa8ec46f9a5f66e86e4 \ No newline at end of file diff --git a/docs/source/generated/examples/aif/plot_aif_parker.rst b/docs/source/generated/examples/aif/plot_aif_parker.rst index c22efca..48dee78 100644 --- a/docs/source/generated/examples/aif/plot_aif_parker.rst +++ b/docs/source/generated/examples/aif/plot_aif_parker.rst @@ -2,7 +2,7 @@ .. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: -.. "generated\examples\aif\plot_aif_parker.py" +.. "generated/examples/aif/plot_aif_parker.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html @@ -79,9 +79,9 @@ Generate synthetic AIF with default settings and plot the result. .. GENERATED FROM PYTHON SOURCE LINES 32-33 -The bolus arrival time (BAT) defaults to 30s. What happens if we change it? Let's try, by changing it in steps of 30s: +The bolus arrival time (BAT) defaults to 0s. What happens if we change it? Let's try, by changing it in steps of 30s: -.. GENERATED FROM PYTHON SOURCE LINES 33-47 +.. GENERATED FROM PYTHON SOURCE LINES 33-49 .. code-block:: default @@ -99,48 +99,14 @@ The bolus arrival time (BAT) defaults to 30s. What happens if we change it? Let' plt.legend() plt.show() - - - -.. image-sg:: /generated/examples/aif/images/sphx_glr_plot_aif_parker_002.png - :alt: plot aif parker - :srcset: /generated/examples/aif/images/sphx_glr_plot_aif_parker_002.png - :class: sphx-glr-single-img - - - - - -.. GENERATED FROM PYTHON SOURCE LINES 48-49 - -the dose defaults to 0.1- what happens if we change it too? - -.. GENERATED FROM PYTHON SOURCE LINES 49-65 - -.. code-block:: default - - - ca = osipi.aif_parker(t, BAT=0, dose=0.05) - plt.plot(t, ca, 'b-', label='BAT = 0s, dose = 0.05') - ca = osipi.aif_parker(t, BAT=30, dose=0.1) - plt.plot(t, ca, 'r-', label='BAT = 30s, dose = 0.1') - ca = osipi.aif_parker(t, BAT=60, dose=0.2) - plt.plot(t, ca, 'g-', label='BAT = 60s, dose = 0.2') - ca = osipi.aif_parker(t, BAT=90, dose=0.3) - plt.plot(t, ca, 'm-', label='BAT = 90s, dose = 0.3') - plt.xlabel('Time (sec)') - plt.ylabel('Plasma concentration (mM)') - plt.legend() - plt.show() - # Choose the last image as a thumbnail for the gallery # sphinx_gallery_thumbnail_number = -1 -.. image-sg:: /generated/examples/aif/images/sphx_glr_plot_aif_parker_003.png +.. image-sg:: /generated/examples/aif/images/sphx_glr_plot_aif_parker_002.png :alt: plot aif parker - :srcset: /generated/examples/aif/images/sphx_glr_plot_aif_parker_003.png + :srcset: /generated/examples/aif/images/sphx_glr_plot_aif_parker_002.png :class: sphx-glr-single-img @@ -150,7 +116,7 @@ the dose defaults to 0.1- what happens if we change it too? .. rst-class:: sphx-glr-timing - **Total running time of the script:** ( 0 minutes 0.648 seconds) + **Total running time of the script:** (0 minutes 0.110 seconds) .. _sphx_glr_download_generated_examples_aif_plot_aif_parker.py: diff --git a/docs/source/generated/examples/aif/plot_aif_parker_codeobj.pickle b/docs/source/generated/examples/aif/plot_aif_parker_codeobj.pickle index 97b4bf9..7f1df19 100644 Binary files a/docs/source/generated/examples/aif/plot_aif_parker_codeobj.pickle and b/docs/source/generated/examples/aif/plot_aif_parker_codeobj.pickle differ diff --git a/docs/source/generated/examples/aif/plot_dummy.ipynb b/docs/source/generated/examples/aif/plot_dummy.ipynb index 3d11365..904ad56 100644 --- a/docs/source/generated/examples/aif/plot_dummy.ipynb +++ b/docs/source/generated/examples/aif/plot_dummy.ipynb @@ -58,25 +58,7 @@ }, "outputs": [], "source": [ - "ca = osipi.aif_parker(t, BAT=0)\nplt.plot(t, ca, 'b-', label='BAT = 0s')\nca = osipi.aif_parker(t, BAT=30)\nplt.plot(t, ca, 'r-', label='BAT = 30s')\nca = osipi.aif_parker(t, BAT=60)\nplt.plot(t, ca, 'g-', label='BAT = 60s')\nca = osipi.aif_parker(t, BAT=90)\nplt.plot(t, ca, 'm-', label='BAT = 90s')\nplt.xlabel('Time (sec)')\nplt.ylabel('Plasma concentration (mM)')\nplt.legend()\nplt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "the dose defaults to 0.1- what happens if we change it too?\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "ca = osipi.aif_parker(t, BAT=0, dose=0.05)\nplt.plot(t, ca, 'b-', label='BAT = 0s, dose = 0.05')\nca = osipi.aif_parker(t, BAT=30, dose=0.1)\nplt.plot(t, ca, 'r-', label='BAT = 30s, dose = 0.1')\nca = osipi.aif_parker(t, BAT=60, dose=0.2)\nplt.plot(t, ca, 'g-', label='BAT = 60s, dose = 0.2')\nca = osipi.aif_parker(t, BAT=90, dose=0.3)\nplt.plot(t, ca, 'm-', label='BAT = 90s, dose = 0.3')\nplt.xlabel('Time (sec)')\nplt.ylabel('Plasma concentration (mM)')\nplt.legend()\nplt.show()\n\n# Choose the last image as a thumbnail for the gallery\n# sphinx_gallery_thumbnail_number = -1" + "ca = osipi.aif_parker(t, BAT=0)\nplt.plot(t, ca, 'b-', label='BAT = 0s')\nca = osipi.aif_parker(t, BAT=30)\nplt.plot(t, ca, 'r-', label='BAT = 30s')\nca = osipi.aif_parker(t, BAT=60)\nplt.plot(t, ca, 'g-', label='BAT = 60s')\nca = osipi.aif_parker(t, BAT=90)\nplt.plot(t, ca, 'm-', label='BAT = 90s')\nplt.xlabel('Time (sec)')\nplt.ylabel('Plasma concentration (mM)')\nplt.legend()\nplt.show()\n\n# Choose the last image as a thumbnail for the gallery\n# sphinx_gallery_thumbnail_number = -1" ] } ], @@ -96,7 +78,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.10" + "version": "3.9.16" } }, "nbformat": 4, diff --git a/docs/source/generated/examples/aif/plot_dummy.py b/docs/source/generated/examples/aif/plot_dummy.py index 9069e41..1f4d326 100644 --- a/docs/source/generated/examples/aif/plot_dummy.py +++ b/docs/source/generated/examples/aif/plot_dummy.py @@ -44,21 +44,5 @@ plt.legend() plt.show() -# %% -# the dose defaults to 0.1- what happens if we change it too? - -ca = osipi.aif_parker(t, BAT=0, dose=0.05) -plt.plot(t, ca, 'b-', label='BAT = 0s, dose = 0.05') -ca = osipi.aif_parker(t, BAT=30, dose=0.1) -plt.plot(t, ca, 'r-', label='BAT = 30s, dose = 0.1') -ca = osipi.aif_parker(t, BAT=60, dose=0.2) -plt.plot(t, ca, 'g-', label='BAT = 60s, dose = 0.2') -ca = osipi.aif_parker(t, BAT=90, dose=0.3) -plt.plot(t, ca, 'm-', label='BAT = 90s, dose = 0.3') -plt.xlabel('Time (sec)') -plt.ylabel('Plasma concentration (mM)') -plt.legend() -plt.show() - # Choose the last image as a thumbnail for the gallery # sphinx_gallery_thumbnail_number = -1 diff --git a/docs/source/generated/examples/aif/plot_dummy.py.md5 b/docs/source/generated/examples/aif/plot_dummy.py.md5 index 1652873..39417a6 100644 --- a/docs/source/generated/examples/aif/plot_dummy.py.md5 +++ b/docs/source/generated/examples/aif/plot_dummy.py.md5 @@ -1 +1 @@ -7db1c2294438a1339c2cc44c61da25c5 \ No newline at end of file +3393738ee0ad511916ee690ca9a231be \ No newline at end of file diff --git a/docs/source/generated/examples/aif/plot_dummy.rst b/docs/source/generated/examples/aif/plot_dummy.rst index a547a91..de75cd2 100644 --- a/docs/source/generated/examples/aif/plot_dummy.rst +++ b/docs/source/generated/examples/aif/plot_dummy.rst @@ -2,7 +2,7 @@ .. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: -.. "generated\examples\aif\plot_dummy.py" +.. "generated/examples/aif/plot_dummy.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html @@ -81,7 +81,7 @@ Generate synthetic AIF with default settings and plot the result. The bolus arrival time (BAT) defaults to 30s. What happens if we change it? Let's try, by changing it in steps of 30s: -.. GENERATED FROM PYTHON SOURCE LINES 33-47 +.. GENERATED FROM PYTHON SOURCE LINES 33-49 .. code-block:: default @@ -99,48 +99,14 @@ The bolus arrival time (BAT) defaults to 30s. What happens if we change it? Let' plt.legend() plt.show() - - - -.. image-sg:: /generated/examples/aif/images/sphx_glr_plot_dummy_002.png - :alt: plot dummy - :srcset: /generated/examples/aif/images/sphx_glr_plot_dummy_002.png - :class: sphx-glr-single-img - - - - - -.. GENERATED FROM PYTHON SOURCE LINES 48-49 - -the dose defaults to 0.1- what happens if we change it too? - -.. GENERATED FROM PYTHON SOURCE LINES 49-65 - -.. code-block:: default - - - ca = osipi.aif_parker(t, BAT=0, dose=0.05) - plt.plot(t, ca, 'b-', label='BAT = 0s, dose = 0.05') - ca = osipi.aif_parker(t, BAT=30, dose=0.1) - plt.plot(t, ca, 'r-', label='BAT = 30s, dose = 0.1') - ca = osipi.aif_parker(t, BAT=60, dose=0.2) - plt.plot(t, ca, 'g-', label='BAT = 60s, dose = 0.2') - ca = osipi.aif_parker(t, BAT=90, dose=0.3) - plt.plot(t, ca, 'm-', label='BAT = 90s, dose = 0.3') - plt.xlabel('Time (sec)') - plt.ylabel('Plasma concentration (mM)') - plt.legend() - plt.show() - # Choose the last image as a thumbnail for the gallery # sphinx_gallery_thumbnail_number = -1 -.. image-sg:: /generated/examples/aif/images/sphx_glr_plot_dummy_003.png +.. image-sg:: /generated/examples/aif/images/sphx_glr_plot_dummy_002.png :alt: plot dummy - :srcset: /generated/examples/aif/images/sphx_glr_plot_dummy_003.png + :srcset: /generated/examples/aif/images/sphx_glr_plot_dummy_002.png :class: sphx-glr-single-img @@ -150,7 +116,7 @@ the dose defaults to 0.1- what happens if we change it too? .. rst-class:: sphx-glr-timing - **Total running time of the script:** ( 0 minutes 0.638 seconds) + **Total running time of the script:** (0 minutes 0.078 seconds) .. _sphx_glr_download_generated_examples_aif_plot_dummy.py: diff --git a/docs/source/generated/examples/aif/plot_dummy_codeobj.pickle b/docs/source/generated/examples/aif/plot_dummy_codeobj.pickle index 97b4bf9..7f1df19 100644 Binary files a/docs/source/generated/examples/aif/plot_dummy_codeobj.pickle and b/docs/source/generated/examples/aif/plot_dummy_codeobj.pickle differ diff --git a/docs/source/generated/examples/aif/sg_execution_times.rst b/docs/source/generated/examples/aif/sg_execution_times.rst index 322eafc..de64540 100644 --- a/docs/source/generated/examples/aif/sg_execution_times.rst +++ b/docs/source/generated/examples/aif/sg_execution_times.rst @@ -6,10 +6,10 @@ Computation times ================= -**00:01.285** total execution time for **generated_examples_aif** files: +**00:00.188** total execution time for **generated_examples_aif** files: +------------------------------------------------------------------------------------+-----------+--------+ -| :ref:`sphx_glr_generated_examples_aif_plot_aif_parker.py` (``plot_aif_parker.py``) | 00:00.648 | 0.0 MB | +| :ref:`sphx_glr_generated_examples_aif_plot_aif_parker.py` (``plot_aif_parker.py``) | 00:00.110 | 0.0 MB | +------------------------------------------------------------------------------------+-----------+--------+ -| :ref:`sphx_glr_generated_examples_aif_plot_dummy.py` (``plot_dummy.py``) | 00:00.638 | 0.0 MB | +| :ref:`sphx_glr_generated_examples_aif_plot_dummy.py` (``plot_dummy.py``) | 00:00.078 | 0.0 MB | +------------------------------------------------------------------------------------+-----------+--------+ diff --git a/docs/source/generated/examples/index.rst b/docs/source/generated/examples/index.rst index 466f40a..e805eb2 100644 --- a/docs/source/generated/examples/index.rst +++ b/docs/source/generated/examples/index.rst @@ -85,18 +85,18 @@ Tissue concentrations .. raw:: html -
+
.. only:: html - .. image:: /generated/examples/tissue/images/thumb/sphx_glr_plot_dummy_thumb.png + .. image:: /generated/examples/tissue/images/thumb/sphx_glr_plot_tofts_thumb.png :alt: - :ref:`sphx_glr_generated_examples_tissue_plot_dummy.py` + :ref:`sphx_glr_generated_examples_tissue_plot_tofts.py` .. raw:: html -
Dummy script as demo
+
The Tofts model
diff --git a/docs/source/generated/examples/tissue/images/sphx_glr_plot_dummy_001.png b/docs/source/generated/examples/tissue/images/sphx_glr_plot_dummy_001.png deleted file mode 100644 index 75c96d4..0000000 Binary files a/docs/source/generated/examples/tissue/images/sphx_glr_plot_dummy_001.png and /dev/null differ diff --git a/docs/source/generated/examples/tissue/images/sphx_glr_plot_tofts_001.png b/docs/source/generated/examples/tissue/images/sphx_glr_plot_tofts_001.png new file mode 100644 index 0000000..ba5f383 Binary files /dev/null and b/docs/source/generated/examples/tissue/images/sphx_glr_plot_tofts_001.png differ diff --git a/docs/source/generated/examples/tissue/images/sphx_glr_plot_tofts_002.png b/docs/source/generated/examples/tissue/images/sphx_glr_plot_tofts_002.png new file mode 100644 index 0000000..49b1ccd Binary files /dev/null and b/docs/source/generated/examples/tissue/images/sphx_glr_plot_tofts_002.png differ diff --git a/docs/source/generated/examples/tissue/images/thumb/sphx_glr_plot_dummy_thumb.png b/docs/source/generated/examples/tissue/images/thumb/sphx_glr_plot_dummy_thumb.png deleted file mode 100644 index 02ee06a..0000000 Binary files a/docs/source/generated/examples/tissue/images/thumb/sphx_glr_plot_dummy_thumb.png and /dev/null differ diff --git a/docs/source/generated/examples/tissue/images/thumb/sphx_glr_plot_tofts_thumb.png b/docs/source/generated/examples/tissue/images/thumb/sphx_glr_plot_tofts_thumb.png new file mode 100644 index 0000000..6aaabaa Binary files /dev/null and b/docs/source/generated/examples/tissue/images/thumb/sphx_glr_plot_tofts_thumb.png differ diff --git a/docs/source/generated/examples/tissue/index.rst b/docs/source/generated/examples/tissue/index.rst index ae5714c..12f0321 100644 --- a/docs/source/generated/examples/tissue/index.rst +++ b/docs/source/generated/examples/tissue/index.rst @@ -17,18 +17,18 @@ Tissue concentrations .. raw:: html -
+
.. only:: html - .. image:: /generated/examples/tissue/images/thumb/sphx_glr_plot_dummy_thumb.png + .. image:: /generated/examples/tissue/images/thumb/sphx_glr_plot_tofts_thumb.png :alt: - :ref:`sphx_glr_generated_examples_tissue_plot_dummy.py` + :ref:`sphx_glr_generated_examples_tissue_plot_tofts.py` .. raw:: html -
Dummy script as demo
+
The Tofts model
@@ -40,5 +40,5 @@ Tissue concentrations .. toctree:: :hidden: - /generated/examples/tissue/plot_dummy + /generated/examples/tissue/plot_tofts diff --git a/docs/source/generated/examples/tissue/plot_dummy.ipynb b/docs/source/generated/examples/tissue/plot_dummy.ipynb deleted file mode 100644 index fc46d6e..0000000 --- a/docs/source/generated/examples/tissue/plot_dummy.ipynb +++ /dev/null @@ -1,61 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n# Dummy script as demo\n\nDummy script to show the examples structure. \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import numpy as np\nimport matplotlib.pyplot as plt\nimport osipi" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Generate synthetic AIF with default settings and plot the result.\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# Define time points in units of seconds - in this case we use a time resolution of 0.5 sec and a total duration of 6 minutes.\nt = np.arange(0, 6*60, 0.5)\n\n# Create an AIF with default settings\nca = osipi.aif_parker(t)\n\n# Plot the AIF over the full range\nplt.plot(t, ca)\nplt.show()\n\n\n# Choose the last image as a thumbnail for the gallery\n# sphinx_gallery_thumbnail_number = -1" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.10" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/docs/source/generated/examples/tissue/plot_dummy.py b/docs/source/generated/examples/tissue/plot_dummy.py deleted file mode 100644 index 6cde113..0000000 --- a/docs/source/generated/examples/tissue/plot_dummy.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -==================== -Dummy script as demo -==================== - -Dummy script to show the examples structure. -""" - -import numpy as np -import matplotlib.pyplot as plt -import osipi - -# %% -# Generate synthetic AIF with default settings and plot the result. - -# Define time points in units of seconds - in this case we use a time resolution of 0.5 sec and a total duration of 6 minutes. -t = np.arange(0, 6*60, 0.5) - -# Create an AIF with default settings -ca = osipi.aif_parker(t) - -# Plot the AIF over the full range -plt.plot(t, ca) -plt.show() - - -# Choose the last image as a thumbnail for the gallery -# sphinx_gallery_thumbnail_number = -1 diff --git a/docs/source/generated/examples/tissue/plot_dummy.py.md5 b/docs/source/generated/examples/tissue/plot_dummy.py.md5 deleted file mode 100644 index 7746462..0000000 --- a/docs/source/generated/examples/tissue/plot_dummy.py.md5 +++ /dev/null @@ -1 +0,0 @@ -345e334f0a9f6b049cf95fbfb3c54d56 \ No newline at end of file diff --git a/docs/source/generated/examples/tissue/plot_dummy.rst b/docs/source/generated/examples/tissue/plot_dummy.rst deleted file mode 100644 index a436fac..0000000 --- a/docs/source/generated/examples/tissue/plot_dummy.rst +++ /dev/null @@ -1,105 +0,0 @@ - -.. DO NOT EDIT. -.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. -.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: -.. "generated\examples\tissue\plot_dummy.py" -.. LINE NUMBERS ARE GIVEN BELOW. - -.. only:: html - - .. note:: - :class: sphx-glr-download-link-note - - :ref:`Go to the end ` - to download the full example code - -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_generated_examples_tissue_plot_dummy.py: - - -==================== -Dummy script as demo -==================== - -Dummy script to show the examples structure. - -.. GENERATED FROM PYTHON SOURCE LINES 8-13 - -.. code-block:: default - - - import numpy as np - import matplotlib.pyplot as plt - import osipi - - - - - - - - -.. GENERATED FROM PYTHON SOURCE LINES 14-15 - -Generate synthetic AIF with default settings and plot the result. - -.. GENERATED FROM PYTHON SOURCE LINES 15-29 - -.. code-block:: default - - - # Define time points in units of seconds - in this case we use a time resolution of 0.5 sec and a total duration of 6 minutes. - t = np.arange(0, 6*60, 0.5) - - # Create an AIF with default settings - ca = osipi.aif_parker(t) - - # Plot the AIF over the full range - plt.plot(t, ca) - plt.show() - - - # Choose the last image as a thumbnail for the gallery - # sphinx_gallery_thumbnail_number = -1 - - - -.. image-sg:: /generated/examples/tissue/images/sphx_glr_plot_dummy_001.png - :alt: plot dummy - :srcset: /generated/examples/tissue/images/sphx_glr_plot_dummy_001.png - :class: sphx-glr-single-img - - - - - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.134 seconds) - - -.. _sphx_glr_download_generated_examples_tissue_plot_dummy.py: - -.. only:: html - - .. container:: sphx-glr-footer sphx-glr-footer-example - - - - - .. container:: sphx-glr-download sphx-glr-download-python - - :download:`Download Python source code: plot_dummy.py ` - - .. container:: sphx-glr-download sphx-glr-download-jupyter - - :download:`Download Jupyter notebook: plot_dummy.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/docs/source/generated/examples/tissue/plot_dummy_codeobj.pickle b/docs/source/generated/examples/tissue/plot_dummy_codeobj.pickle deleted file mode 100644 index ae2b437..0000000 Binary files a/docs/source/generated/examples/tissue/plot_dummy_codeobj.pickle and /dev/null differ diff --git a/docs/source/generated/examples/tissue/plot_tofts.ipynb b/docs/source/generated/examples/tissue/plot_tofts.ipynb new file mode 100644 index 0000000..e937d8c --- /dev/null +++ b/docs/source/generated/examples/tissue/plot_tofts.ipynb @@ -0,0 +1,104 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# The Tofts model\n\nSimulating tissue concentrations from Tofts model with different settings.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Import necessary packages\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import numpy as np\nimport matplotlib.pyplot as plt\nimport osipi" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Generate Parker AIF with default settings.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Define time points in units of seconds - in this case we use a time resolution of 1 sec and a total duration of 6 minutes.\nt = np.arange(0, 6*60, 1)\n\n# Create an AIF with default settings\nca = osipi.aif_parker(t)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot the tissue concentrations for an extracellular volume fraction of 0.2 and 3 different transfer rate constants of 0.05, 0.2 and 0.6 /min\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "Ktrans = [0.05, 0.2, 0.6] # in units of 1/min\nve = 0.2 # volume fraction between 0 and 1\nct = osipi.tofts(t, ca, Ktrans=Ktrans[0], ve=ve)\nplt.plot(t, ct, 'b-', label=f'Ktrans = {Ktrans[0]} /min')\nct = osipi.tofts(t, ca, Ktrans[1], ve)\nplt.plot(t, ct, 'g-', label=f'Ktrans = {Ktrans[1]} /min')\nct = osipi.tofts(t, ca, Ktrans[2], ve)\nplt.plot(t, ct, 'm-', label=f'Ktrans = {Ktrans[2]} /min')\nplt.xlabel('Time (sec)')\nplt.ylabel('Tissue concentration (mM)')\nplt.legend()\nplt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Comparing different discretization methods for an extracellular volume fraction of 0.2 and Ktrans of 0.2 /min\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "ct = osipi.tofts(t, ca, Ktrans=Ktrans[1], ve=ve) # Defaults to Convolution\nplt.plot(t, ct, 'b-', label='Convolution')\nct = osipi.tofts(t, ca, Ktrans=Ktrans[1], ve=ve, discretization_method='exp')\nplt.plot(t, ct, 'g-', label='Exponential Convolution')\nplt.title(f'Ktrans = {Ktrans[1]} /min')\nplt.xlabel('Time (sec)')\nplt.ylabel('Tissue concentration (mM)')\nplt.legend()\nplt.show()\n\n# Choose the last image as a thumbnail for the gallery\n# sphinx_gallery_thumbnail_number = -1" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.16" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/docs/source/generated/examples/tissue/plot_tofts.py b/docs/source/generated/examples/tissue/plot_tofts.py new file mode 100644 index 0000000..dc22974 --- /dev/null +++ b/docs/source/generated/examples/tissue/plot_tofts.py @@ -0,0 +1,52 @@ +""" +==================== +The Tofts model +==================== + +Simulating tissue concentrations from Tofts model with different settings. +""" + +# %% +# Import necessary packages +import numpy as np +import matplotlib.pyplot as plt +import osipi + +# %% +# Generate Parker AIF with default settings. + +# Define time points in units of seconds - in this case we use a time resolution of 1 sec and a total duration of 6 minutes. +t = np.arange(0, 6*60, 1) + +# Create an AIF with default settings +ca = osipi.aif_parker(t) + +# %% +# Plot the tissue concentrations for an extracellular volume fraction of 0.2 and 3 different transfer rate constants of 0.05, 0.2 and 0.6 /min +Ktrans = [0.05, 0.2, 0.6] # in units of 1/min +ve = 0.2 # volume fraction between 0 and 1 +ct = osipi.tofts(t, ca, Ktrans=Ktrans[0], ve=ve) +plt.plot(t, ct, 'b-', label=f'Ktrans = {Ktrans[0]} /min') +ct = osipi.tofts(t, ca, Ktrans[1], ve) +plt.plot(t, ct, 'g-', label=f'Ktrans = {Ktrans[1]} /min') +ct = osipi.tofts(t, ca, Ktrans[2], ve) +plt.plot(t, ct, 'm-', label=f'Ktrans = {Ktrans[2]} /min') +plt.xlabel('Time (sec)') +plt.ylabel('Tissue concentration (mM)') +plt.legend() +plt.show() + +# %% +# Comparing different discretization methods for an extracellular volume fraction of 0.2 and Ktrans of 0.2 /min +ct = osipi.tofts(t, ca, Ktrans=Ktrans[1], ve=ve) # Defaults to Convolution +plt.plot(t, ct, 'b-', label='Convolution') +ct = osipi.tofts(t, ca, Ktrans=Ktrans[1], ve=ve, discretization_method='exp') +plt.plot(t, ct, 'g-', label='Exponential Convolution') +plt.title(f'Ktrans = {Ktrans[1]} /min') +plt.xlabel('Time (sec)') +plt.ylabel('Tissue concentration (mM)') +plt.legend() +plt.show() + +# Choose the last image as a thumbnail for the gallery +# sphinx_gallery_thumbnail_number = -1 diff --git a/docs/source/generated/examples/tissue/plot_tofts.py.md5 b/docs/source/generated/examples/tissue/plot_tofts.py.md5 new file mode 100644 index 0000000..3a415ba --- /dev/null +++ b/docs/source/generated/examples/tissue/plot_tofts.py.md5 @@ -0,0 +1 @@ +562f87655efc9f9b885f617d06bc5dba \ No newline at end of file diff --git a/docs/source/generated/examples/tissue/plot_tofts.rst b/docs/source/generated/examples/tissue/plot_tofts.rst new file mode 100644 index 0000000..bd7378d --- /dev/null +++ b/docs/source/generated/examples/tissue/plot_tofts.rst @@ -0,0 +1,161 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "generated/examples/tissue/plot_tofts.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_generated_examples_tissue_plot_tofts.py: + + +==================== +The Tofts model +==================== + +Simulating tissue concentrations from Tofts model with different settings. + +.. GENERATED FROM PYTHON SOURCE LINES 10-11 + +Import necessary packages + +.. GENERATED FROM PYTHON SOURCE LINES 11-15 + +.. code-block:: default + + import numpy as np + import matplotlib.pyplot as plt + import osipi + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 16-17 + +Generate Parker AIF with default settings. + +.. GENERATED FROM PYTHON SOURCE LINES 17-24 + +.. code-block:: default + + + # Define time points in units of seconds - in this case we use a time resolution of 1 sec and a total duration of 6 minutes. + t = np.arange(0, 6*60, 1) + + # Create an AIF with default settings + ca = osipi.aif_parker(t) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 25-26 + +Plot the tissue concentrations for an extracellular volume fraction of 0.2 and 3 different transfer rate constants of 0.05, 0.2 and 0.6 /min + +.. GENERATED FROM PYTHON SOURCE LINES 26-39 + +.. code-block:: default + + Ktrans = [0.05, 0.2, 0.6] # in units of 1/min + ve = 0.2 # volume fraction between 0 and 1 + ct = osipi.tofts(t, ca, Ktrans=Ktrans[0], ve=ve) + plt.plot(t, ct, 'b-', label=f'Ktrans = {Ktrans[0]} /min') + ct = osipi.tofts(t, ca, Ktrans[1], ve) + plt.plot(t, ct, 'g-', label=f'Ktrans = {Ktrans[1]} /min') + ct = osipi.tofts(t, ca, Ktrans[2], ve) + plt.plot(t, ct, 'm-', label=f'Ktrans = {Ktrans[2]} /min') + plt.xlabel('Time (sec)') + plt.ylabel('Tissue concentration (mM)') + plt.legend() + plt.show() + + + + +.. image-sg:: /generated/examples/tissue/images/sphx_glr_plot_tofts_001.png + :alt: plot tofts + :srcset: /generated/examples/tissue/images/sphx_glr_plot_tofts_001.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 40-41 + +Comparing different discretization methods for an extracellular volume fraction of 0.2 and Ktrans of 0.2 /min + +.. GENERATED FROM PYTHON SOURCE LINES 41-53 + +.. code-block:: default + + ct = osipi.tofts(t, ca, Ktrans=Ktrans[1], ve=ve) # Defaults to Convolution + plt.plot(t, ct, 'b-', label='Convolution') + ct = osipi.tofts(t, ca, Ktrans=Ktrans[1], ve=ve, discretization_method='exp') + plt.plot(t, ct, 'g-', label='Exponential Convolution') + plt.title(f'Ktrans = {Ktrans[1]} /min') + plt.xlabel('Time (sec)') + plt.ylabel('Tissue concentration (mM)') + plt.legend() + plt.show() + + # Choose the last image as a thumbnail for the gallery + # sphinx_gallery_thumbnail_number = -1 + + + +.. image-sg:: /generated/examples/tissue/images/sphx_glr_plot_tofts_002.png + :alt: Ktrans = 0.2 /min + :srcset: /generated/examples/tissue/images/sphx_glr_plot_tofts_002.png + :class: sphx-glr-single-img + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 0.093 seconds) + + +.. _sphx_glr_download_generated_examples_tissue_plot_tofts.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: plot_tofts.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: plot_tofts.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/docs/source/generated/examples/tissue/plot_tofts_codeobj.pickle b/docs/source/generated/examples/tissue/plot_tofts_codeobj.pickle new file mode 100644 index 0000000..dfbf690 Binary files /dev/null and b/docs/source/generated/examples/tissue/plot_tofts_codeobj.pickle differ diff --git a/docs/source/generated/examples/tissue/sg_execution_times.rst b/docs/source/generated/examples/tissue/sg_execution_times.rst index 0665758..54b7203 100644 --- a/docs/source/generated/examples/tissue/sg_execution_times.rst +++ b/docs/source/generated/examples/tissue/sg_execution_times.rst @@ -6,8 +6,8 @@ Computation times ================= -**00:00.134** total execution time for **generated_examples_tissue** files: +**00:00.093** total execution time for **generated_examples_tissue** files: +-----------------------------------------------------------------------------+-----------+--------+ -| :ref:`sphx_glr_generated_examples_tissue_plot_dummy.py` (``plot_dummy.py``) | 00:00.134 | 0.0 MB | +| :ref:`sphx_glr_generated_examples_tissue_plot_tofts.py` (``plot_tofts.py``) | 00:00.093 | 0.0 MB | +-----------------------------------------------------------------------------+-----------+--------+ diff --git a/docs/source/reference/alphabetical.rst b/docs/source/reference/alphabetical.rst index 8160ac2..c902bea 100644 --- a/docs/source/reference/alphabetical.rst +++ b/docs/source/reference/alphabetical.rst @@ -13,6 +13,5 @@ Alphabetic list of all currently available `osipi` code snippets. aif_parker aif_georgiou aif_weinmann - - - + tofts + extended_tofts diff --git a/docs/source/reference/general.convolution.rst b/docs/source/reference/general.convolution.rst new file mode 100644 index 0000000..0c9d9b0 --- /dev/null +++ b/docs/source/reference/general.convolution.rst @@ -0,0 +1,14 @@ +Convolution +^^^^^^^^^^^ + +.. currentmodule:: osipi + +.. note:: + + This is a placeholder page. There is no relevant functionality in ``osipi`` yet. + + Please check back later - or better yet, write up what you were looking for here and contribute it! This way the next person looking for this feature won't have to.. + + See :ref:`developer-guide` for guidance on how to contribute. + + diff --git a/docs/source/reference/general.descriptive.rst b/docs/source/reference/general.descriptive.rst index 4f5ddc4..813ab6c 100644 --- a/docs/source/reference/general.descriptive.rst +++ b/docs/source/reference/general.descriptive.rst @@ -5,11 +5,11 @@ Descriptive .. note:: - This is a placeholder page. There is no relevant functionality in ``osipi`` yet. - + This is a placeholder page. There is no relevant functionality in ``osipi`` yet. + Please check back later - or better yet, write up what you were looking for here and contribute it! This way the next person looking for this feature won't have to.. See :ref:`developer-guide` for guidance on how to contribute. - + diff --git a/docs/source/reference/general.rst b/docs/source/reference/general.rst index 563c680..df0ee2f 100644 --- a/docs/source/reference/general.rst +++ b/docs/source/reference/general.rst @@ -6,6 +6,7 @@ General :maxdepth: 2 general.averaging + general.convolution general.deconvolution general.descriptive general.forward diff --git a/docs/source/reference/models.concentration.tissue.rst b/docs/source/reference/models.concentration.tissue.rst index f654012..6e20d6d 100644 --- a/docs/source/reference/models.concentration.tissue.rst +++ b/docs/source/reference/models.concentration.tissue.rst @@ -3,14 +3,13 @@ Tissue models .. currentmodule:: osipi -.. note:: - - This is a placeholder page. There is no relevant functionality in ``osipi`` yet. - - Please check back later - or better yet, write up what you were looking for here and contribute it! This way the next person looking for this feature won't have to.. - - See :ref:`developer-guide` for guidance on how to contribute. - +Tofts model +----------- +.. autosummary:: + :toctree: ../generated/api/ + :template: autosummary.rst + tofts + extended_tofts diff --git a/docs/source/user_guide/forward/forward.rst b/docs/source/user_guide/forward/forward.rst index 1911d19..894964e 100644 --- a/docs/source/user_guide/forward/forward.rst +++ b/docs/source/user_guide/forward/forward.rst @@ -19,7 +19,21 @@ Generate an AIF and plot it: Generating a tissue concentration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Coming soon.. +Generate tissue concentration and plot it: + +.. code-block:: python + + import numpy as np + import matplotlib.pyplot as plt + import osipi + + t = np.arange(0, 6*60, 1) + ca = osipi.aif_parker(t) + Ktrans = 0.6 + ve = 0.2 + ct = osipi.tofts(t, ca, Ktrans=Ktrans/60, ve=ve) + plt.plot(t, ct) + plt.show() Generating an MRI signal diff --git a/requirements.txt b/requirements.txt index e69de29..4a2a08b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -0,0 +1,3 @@ +numpy +scipy +matplotlib \ No newline at end of file diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000..d09ec79 Binary files /dev/null and b/src/.DS_Store differ diff --git a/src/osipi/__init__.py b/src/osipi/__init__.py index 90370f1..ff2f20b 100644 --- a/src/osipi/__init__.py +++ b/src/osipi/__init__.py @@ -4,3 +4,8 @@ aif_georgiou, aif_weinmann, ) + +from ._tissue import ( + tofts, + extended_tofts +) \ No newline at end of file diff --git a/src/osipi/_aif.py b/src/osipi/_aif.py index 52933d0..ec98f8c 100644 --- a/src/osipi/_aif.py +++ b/src/osipi/_aif.py @@ -1,14 +1,13 @@ import numpy as np -def aif_parker(t:np.ndarray, BAT:float=30.0, Hct:float=0.0, dose:float=0.1)->np.ndarray: +def aif_parker(t:np.ndarray, BAT:float=0.0, Hct:float=0.0)->np.ndarray: """AIF model as defined by Parker et al (2005) Args: t (np.ndarray): array of time points in units of sec. [OSIPI code Q.GE1.004] - BAT (float, optional): Time in seconds before the bolus arrives. Defaults to 30sec. [OSIPI code Q.BA1.001] + BAT (float, optional): Time in seconds before the bolus arrives. Defaults to 0. [OSIPI code Q.BA1.001] Hct (float, optional): Hematocrit. Defaults to 0.0. [OSIPI code Q.PH1.012] - dose (float, optional): Injected contrast agent dose in units of [???]. Defaults to 0.1. [OSIPI code Q.IC1.999] Returns: np.ndarray: Concentrations in mM for each time point in t. @@ -25,7 +24,7 @@ def aif_parker(t:np.ndarray, BAT:float=30.0, Hct:float=0.0, dose:float=0.1)->np. Example: - Create an array of time points covering 6min in steps of 1sec, calculate the Parker AIF at these time points and plot the results. + Create an array of time points covering 6 min in steps of 1 sec, calculate the Parker AIF at these time points and plot the results. Import packages: @@ -34,12 +33,11 @@ def aif_parker(t:np.ndarray, BAT:float=30.0, Hct:float=0.0, dose:float=0.1)->np. Calculate AIF and plot - >>> t = np.arange(0, 6*60, 0.1) + >>> t = np.arange(0, 6*60, 1) >>> ca = osipi.aif_parker(t) >>> plt.plot(t,ca) >>> plt.show() """ - # Convert from OSIPI units (sec) to units used internally (mins) t_min = t/60 bat_min = BAT/60 @@ -63,13 +61,13 @@ def aif_parker(t:np.ndarray, BAT:float=30.0, Hct:float=0.0, dose:float=0.1)->np. # alpha = 1.064, beta = 0.166, s = 37.772, tau = 0.482 sigmoid = 1.050 * np.exp(-0.1685 * t_offset) / (1.0 + np.exp(-38.078 * (t_offset - 0.483))) - pop_aif = ((dose / 0.1) * (gaussian1 + gaussian2 + sigmoid)) / \ + pop_aif = ((gaussian1 + gaussian2 + sigmoid)) / \ (1.0 - Hct) return pop_aif -def aif_georgiou(t:np.ndarray, BAT:float=30.0)->np.ndarray: +def aif_georgiou(t:np.ndarray, BAT:float=0.0)->np.ndarray: """AIF model as defined by Georgiou et al. Note: @@ -77,7 +75,7 @@ def aif_georgiou(t:np.ndarray, BAT:float=30.0)->np.ndarray: Args: t (np.ndarray): array of time points in units of sec. [OSIPI code Q.GE1.004] - BAT (float, optional): Time in seconds before the bolus arrives. Defaults to 30sec. [OSIPI code Q.BA1.001] + BAT (float, optional): Time in seconds before the bolus arrives. Defaults to 0sec. [OSIPI code Q.BA1.001] Returns: np.ndarray: Concentrations in mM for each time point in t. @@ -115,7 +113,7 @@ def aif_georgiou(t:np.ndarray, BAT:float=30.0)->np.ndarray: raise NotImplementedError(msg) -def aif_weinmann(t:np.ndarray, BAT:float=30.0)->np.ndarray: +def aif_weinmann(t:np.ndarray, BAT:float=0.0)->np.ndarray: """AIF model as defined by Weinmann et al. Note: @@ -123,7 +121,7 @@ def aif_weinmann(t:np.ndarray, BAT:float=30.0)->np.ndarray: Args: t (np.ndarray): array of time points in units of sec. [OSIPI code Q.GE1.004] - BAT (float, optional): Time in seconds before the bolus arrives. Defaults to 30sec. [OSIPI code Q.BA1.001] + BAT (float, optional): Time in seconds before the bolus arrives. Defaults to 0sec. [OSIPI code Q.BA1.001] Returns: np.ndarray: Concentrations in mM for each time point in t. diff --git a/src/osipi/_convolution.py b/src/osipi/_convolution.py new file mode 100644 index 0000000..c6d925e --- /dev/null +++ b/src/osipi/_convolution.py @@ -0,0 +1,35 @@ +import numpy as np + + +def exp_conv(T: float, t: np.ndarray, a: np.ndarray) -> np.ndarray: + """Exponential convolution operation of (1/T)exp(-t/T) with a + + Args: + T (float): exponent in time units + t (np.ndarray): array of time points + a (np.ndarray): array to be convolved with time exponential + + Returns: + np.ndarray: convolved array + """ + if T == 0: + return a + + n = len(t) + f = np.zeros((n,)) + + x = (t[1:n - 1] - t[0:n - 2]) / T + da = (a[1:n - 1] - a[0:n - 2]) / x + + E = np.exp(-x) + E0 = 1 - E + E1 = x - E0 + + add = a[0:n - 2] * E0 + da * E1 + + for i in range(0, n - 2): + f[i + 1] = E[i] * f[i] + add[i] + + f[n - 1] = f[n - 2] + return f + diff --git a/src/osipi/_tissue.py b/src/osipi/_tissue.py new file mode 100644 index 0000000..a0f4e5b --- /dev/null +++ b/src/osipi/_tissue.py @@ -0,0 +1,172 @@ +import numpy as np +from scipy.interpolate import interp1d +from ._convolution import exp_conv +import warnings + + +def tofts(t: np.ndarray, ca: np.ndarray, Ktrans: float, ve: float, Ta: float = 30.0, + discretization_method: str = "conv") -> np.ndarray: + """Tofts model as defined by Tofts and Kermode (1991) + + Args: + t (np.ndarray): array of time points in units of sec. [OSIPI code Q.GE1.004] + ca (np.ndarray): Arterial concentrations in mM for each time point in t. [OSIPI code Q.IC1.001] + Ktrans (float): Volume transfer constant in units of 1/min. [OSIPI code Q.PH1.008] + ve (float): Relative volume fraction of the extracellular extravascular compartment (e). [OSIPI code Q.PH1.001.[e]] + Ta (float, optional): Arterial delay time, i.e., difference in onset time between tissue curve and AIF in units of sec. Defaults to 30 seconds. [OSIPI code Q.PH1.007] + discretization_method (str, optional): Defines the discretization method. Options include + + – 'conv': Numerical convolution (default) [OSIPI code G.DI1.001] + + – 'exp': Exponential convolution [OSIPI code G.DI1.006] + + + Returns: + np.ndarray: Tissue concentrations in mM for each time point in t. + + See Also: + `extended_tofts` + + References: + - Lexicon url: https://osipi.github.io/OSIPI_CAPLEX/perfusionModels/#indicator-kinetic-models + - Lexicon code: M.IC1.004 + - OSIPI name: Tofts Model + - Adapted from contributions by: LEK_UoEdinburgh_UK, ST_USyd_AUS, MJT_UoEdinburgh_UK + + Example: + + Create an array of time points covering 6 min in steps of 1 sec, calculate the Parker AIF at these time points, calculate tissue concentrations + using the Tofts model and plot the results. + + Import packages: + + >>> import matplotlib.pyplot as plt + >>> import osipi + >>> import numpy + + Calculate AIF: + + >>> t = np.arange(0, 6*60, 1) + >>> ca = osipi.aif_parker(t) + + Calculate tissue concentrations and plot: + >>> Ktrans = 0.6 # in units of 1/min + >>> ve = 0.2 # takes values from 0 to 1 + >>> ct = osipi.tofts(t, ca, Ktrans, ve) + >>> plt.plot(t, ca, 'r', t, ct, 'b') + """ + if not np.allclose(np.diff(t), np.diff(t)[0]): + warnings.warn('Non-uniform time spacing detected. Time array may be resampled.', stacklevel=2) + + if Ktrans <= 0 or ve <= 0: + ct = 0 * ca + warnings.warn('Tissue concentration will be set to zero if Ktrans or ve are less than or equal to zero.', stacklevel=2) + else: + # Convert units + Ktrans = Ktrans/60 # from 1/min to 1/sec + + if discretization_method == 'exp': # Use exponential convolution + # Shift the AIF by the arterial delay time (if not zero) + if Ta != 0: + f = interp1d(t, ca, kind='linear', bounds_error=False, fill_value=0) + ca = (t > Ta) * f(t - Ta) + + Tc = ve / Ktrans + ct = ve * exp_conv(Tc, t, ca) + + else: # Use convolution by default + # Calculate the impulse response function + kep = Ktrans / ve + imp = Ktrans * np.exp(-1 * kep * t) + + # Shift the AIF by the arterial delay time (if not zero) + if Ta != 0: + f = interp1d(t, ca, kind='linear', bounds_error=False, fill_value=0) + ca = (t > Ta) * f(t - Ta) + + # Check if time data grid is uniformly spaced + if np.allclose(np.diff(t), np.diff(t)[0]): + # Convolve impulse response with AIF + convolution = np.convolve(ca, imp) + + # Discard unwanted points and make sure time spacing is correct + ct = convolution[0:len(t)] * t[1] + else: + # Resample at the smallest spacing + dt = np.min(np.diff(t)) + t_resampled = np.linspace(t[0], t[-1], int((t[-1]-t[0])/dt)) + ca_func = interp1d(t, ca, kind='quadratic', bounds_error=False, fill_value=0) + imp_func = interp1d(t, imp, kind='quadratic', bounds_error=False, fill_value=0) + ca_resampled = ca_func(t_resampled) + imp_resampled = imp_func(t_resampled) + # Convolve impulse response with AIF + convolution = np.convolve(ca_resampled, imp_resampled) + + # Discard unwanted points and make sure time spacing is correct + ct_resampled = convolution[0:len(t_resampled)] * t_resampled[1] + + # Restore time grid spacing + ct_func = interp1d(t_resampled, ct_resampled, kind='quadratic', bounds_error=False, fill_value=0) + ct = ct_func(t) + + return ct + + +def extended_tofts(t: np.ndarray, ca: np.ndarray, Ktrans: float, ve: float, Ta: float = 30.0, + discretization_method: str = "conv") -> np.ndarray: + """Extended tofts model as defined by ???. + + Note: + This function is not yet implemented. If you are implementing it yourself please consider submitting a code contribution to OSIPI, so nobody ever has to write this function again! + + Args: + t (np.ndarray): array of time points in units of sec. [OSIPI code Q.GE1.004] + ca (np.ndarray): Arterial concentrations in mM for each time point in t. [OSIPI code Q.IC1.001] + Ktrans (float): Volume transfer constant in units of 1/sec. [OSIPI code Q.PH1.008] + ve (float): Relative volume fraction of the extracellular extravascular compartment (e). [OSIPI code Q.PH1.001.[e]] + Ta (float, optional): Arterial delay time, i.e., difference in onset time between tissue curve and AIF in units of sec. Defaults to 30 seconds. [OSIPI code Q.PH1.007] + discretization_method (str, optional): Defines the discretization method. Options include + + – 'conv': Numerical convolution (default) [OSIPI code G.DI1.001] + + – 'exp': Exponential convolution [OSIPI code G.DI1.006] + + + Returns: + np.ndarray: Tissue concentrations in mM for each time point in t. + + See Also: + `tofts` + + References: + - Lexicon url: https://osipi.github.io/OSIPI_CAPLEX/perfusionModels/#indicator-kinetic-models + - Lexicon code: M.IC1.005 + - OSIPI name: Extended Tofts Model + - Adapted from contributions by: TBC + + Example: + + Create an array of time points covering 6min in steps of 1sec, calculate the Parker AIF at these time points, calculate tissue concentrations + using the Extended Tofts model and plot the results. + + Import packages: + + >>> import matplotlib.pyplot as plt + >>> import osipi + + Calculate AIF + + >>> t = np.arange(0, 6*60, 0.1) + >>> ca = osipi.aif_parker(t) + + Calculate tissue concentrations and plot + >>> Ktrans = 0.6/60 # in units of 1/sec + >>> ve = 0.2 # takes values from 0 to 1 + >>> ct = osipi.extended_tofts(t, ca, Ktrans, ve) + >>> plt.plot(t, ca, 'r', t, ct, 'b') + """ + + msg = 'This function is not yet implemented \n' + msg += 'If you implement it yourself, please consider submitting it as an OSIPI code contribution' + raise NotImplementedError(msg) + diff --git a/tests/test_tissue.py b/tests/test_tissue.py new file mode 100644 index 0000000..eb97a0b --- /dev/null +++ b/tests/test_tissue.py @@ -0,0 +1,74 @@ +import numpy as np +import osipi +import math +import matplotlib.pyplot as plt + +def test_tissue_tofts(): + + # 1. Basic operation of the function - test that the peak tissue concentration is less than the peak AIF + t = np.linspace(0, 6 * 60, 360) + ca = osipi.aif_parker(t) + ct = osipi.tofts(t, ca, Ktrans=0.6, ve=0.2) + assert np.round(np.max(ct)) < np.round(np.max(ca)) + + # 2. Basic operation of the function - test with non-uniform spacing of time array + t = np.geomspace(1, 6*60+1, num=360)-1 + ca = osipi.aif_parker(t) + ct = osipi.tofts(t, ca, Ktrans=0.6, ve=0.2) + assert np.round(np.max(ct)) < np.round(np.max(ca)) + + # 3. The offset option - test that the tissue concentration is shifted from the AIF by the specified offset time + t = np.arange(0, 6 * 60, 1) + ca = osipi.aif_parker(t) + ct = osipi.tofts(t, ca, Ktrans=0.6, ve=0.2, Ta=60.0) + assert (np.min(np.where(ct>0.0)) - np.min(np.where(ca>0.0)) - 1)*1 == 60.0 + + # 4. Test that the discretization options give almost the same result - time step must be very small + t = np.arange(0, 6 * 60, 0.01) + ca = osipi.aif_parker(t) + ct_conv = osipi.tofts(t, ca, Ktrans=0.6, ve=0.2) + ct_exp = osipi.tofts(t, ca, Ktrans=0.6, ve=0.2, discretization_method='exp') + assert np.allclose(ct_conv, ct_exp, rtol=1e-4, atol=1e-3) + + # 5. Test that the ratio of the area under the ct and ca curves is approximately the extracellular volume + t = np.arange(0, 6 * 60, 1) + ca = osipi.aif_parker(t) + ct_conv = osipi.tofts(t, ca, Ktrans=0.6, ve=0.2) + ct_exp = osipi.tofts(t, ca, Ktrans=0.6, ve=0.2, discretization_method='exp') + assert math.isclose(np.trapz(ct_conv, t)/np.trapz(ca, t), 0.2, abs_tol=1e-1) + assert math.isclose(np.trapz(ct_exp, t)/np.trapz(ca, t), 0.2, abs_tol=1e-1) + + # 6. Test specific use cases + t = np.arange(0, 6 * 60, 1) + ca = osipi.aif_parker(t) + ct_conv = osipi.tofts(t, ca, Ktrans=0, ve=0.2) + assert np.count_nonzero(ct_conv) == 0 + + ct_exp = osipi.tofts(t, ca, Ktrans=0, ve=0.2, discretization_method='exp') + assert np.count_nonzero(ct_exp) == 0 + + ct_conv = osipi.tofts(t, ca, Ktrans=0.6, ve=0) + assert np.count_nonzero(ct_conv) == 0 + + ct_exp = osipi.tofts(t, ca, Ktrans=0.6, ve=0, discretization_method='exp') + assert np.count_nonzero(ct_exp) == 0 + +def test_tissue_extended_tofts(): + + # Not implemented yet so need to raise an error + t = np.arange(0, 6 * 60, 0.1) + ca = osipi.aif_parker(t) + try: + ct = osipi.extended_tofts(t, ca, Ktrans=0.6/60, ve=0.2) + except: + assert True + else: + assert False + +if __name__ == "__main__": + + test_tissue_tofts() + test_tissue_extended_tofts() + + print('All tissue concentration model tests passed!!') +