diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 368ba17..3700104 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,4 +1,4 @@ -name: ✨ Idea [Feature request] +name: ✨ Idea [Feature request] description: Tell us about the idea you have ! title: "[Feature] " labels: [idea] diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml index f736444..a12035f 100755 --- a/.github/workflows/documentation.yaml +++ b/.github/workflows/documentation.yaml @@ -2,7 +2,7 @@ name: Docs run-name: "Updating Docs as ${{ github.actor }} has pushed changes to main" on: push: - branches: + branches: - main jobs: @@ -43,4 +43,3 @@ jobs: force_orphan: true - run: echo "Status of job = ${{ job.status }}." - diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 0000000..4efef22 --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,23 @@ +name: Lint and Auto-Fix Code + +on: + push: + pull_request: + branches: + - main + +jobs: + + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v3 + + - name: Install pre-commit + run: pip install pre-commit + + - name: Run pre-commit + run: pre-commit run --all-files diff --git a/.gitignore b/.gitignore index 02653df..b4ef2fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .idea/ /.venv dist +pytest_cache +src/osipi/__pycache__ tests/__pycache__ -src/osipi/__pycache__ \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..ea07871 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,32 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.8 # Use the latest version + hooks: + - id: ruff + args: [--fix] # Optional: to enable lint fixes + - id: ruff-format + - repo: https://github.com/pycqa/flake8 + rev: 7.0.0 + hooks: + - id: flake8 + args: + - --exclude=venv,.git,__pycache__,.eggs,.mypy_cache,.pytest_cache + - --max-line-length=100 + - --ignore=E203 + - --per-file-ignores=__init__.py:F401 + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + + - repo: https://github.com/rstcheck/rstcheck + rev: v6.1.2 # Use the latest version + hooks: + - id: rstcheck + entry: rstcheck --ignore-messages="(Hyperlink target .* is not referenced.|No directive entry for + .*|Unknown directive type .*|Duplicate explicit target name.*)" + args: [ + "--ignore-roles", "ref", + ] diff --git a/README.md b/README.md index a721b36..6aa9ef4 100755 --- a/README.md +++ b/README.md @@ -33,20 +33,11 @@ We welcome contributions to OSIPI! To contribute, follow these steps: - `pip install poetry` - `poetry install` - `poetry shell` - + - Make your changes and commit them with descriptive messages. - Push your changes to your fork. - Submit a pull request to the main OSIPI repository. -#### As mentioned before, this project is still in the early stages of development. If you'd like to contribute by adding functionality, we recommend opening an issue first to discuss your proposed functionality and the best ways to implement it. +#### As mentioned before, this project is still in the early stages of development. If you'd like to contribute by adding functionality, we recommend opening an issue first to discuss your proposed functionality and the best ways to implement it. ## Details For more detail please see the [documentation](https://osipi.github.io/pypi/). - - - - - - - - - diff --git a/docs/examples/README.rst b/docs/examples/README.rst index 9eeb80a..adff135 100755 --- a/docs/examples/README.rst +++ b/docs/examples/README.rst @@ -3,5 +3,3 @@ Examples ######## Illustrating common use cases of osipi. - - diff --git a/docs/examples/aif/README.rst b/docs/examples/aif/README.rst index 03dc96f..1e84a81 100755 --- a/docs/examples/aif/README.rst +++ b/docs/examples/aif/README.rst @@ -1,5 +1,3 @@ ************************ Arterial Input Functions ************************ - - diff --git a/docs/examples/aif/plot_aif_parker.py b/docs/examples/aif/plot_aif_parker.py index 4f3a82d..4338078 100755 --- a/docs/examples/aif/plot_aif_parker.py +++ b/docs/examples/aif/plot_aif_parker.py @@ -1,46 +1,48 @@ -""" -====================================== -The Parker AIF - a play with variables -====================================== +"""====================================== + +The Parker AIF - a play with variables ====================================== Simulating a Parker +AIF with different settings. -Simulating a Parker AIF with different settings. """ +import matplotlib.pyplot as plt + # %% # Import necessary packages 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) +# 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, 'r-') -plt.plot(t, 0*t, 'k-') -plt.xlabel('Time (sec)') -plt.ylabel('Plasma concentration (mM)') +plt.plot(t, ca, "r-") +plt.plot(t, 0 * t, "k-") +plt.xlabel("Time (sec)") +plt.ylabel("Plasma concentration (mM)") plt.show() # %% -# The bolus arrival time (BAT) defaults to 0s. 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') +plt.plot(t, ca, "b-", label="BAT = 0s") ca = osipi.aif_parker(t, BAT=30) -plt.plot(t, ca, 'r-', label='BAT = 30s') +plt.plot(t, ca, "r-", label="BAT = 30s") ca = osipi.aif_parker(t, BAT=60) -plt.plot(t, ca, 'g-', label='BAT = 60s') +plt.plot(t, ca, "g-", label="BAT = 60s") ca = osipi.aif_parker(t, BAT=90) -plt.plot(t, ca, 'm-', label='BAT = 90s') -plt.xlabel('Time (sec)') -plt.ylabel('Plasma concentration (mM)') +plt.plot(t, ca, "m-", label="BAT = 90s") +plt.xlabel("Time (sec)") +plt.ylabel("Plasma concentration (mM)") plt.legend() plt.show() diff --git a/docs/examples/aif/plot_dummy.py b/docs/examples/aif/plot_dummy.py index 1f4d326..13cbac2 100755 --- a/docs/examples/aif/plot_dummy.py +++ b/docs/examples/aif/plot_dummy.py @@ -3,44 +3,47 @@ A dummy script ============== -Dummy script to illustrate structure of examples folder +Dummy script to illustrate structure of examples folder """ +import matplotlib.pyplot as plt + # %% # Import necessary packages 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) +# 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, 'r-') -plt.plot(t, 0*t, 'k-') -plt.xlabel('Time (sec)') -plt.ylabel('Plasma concentration (mM)') +plt.plot(t, ca, "r-") +plt.plot(t, 0 * t, "k-") +plt.xlabel("Time (sec)") +plt.ylabel("Plasma concentration (mM)") 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 30s. 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') +plt.plot(t, ca, "b-", label="BAT = 0s") ca = osipi.aif_parker(t, BAT=30) -plt.plot(t, ca, 'r-', label='BAT = 30s') +plt.plot(t, ca, "r-", label="BAT = 30s") ca = osipi.aif_parker(t, BAT=60) -plt.plot(t, ca, 'g-', label='BAT = 60s') +plt.plot(t, ca, "g-", label="BAT = 60s") ca = osipi.aif_parker(t, BAT=90) -plt.plot(t, ca, 'm-', label='BAT = 90s') -plt.xlabel('Time (sec)') -plt.ylabel('Plasma concentration (mM)') +plt.plot(t, ca, "m-", label="BAT = 90s") +plt.xlabel("Time (sec)") +plt.ylabel("Plasma concentration (mM)") plt.legend() plt.show() diff --git a/docs/examples/tissue/README.rst b/docs/examples/tissue/README.rst index 6f613fb..022965a 100755 --- a/docs/examples/tissue/README.rst +++ b/docs/examples/tissue/README.rst @@ -1,5 +1,3 @@ ********************* Tissue concentrations ********************* - - diff --git a/docs/examples/tissue/plot_extended_tofts.py b/docs/examples/tissue/plot_extended_tofts.py index a4c60d3..f2df6fb 100755 --- a/docs/examples/tissue/plot_extended_tofts.py +++ b/docs/examples/tissue/plot_extended_tofts.py @@ -6,46 +6,50 @@ Simulating tissue concentrations from extended Tofts model with different settings. """ +import matplotlib.pyplot as plt + # %% # 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) +# 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 plasma volumes of 0.05, 0.2 and 0.6 -Ktrans = 0.2 # in units of 1/min +# Plot the tissue concentrations for an extracellular volume fraction +# of 0.2 and 3 different plasma volumes of 0.05, 0.2 and 0.6 +Ktrans = 0.2 # in units of 1/min ve = 0.2 # volume fraction between 0 and 1 -vp = [0.05, 0.2, 0.6] # volume fraction between 0 and 1 +vp = [0.05, 0.2, 0.6] # volume fraction between 0 and 1 ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[0]) -plt.plot(t, ct, 'b-', label=f'vp = {vp[0]}') +plt.plot(t, ct, "b-", label=f"vp = {vp[0]}") ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[1]) -plt.plot(t, ct, 'g-', label=f'vp = {vp[1]}') +plt.plot(t, ct, "g-", label=f"vp = {vp[1]}") ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[2]) -plt.plot(t, ct, 'm-', label=f'vp = {vp[2]}') -plt.xlabel('Time (sec)') -plt.ylabel('Tissue concentration (mM)') +plt.plot(t, ct, "m-", label=f"vp = {vp[2]}") +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, Ktrans of 0.2 /min and vp of 0.05 -ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[0]) # Defaults to Convolution -plt.plot(t, ct, 'b-', label='Convolution') -ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[0], discretization_method='exp') -plt.plot(t, ct, 'g-', label='Exponential Convolution') -plt.title(f'Ktrans = {Ktrans} /min') -plt.xlabel('Time (sec)') -plt.ylabel('Tissue concentration (mM)') +# Comparing different discretization methods for an extracellular +# volume fraction of 0.2, Ktrans of 0.2 /min and vp of 0.05 +ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[0]) # Defaults to Convolution +plt.plot(t, ct, "b-", label="Convolution") +ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[0], discretization_method="exp") +plt.plot(t, ct, "g-", label="Exponential Convolution") +plt.title(f"Ktrans = {Ktrans} /min") +plt.xlabel("Time (sec)") +plt.ylabel("Tissue concentration (mM)") plt.legend() plt.show() diff --git a/docs/examples/tissue/plot_tofts.py b/docs/examples/tissue/plot_tofts.py index dc22974..d623da9 100755 --- a/docs/examples/tissue/plot_tofts.py +++ b/docs/examples/tissue/plot_tofts.py @@ -6,45 +6,50 @@ Simulating tissue concentrations from Tofts model with different settings. """ +import matplotlib.pyplot as plt + # %% # 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) +# 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 +# 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') +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') +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.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)') +# 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() diff --git a/docs/requirements.txt b/docs/requirements.txt index 63f5b84..61521a6 100755 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -14,3 +14,4 @@ sphinxcontrib-qthelp sphinxcontrib-serializinghtml #nbsphinx sphinx-gallery +rstcheck[sphinx] diff --git a/docs/source/_templates/autosummary.rst b/docs/source/_templates/autosummary.rst index c5f90e8..7775984 100755 --- a/docs/source/_templates/autosummary.rst +++ b/docs/source/_templates/autosummary.rst @@ -6,6 +6,7 @@ {% if objtype in ['class'] %} .. auto{{ objtype }}:: {{ objname }} + :show-inheritance: :special-members: __call__ @@ -16,11 +17,14 @@ {% if objtype in ['class', 'method', 'function'] %} {% if objname in ['AxesGrid', 'Scalable', 'HostAxes', 'FloatingAxes', + 'ParasiteAxesAuxTrans', 'ParasiteAxes'] %} + .. Filter out the above aliases to other classes, as sphinx gallery creates no example file for those (sphinx-gallery/sphinx-gallery#365) {% else %} + .. minigallery:: {{module}}.{{objname}} :add-heading: diff --git a/docs/source/about/index.rst b/docs/source/about/index.rst index 5909101..647e2ac 100755 --- a/docs/source/about/index.rst +++ b/docs/source/about/index.rst @@ -2,7 +2,7 @@ About ##### -``osipi`` is developed by the Open Source Initiative for Perfusion Imaging `OSIPI <https://osipi.github.io/>`_, an initiative of the perfusion study group of the International Society for Magnetic Resonance in Medicine `ISMRM <https://www.ismrm.org>`_. +``osipi`` is developed by the Open Source Initiative for Perfusion Imaging `OSIPI <https://osipi.github.io/>`_, an initiative of the perfusion study group of the International Society for Magnetic Resonance in Medicine `ISMRM <https://www.ismrm.org>`_. The ``osipi`` package structure and logic follows the `lexicon <https://osipi.github.io/OSIPI_CAPLEX/>`_ defined by OSIPI, and wraps around selected implementations collected in the `code contributions <https://github.com/OSIPI/DCE-DSC-MRI_CodeCollection>`_ of OSIPI. @@ -10,4 +10,3 @@ Scope ^^^^^ `osipi` currently only includes methods for the dynamic contrast (DC) approach to perfusion MRI (DC-MRI, a unifying term for the separate fields DCE-MRI and DSC-MRI). In particular arterial spin labelling (ASL) solutions are not currently included, but this may change in the future. - diff --git a/docs/source/conf.py b/docs/source/conf.py index 6ce4c2c..ee5f9f1 100755 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -3,102 +3,111 @@ # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html -# -- Project information ----------------------------------------------------- +import os + +# -- Project information --------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information -project = 'osipi' -copyright = '2022, OSIPI' -author = 'OSIPI' -release = '0.1.1' +import sys -# -- Path setup -------------------------------------------------------------- +project = "osipi" +copyright = "2022, OSIPI" +author = "OSIPI" +release = "0.1.1" + +# -- Path setup ------------------------------------------------------ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here -import os -import sys -sys.path.insert(0, os.path.abspath('../../src')) +# documentation root, use os.path.abspath to make it absolute, like +# shown here -# -- General configuration --------------------------------------------------- +sys.path.insert(0, os.path.abspath("../../src")) + +# -- General configuration ------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration # Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones extensions = [ - 'sphinx.ext.napoleon', # parsing of NumPy and Google style docstrings - 'sphinx.ext.autodoc', # sphinx autodocumentation generation - 'sphinx.ext.autosummary', # generates function/method/attribute summary lists - 'sphinx.ext.viewcode', # viewing source code - 'sphinx.ext.intersphinx', # generate links to the documentation of objects in external projects - 'autodocsumm', - 'myst_parser', # parser for markdown language - 'sphinx_copybutton', # copy button for code blocks - 'sphinx_design', # sphinx web design components - 'sphinx_remove_toctrees', # selectively remove toctree objects from pages - 'sphinx_gallery.gen_gallery', # thumbnail galleries + "sphinx.ext.napoleon", # parsing of NumPy and Google style docstrings + "sphinx.ext.autodoc", # sphinx autodocumentation generation + # generates function/method/attribute summary lists + "sphinx.ext.autosummary", + "sphinx.ext.viewcode", # viewing source code + # generate links to the documentation of objects in external + # projects + "sphinx.ext.intersphinx", + "autodocsumm", + "myst_parser", # parser for markdown language + "sphinx_copybutton", # copy button for code blocks + "sphinx_design", # sphinx web design components + "sphinx_remove_toctrees", # selectively remove toctree objects from pages + "sphinx_gallery.gen_gallery", # thumbnail galleries ] # Settings for sphinx-gallery, see # https://sphinx-gallery.github.io/stable/getting_started.html#create-simple-gallery sphinx_gallery_conf = { # path to the example scripts relative to conf.py - 'examples_dirs': '../examples', - + "examples_dirs": "../examples", # path to where to save gallery generated output - 'gallery_dirs': 'generated/examples', - + "gallery_dirs": "generated/examples", # directory where function/class granular galleries are stored - 'backreferences_dir': 'generated/backreferences', - - # Modules for which function/class level galleries are created. - 'doc_module': ('osipi', ), - + "backreferences_dir": "generated/backreferences", + # Modules for which function/class level galleries are created. + "doc_module": ("osipi",), # objects to exclude from implicit backreferences. The default option # is an empty set, i.e. exclude nothing. - 'exclude_implicit_doc': {}, - + "exclude_implicit_doc": {}, # thumbnail for examples that do not generate any plot - 'default_thumb_file': '_static/osipi.png', - + "default_thumb_file": "_static/osipi.png", # Disabling download button of all scripts - 'download_all_examples': False, + "download_all_examples": False, } -# This way a link to other methods, classes, or modules can be made with back ticks so that you don't have to use qualifiers like :class:, :func:, :meth: and the likes -default_role = 'obj' +# This way a link to other methods, classes, or modules can be made +# with back ticks so that you don't have to use qualifiers like +# :class:, :func:, :meth: and the likes +default_role = "obj" -# Add any paths that contain templates here, relative to this directory -templates_path = ['_templates'] +# Add any paths that contain templates here, relative to this +# directory +templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path exclude_patterns = [] -# -- Extension configuration ------------------------------------------------- -# Map intersphinx to pre-exisiting documentation from other projects used in this project +# -- Extension configuration ----------------------------------------- +# Map intersphinx to pre-exisiting documentation from other projects +# used in this project intersphinx_mapping = { - 'python': ('https://docs.python.org/3/', None), - 'numpy': ('https://numpy.org/doc/stable/', None), - 'matplotlib': ('https://matplotlib.org/stable/', None), - 'pydicom': ('https://pydicom.github.io/pydicom/stable/', None), - 'nibabel': ('https://nipy.org/nibabel/', None), - 'pandas': ('https://pandas.pydata.org/docs/', None), - 'skimage': ('https://scikit-image.org/docs/stable/', None), + "python": ("https://docs.python.org/3/", None), + "numpy": ("https://numpy.org/doc/stable/", None), + "matplotlib": ("https://matplotlib.org/stable/", None), + "pydicom": ("https://pydicom.github.io/pydicom/stable/", None), + "nibabel": ("https://nipy.org/nibabel/", None), + "pandas": ("https://pandas.pydata.org/docs/", None), + "skimage": ("https://scikit-image.org/docs/stable/", None), } -autosummary_generate = True # enable autosummary extension +autosummary_generate = True # enable autosummary extension -# Tell sphinx-autodoc-typehints to generate stub parameter annotations including types, even if the parameters aren't explicitly documented. +# Tell sphinx-autodoc-typehints to generate stub parameter annotations +# including types, even if the parameters aren't explicitly +# documented. always_document_param_types = True # Remove auto-generated API docs from sidebars. remove_from_toctrees = ["_autosummary/*"] -# -- Options for HTML output ------------------------------------------------- +# -- Options for HTML output ----------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -# The theme to use for HTML and HTML Help pages. See the documentation for a list of builtin themes -html_theme = 'pydata_sphinx_theme' +# The theme to use for HTML and HTML Help pages. See the +# documentation for a list of builtin themes +html_theme = "pydata_sphinx_theme" html_theme_options = { "github_url": "https://github.com/OSIPI/pypi", @@ -107,18 +116,21 @@ } html_context = { "github_url": "https://github.com", - "github_user": "OSIPI", - "github_repo": "pypi", - "github_version":"dev", + "github_user": "OSIPI", + "github_repo": "pypi", + "github_version": "dev", "doc_path": "docs/source", } -# Add any paths that contain custom static files (such as style sheets) here, relative to this directory. They are copied after the builtin static files, so a file named "default.css" will overwrite the builtin "default.css" -html_static_path = ['_static'] +# Add any paths that contain custom static files (such as style +# sheets) here, relative to this directory. They are copied after the +# builtin static files, so a file named "default.css" will overwrite +# the builtin "default.css" +html_static_path = ["_static"] # The suffix(es) of source filenames. -source_suffix = ['.rst', '.md'] +source_suffix = [".rst", ".md"] # The name of an image file (relative to this directory) to place at the top # of the sidebar. -html_logo = '_static/osipi.png' \ No newline at end of file +html_logo = "_static/osipi.png" diff --git a/docs/source/developers_guide/index.rst b/docs/source/developers_guide/index.rst index bf2c080..c879b92 100755 --- a/docs/source/developers_guide/index.rst +++ b/docs/source/developers_guide/index.rst @@ -4,9 +4,9 @@ Developer guide ############### -There are multiple ways to contribute to `osipi` and we welcome them all. `osipi` is a tool developed by the research community for the research community, and we are all responsible for ensuring it is as good as it can be. So, if you feel some part of it is not, fix it! +There are multiple ways to contribute to `osipi` and we welcome them all. `osipi` is a tool developed by the research community for the research community, and we are all responsible for ensuring it is as good as it can be. So, if you feel some part of it is not, fix it! -The way to do this is by making a pull request on github. If you are not familiar with github pull requests, it is not as scary as it sounds. The simplest way is to find the file that you want to edit on gtihub in your browser, edit it manually and follow the prompts to create a fork and pull request. +The way to do this is by making a pull request on github. If you are not familiar with github pull requests, it is not as scary as it sounds. The simplest way is to find the file that you want to edit on gtihub in your browser, edit it manually and follow the prompts to create a fork and pull request. ************************** How to contribute examples @@ -16,9 +16,9 @@ One way to contribute is by providing examples of how you used `osipi` for a spe 1. Code up your example in a single python file in a narrative style, similar to a notebook. Have a look at the current examples to see how these need to be formatted, especially thier docstrings, to make sure they show up properly on the website. 2. When you save your file, make sure the filename starts with *plot_*. -3. Then drop your file in the examples folder `osipi-docs-examples` in the appropriate subolder. +3. Then drop your file in the examples folder `osipi-docs-examples` in the appropriate subolder. -When the documentation is generated, your example will automatically appear in the examples gallery and also in the documentation of any function you are using in the example. +When the documentation is generated, your example will automatically appear in the examples gallery and also in the documentation of any function you are using in the example. ******************************* @@ -27,7 +27,7 @@ How to contribute documentation `osipi` is a user interface and for that reason good, clear and well-structured documentation is equally important as the quality of the functionality itself. We especially welcome suggestions for improving the documentation from end-users who are not necessarily contributing new code. You know best what works, and what doesn't. -If you are a user of `osipi`, and some part of the documentation is not as clear as it can be, then submit your suggestions for improvement and make sure that the next person will not have to face the same issues. +If you are a user of `osipi`, and some part of the documentation is not as clear as it can be, then submit your suggestions for improvement and make sure that the next person will not have to face the same issues. If you want to edit the documentation of a specific function, then you need to find the function in the osipi source code `osipi-src`. Find the function and edit the documentation string immediately below its definition. If you want to edit any other part of the documentation, find the appropriate file in the documentation source code `osipi-docs-source` and edit it there. @@ -38,7 +38,7 @@ 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 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. +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. The tests are defined in the folder `osipi-tests`. @@ -49,9 +49,9 @@ How to contribute functionality 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 <https://github.com/OSIPI/DCE-DSC-MRI_CodeCollection/wiki/How-to-contribute-code>`_. The task force will catalogue your code in the contributions repository and test it as explained in the `guidance <https://github.com/OSIPI/DCE-DSC-MRI_CodeCollection/wiki/The-testing-approach>`_. 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. +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 <https://github.com/OSIPI/DCE-DSC-MRI_CodeCollection/wiki/How-to-contribute-code>`_. The task force will catalogue your code in the contributions repository and test it as explained in the `guidance <https://github.com/OSIPI/DCE-DSC-MRI_CodeCollection/wiki/The-testing-approach>`_. 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. -While this is the general process, we accept there may be situations where a new submission to the contributions repository is overkill, for instance if your improvement concerns documentation only, or improvements in code structure or style. In that case a direct pull request to `osipi` may be acceptable - when in doubt please contact the OSIPI repository lead in the first instance to avoid unnecessary work. +While this is the general process, we accept there may be situations where a new submission to the contributions repository is overkill, for instance if your improvement concerns documentation only, or improvements in code structure or style. In that case a direct pull request to `osipi` may be acceptable - when in doubt please contact the OSIPI repository lead in the first instance to avoid unnecessary work. See section on design principles below for general requirements from `osipi` code snippets. @@ -61,7 +61,7 @@ How to contribute issues ************************ If you have a constructive suggestion for how `osipi` can be improved, but you are not able to address it yourself for some reason, it is still extremely helpful if you write this up as an issue so it can be picked up by others at a later stage. To write up an issue, go to the `osipi` repository on github, select `issues` and write a new one. Make sure to provide sufficient detail so that others can understand and address the issue. - + ***************** Design principles @@ -70,13 +70,13 @@ Design principles Style guide ^^^^^^^^^^^ -`osipi` follows the `google python style guide <https://google.github.io/styleguide/pyguide.html>`_. This means especially also that we expect proper attention to error handling. Consider for instance what happens if a user calls your snippet using arguments of incorrect type or length. Will they get an appropriate error message that will help them identify and fix the error? +`osipi` follows the `google python style guide <https://google.github.io/styleguide/pyguide.html>`_. This means especially also that we expect proper attention to error handling. Consider for instance what happens if a user calls your snippet using arguments of incorrect type or length. Will they get an appropriate error message that will help them identify and fix the error? Package structure ^^^^^^^^^^^^^^^^^ -The `osipi` documentation follows the structure of the OSIPI Lexicon exactly - see `here <https://osipi.github.io/OSIPI_CAPLEX/>`_ for a detailed description of the Lexicon. +The `osipi` documentation follows the structure of the OSIPI Lexicon exactly - see `here <https://osipi.github.io/OSIPI_CAPLEX/>`_ for a detailed description of the Lexicon. From a user perspective, the package structure is a flat list of functions that can all be accessed as `osipi.some_function`. They are listed in the __init__ file of the package, directly under the folder `src\osipi`. For clarity the code itself is organised into modules, but these may evolve over time and should not be accessed directly. Module names all start with an underscore `_module.py` to emphasise their private and transient nature. Equally, subfolders may be added in the future as the package grows. @@ -84,18 +84,18 @@ From a user perspective, the package structure is a flat list of functions that :: src - └── osipi + └── osipi ├── __init__.py ├── _module_1.py ├── _module_2.py - ├── + ├── └── _module_n.py Code snippets ^^^^^^^^^^^^^ -`osipi` is a collection of *simple* code snippets following a *simple* functional programming paradigm (did you see how we said *simple* twice there?). Each code snippet is a python function that takes OSIPI variables as argument and returns other OSIPI variables as result. At this stage we are *not* planning to include an object oriented interface or internal logic as this reduces the modularity of the code snippets, reduces compatibility with other packages and increases the overhead of learning how to use `osipi`. Therefore all code contributions will essentially exist of a new function, or an improvement of an existing function. +`osipi` is a collection of *simple* code snippets following a *simple* functional programming paradigm (did you see how we said *simple* twice there?). Each code snippet is a python function that takes OSIPI variables as argument and returns other OSIPI variables as result. At this stage we are *not* planning to include an object oriented interface or internal logic as this reduces the modularity of the code snippets, reduces compatibility with other packages and increases the overhead of learning how to use `osipi`. Therefore all code contributions will essentially exist of a new function, or an improvement of an existing function. Beyond the general requirements of the `google python style guide <https://google.github.io/styleguide/pyguide.html>`_, `osipi` requires that each new function is accompanied by an appropriate test in the tests folder, and that each function fully conforms to the Lexicon. In particular: @@ -111,7 +111,7 @@ Beyond the general requirements of the `google python style guide <https://googl 3. All arguments and return values must be provided in OSIPI units as defined in the Lexicon. 4. Arguments should be provided using OSIPI notation as defined in the Lexicon. 5. The docstring of the function must contain a self contained code example that runs the function and illustrates the output. - + .. note:: @@ -119,5 +119,4 @@ Beyond the general requirements of the `google python style guide <https://googl .. note:: - The original `library for code contributions <https://github.com/OSIPI/DCE-DSC-MRI_CodeCollection/wiki/How-to-contribute-code>`_ is less stringent as to code structure and documentation or testing requirements. However, it is nevertheless advisable to adhere to the same guidelines when submitting code to the original contributions repository as this will make it easier for testers to understand and run your code, and it will reduce the overhead on your part in rewriting the code if it is subsequently invited as a contribution to `osipi`. - + The original `library for code contributions <https://github.com/OSIPI/DCE-DSC-MRI_CodeCollection/wiki/How-to-contribute-code>`_ is less stringent as to code structure and documentation or testing requirements. However, it is nevertheless advisable to adhere to the same guidelines when submitting code to the original contributions repository as this will make it easier for testers to understand and run your code, and it will reduce the overhead on your part in rewriting the code if it is subsequently invited as a contribution to `osipi`. diff --git a/docs/source/generated/api/osipi.aif_georgiou.rst b/docs/source/generated/api/osipi.aif_georgiou.rst index 11c89fe..d8ba024 100755 --- a/docs/source/generated/api/osipi.aif_georgiou.rst +++ b/docs/source/generated/api/osipi.aif_georgiou.rst @@ -14,5 +14,3 @@ .. minigallery:: osipi.aif_georgiou :add-heading: - - diff --git a/docs/source/generated/api/osipi.aif_parker.rst b/docs/source/generated/api/osipi.aif_parker.rst index 33c9b48..35cd470 100755 --- a/docs/source/generated/api/osipi.aif_parker.rst +++ b/docs/source/generated/api/osipi.aif_parker.rst @@ -14,5 +14,3 @@ .. minigallery:: osipi.aif_parker :add-heading: - - diff --git a/docs/source/generated/api/osipi.aif_weinmann.rst b/docs/source/generated/api/osipi.aif_weinmann.rst index 80684b4..e412fb0 100755 --- a/docs/source/generated/api/osipi.aif_weinmann.rst +++ b/docs/source/generated/api/osipi.aif_weinmann.rst @@ -14,5 +14,3 @@ .. minigallery:: osipi.aif_weinmann :add-heading: - - diff --git a/docs/source/generated/api/osipi.extended_tofts.rst b/docs/source/generated/api/osipi.extended_tofts.rst index 48a2d6e..32027a9 100755 --- a/docs/source/generated/api/osipi.extended_tofts.rst +++ b/docs/source/generated/api/osipi.extended_tofts.rst @@ -14,5 +14,3 @@ .. minigallery:: osipi.extended_tofts :add-heading: - - diff --git a/docs/source/generated/api/osipi.tofts.rst b/docs/source/generated/api/osipi.tofts.rst index 69f53b7..317c72b 100755 --- a/docs/source/generated/api/osipi.tofts.rst +++ b/docs/source/generated/api/osipi.tofts.rst @@ -14,5 +14,3 @@ .. 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 e0b9646..099b5ed 100755 --- a/docs/source/generated/backreferences/osipi.aif_parker.examples +++ b/docs/source/generated/backreferences/osipi.aif_parker.examples @@ -36,4 +36,3 @@ Examples using ``osipi.aif_parker`` .. raw:: html </div> - diff --git a/docs/source/generated/backreferences/osipi.extended_tofts.examples b/docs/source/generated/backreferences/osipi.extended_tofts.examples index 9fb76fd..fab4186 100755 --- a/docs/source/generated/backreferences/osipi.extended_tofts.examples +++ b/docs/source/generated/backreferences/osipi.extended_tofts.examples @@ -36,4 +36,3 @@ Examples using ``osipi.extended_tofts`` .. raw:: html </div> - diff --git a/docs/source/generated/backreferences/osipi.tofts.examples b/docs/source/generated/backreferences/osipi.tofts.examples index 307717e..8157cbb 100755 --- a/docs/source/generated/backreferences/osipi.tofts.examples +++ b/docs/source/generated/backreferences/osipi.tofts.examples @@ -36,4 +36,3 @@ Examples using ``osipi.tofts`` .. raw:: html </div> - diff --git a/docs/source/generated/examples/aif/index.rst b/docs/source/generated/examples/aif/index.rst index 87275a9..d9ec3b3 100755 --- a/docs/source/generated/examples/aif/index.rst +++ b/docs/source/generated/examples/aif/index.rst @@ -59,4 +59,3 @@ Arterial Input Functions /generated/examples/aif/plot_aif_parker /generated/examples/aif/plot_dummy - diff --git a/docs/source/generated/examples/aif/plot_aif_parker.ipynb b/docs/source/generated/examples/aif/plot_aif_parker.ipynb index 121a62b..f0452bc 100755 --- a/docs/source/generated/examples/aif/plot_aif_parker.ipynb +++ b/docs/source/generated/examples/aif/plot_aif_parker.ipynb @@ -1,86 +1,113 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n# The Parker AIF - a play with variables\n\nSimulating a Parker AIF 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 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, 'r-')\nplt.plot(t, 0*t, 'k-')\nplt.xlabel('Time (sec)')\nplt.ylabel('Plasma concentration (mM)')\nplt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "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" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "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()\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" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# The Parker AIF - a play with variables\n\nSimulating a Parker AIF with different settings. \n" + ] }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file + { + "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\n", + "import matplotlib.pyplot as plt\n", + "import 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.\n", + "t = np.arange(0, 6 * 60, 0.5)\n", + "\n", + "# Create an AIF with default settings\n", + "ca = osipi.aif_parker(t)\n", + "\n", + "# Plot the AIF over the full range\n", + "plt.plot(t, ca, \"r-\")\n", + "plt.plot(t, 0 * t, \"k-\")\n", + "plt.xlabel(\"Time (sec)\")\n", + "plt.ylabel(\"Plasma concentration (mM)\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "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" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "ca = osipi.aif_parker(t, BAT=0)\n", + "plt.plot(t, ca, \"b-\", label=\"BAT = 0s\")\n", + "ca = osipi.aif_parker(t, BAT=30)\n", + "plt.plot(t, ca, \"r-\", label=\"BAT = 30s\")\n", + "ca = osipi.aif_parker(t, BAT=60)\n", + "plt.plot(t, ca, \"g-\", label=\"BAT = 60s\")\n", + "ca = osipi.aif_parker(t, BAT=90)\n", + "plt.plot(t, ca, \"m-\", label=\"BAT = 90s\")\n", + "plt.xlabel(\"Time (sec)\")\n", + "plt.ylabel(\"Plasma concentration (mM)\")\n", + "plt.legend()\n", + "plt.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 +} diff --git a/docs/source/generated/examples/aif/plot_aif_parker.py b/docs/source/generated/examples/aif/plot_aif_parker.py index 4f3a82d..4338078 100755 --- a/docs/source/generated/examples/aif/plot_aif_parker.py +++ b/docs/source/generated/examples/aif/plot_aif_parker.py @@ -1,46 +1,48 @@ -""" -====================================== -The Parker AIF - a play with variables -====================================== +"""====================================== + +The Parker AIF - a play with variables ====================================== Simulating a Parker +AIF with different settings. -Simulating a Parker AIF with different settings. """ +import matplotlib.pyplot as plt + # %% # Import necessary packages 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) +# 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, 'r-') -plt.plot(t, 0*t, 'k-') -plt.xlabel('Time (sec)') -plt.ylabel('Plasma concentration (mM)') +plt.plot(t, ca, "r-") +plt.plot(t, 0 * t, "k-") +plt.xlabel("Time (sec)") +plt.ylabel("Plasma concentration (mM)") plt.show() # %% -# The bolus arrival time (BAT) defaults to 0s. 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') +plt.plot(t, ca, "b-", label="BAT = 0s") ca = osipi.aif_parker(t, BAT=30) -plt.plot(t, ca, 'r-', label='BAT = 30s') +plt.plot(t, ca, "r-", label="BAT = 30s") ca = osipi.aif_parker(t, BAT=60) -plt.plot(t, ca, 'g-', label='BAT = 60s') +plt.plot(t, ca, "g-", label="BAT = 60s") ca = osipi.aif_parker(t, BAT=90) -plt.plot(t, ca, 'm-', label='BAT = 90s') -plt.xlabel('Time (sec)') -plt.ylabel('Plasma concentration (mM)') +plt.plot(t, ca, "m-", label="BAT = 90s") +plt.xlabel("Time (sec)") +plt.ylabel("Plasma concentration (mM)") plt.legend() plt.show() 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 756e853..38d4e11 100755 --- a/docs/source/generated/examples/aif/plot_aif_parker.py.md5 +++ b/docs/source/generated/examples/aif/plot_aif_parker.py.md5 @@ -1 +1 @@ -2ad42a0f8a1b8aa8ec46f9a5f66e86e4 \ No newline at end of file +2ad42a0f8a1b8aa8ec46f9a5f66e86e4 diff --git a/docs/source/generated/examples/aif/plot_aif_parker.rst b/docs/source/generated/examples/aif/plot_aif_parker.rst index 48dee78..f862c5c 100755 --- a/docs/source/generated/examples/aif/plot_aif_parker.rst +++ b/docs/source/generated/examples/aif/plot_aif_parker.rst @@ -22,7 +22,7 @@ The Parker AIF - a play with variables ====================================== -Simulating a Parker AIF with different settings. +Simulating a Parker AIF with different settings. .. GENERATED FROM PYTHON SOURCE LINES 10-11 diff --git a/docs/source/generated/examples/aif/plot_dummy.ipynb b/docs/source/generated/examples/aif/plot_dummy.ipynb index 904ad56..7da2ef2 100755 --- a/docs/source/generated/examples/aif/plot_dummy.ipynb +++ b/docs/source/generated/examples/aif/plot_dummy.ipynb @@ -1,86 +1,113 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n# A dummy script\n\nDummy script to illustrate structure of examples folder \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 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, 'r-')\nplt.plot(t, 0*t, 'k-')\nplt.xlabel('Time (sec)')\nplt.ylabel('Plasma concentration (mM)')\nplt.show()" - ] - }, - { - "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" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "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()\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" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# A dummy script\n\nDummy script to illustrate structure of examples folder \n" + ] }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file + { + "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\n", + "import matplotlib.pyplot as plt\n", + "import 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.\n", + "t = np.arange(0, 6 * 60, 0.5)\n", + "\n", + "# Create an AIF with default settings\n", + "ca = osipi.aif_parker(t)\n", + "\n", + "# Plot the AIF over the full range\n", + "plt.plot(t, ca, \"r-\")\n", + "plt.plot(t, 0 * t, \"k-\")\n", + "plt.xlabel(\"Time (sec)\")\n", + "plt.ylabel(\"Plasma concentration (mM)\")\n", + "plt.show()" + ] + }, + { + "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" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "ca = osipi.aif_parker(t, BAT=0)\n", + "plt.plot(t, ca, \"b-\", label=\"BAT = 0s\")\n", + "ca = osipi.aif_parker(t, BAT=30)\n", + "plt.plot(t, ca, \"r-\", label=\"BAT = 30s\")\n", + "ca = osipi.aif_parker(t, BAT=60)\n", + "plt.plot(t, ca, \"g-\", label=\"BAT = 60s\")\n", + "ca = osipi.aif_parker(t, BAT=90)\n", + "plt.plot(t, ca, \"m-\", label=\"BAT = 90s\")\n", + "plt.xlabel(\"Time (sec)\")\n", + "plt.ylabel(\"Plasma concentration (mM)\")\n", + "plt.legend()\n", + "plt.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 +} diff --git a/docs/source/generated/examples/aif/plot_dummy.py b/docs/source/generated/examples/aif/plot_dummy.py index 1f4d326..13cbac2 100755 --- a/docs/source/generated/examples/aif/plot_dummy.py +++ b/docs/source/generated/examples/aif/plot_dummy.py @@ -3,44 +3,47 @@ A dummy script ============== -Dummy script to illustrate structure of examples folder +Dummy script to illustrate structure of examples folder """ +import matplotlib.pyplot as plt + # %% # Import necessary packages 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) +# 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, 'r-') -plt.plot(t, 0*t, 'k-') -plt.xlabel('Time (sec)') -plt.ylabel('Plasma concentration (mM)') +plt.plot(t, ca, "r-") +plt.plot(t, 0 * t, "k-") +plt.xlabel("Time (sec)") +plt.ylabel("Plasma concentration (mM)") 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 30s. 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') +plt.plot(t, ca, "b-", label="BAT = 0s") ca = osipi.aif_parker(t, BAT=30) -plt.plot(t, ca, 'r-', label='BAT = 30s') +plt.plot(t, ca, "r-", label="BAT = 30s") ca = osipi.aif_parker(t, BAT=60) -plt.plot(t, ca, 'g-', label='BAT = 60s') +plt.plot(t, ca, "g-", label="BAT = 60s") ca = osipi.aif_parker(t, BAT=90) -plt.plot(t, ca, 'm-', label='BAT = 90s') -plt.xlabel('Time (sec)') -plt.ylabel('Plasma concentration (mM)') +plt.plot(t, ca, "m-", label="BAT = 90s") +plt.xlabel("Time (sec)") +plt.ylabel("Plasma concentration (mM)") plt.legend() plt.show() diff --git a/docs/source/generated/examples/aif/plot_dummy.py.md5 b/docs/source/generated/examples/aif/plot_dummy.py.md5 index 39417a6..7c38a5e 100755 --- a/docs/source/generated/examples/aif/plot_dummy.py.md5 +++ b/docs/source/generated/examples/aif/plot_dummy.py.md5 @@ -1 +1 @@ -3393738ee0ad511916ee690ca9a231be \ No newline at end of file +3393738ee0ad511916ee690ca9a231be diff --git a/docs/source/generated/examples/aif/plot_dummy.rst b/docs/source/generated/examples/aif/plot_dummy.rst index de75cd2..5d43ebc 100755 --- a/docs/source/generated/examples/aif/plot_dummy.rst +++ b/docs/source/generated/examples/aif/plot_dummy.rst @@ -22,7 +22,7 @@ A dummy script ============== -Dummy script to illustrate structure of examples folder +Dummy script to illustrate structure of examples folder .. GENERATED FROM PYTHON SOURCE LINES 10-11 diff --git a/docs/source/generated/examples/tissue/index.rst b/docs/source/generated/examples/tissue/index.rst index b028c57..a1b6f62 100755 --- a/docs/source/generated/examples/tissue/index.rst +++ b/docs/source/generated/examples/tissue/index.rst @@ -59,4 +59,3 @@ Tissue concentrations /generated/examples/tissue/plot_tofts /generated/examples/tissue/plot_extended_tofts - diff --git a/docs/source/generated/examples/tissue/plot_extended_tofts.ipynb b/docs/source/generated/examples/tissue/plot_extended_tofts.ipynb index d42ad9c..52b04cd 100755 --- a/docs/source/generated/examples/tissue/plot_extended_tofts.ipynb +++ b/docs/source/generated/examples/tissue/plot_extended_tofts.ipynb @@ -1,104 +1,133 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n# The Extended Tofts model\n\nSimulating tissue concentrations from extended 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 plasma volumes of 0.05, 0.2 and 0.6\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "Ktrans = 0.2 # in units of 1/min\nve = 0.2 # volume fraction between 0 and 1\nvp = [0.05, 0.2, 0.6] # volume fraction between 0 and 1\nct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[0])\nplt.plot(t, ct, 'b-', label=f'vp = {vp[0]}')\nct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[1])\nplt.plot(t, ct, 'g-', label=f'vp = {vp[1]}')\nct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[2])\nplt.plot(t, ct, 'm-', label=f'vp = {vp[2]}')\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, Ktrans of 0.2 /min and vp of 0.05\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[0]) # Defaults to Convolution\nplt.plot(t, ct, 'b-', label='Convolution')\nct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[0], discretization_method='exp')\nplt.plot(t, ct, 'g-', label='Exponential Convolution')\nplt.title(f'Ktrans = {Ktrans} /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.11.2" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# The Extended Tofts model\n\nSimulating tissue concentrations from extended Tofts model with different settings.\n" + ] }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file + { + "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\n", + "import matplotlib.pyplot as plt\n", + "import 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.\n", + "t = np.arange(0, 6 * 60, 1)\n", + "\n", + "# Create an AIF with default settings\n", + "ca = osipi.aif_parker(t)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot the tissue concentrations for an extracellular volume fraction of 0.2 and 3 different plasma volumes of 0.05, 0.2 and 0.6\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "Ktrans = 0.2 # in units of 1/min\n", + "ve = 0.2 # volume fraction between 0 and 1\n", + "vp = [0.05, 0.2, 0.6] # volume fraction between 0 and 1\n", + "ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[0])\n", + "plt.plot(t, ct, \"b-\", label=f\"vp = {vp[0]}\")\n", + "ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[1])\n", + "plt.plot(t, ct, \"g-\", label=f\"vp = {vp[1]}\")\n", + "ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[2])\n", + "plt.plot(t, ct, \"m-\", label=f\"vp = {vp[2]}\")\n", + "plt.xlabel(\"Time (sec)\")\n", + "plt.ylabel(\"Tissue concentration (mM)\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Comparing different discretization methods for an extracellular volume fraction of 0.2, Ktrans of 0.2 /min and vp of 0.05\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[0]) # Defaults to Convolution\n", + "plt.plot(t, ct, \"b-\", label=\"Convolution\")\n", + "ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[0], discretization_method=\"exp\")\n", + "plt.plot(t, ct, \"g-\", label=\"Exponential Convolution\")\n", + "plt.title(f\"Ktrans = {Ktrans} /min\")\n", + "plt.xlabel(\"Time (sec)\")\n", + "plt.ylabel(\"Tissue concentration (mM)\")\n", + "plt.legend()\n", + "plt.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.11.2" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/docs/source/generated/examples/tissue/plot_extended_tofts.py b/docs/source/generated/examples/tissue/plot_extended_tofts.py index a4c60d3..f2df6fb 100755 --- a/docs/source/generated/examples/tissue/plot_extended_tofts.py +++ b/docs/source/generated/examples/tissue/plot_extended_tofts.py @@ -6,46 +6,50 @@ Simulating tissue concentrations from extended Tofts model with different settings. """ +import matplotlib.pyplot as plt + # %% # 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) +# 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 plasma volumes of 0.05, 0.2 and 0.6 -Ktrans = 0.2 # in units of 1/min +# Plot the tissue concentrations for an extracellular volume fraction +# of 0.2 and 3 different plasma volumes of 0.05, 0.2 and 0.6 +Ktrans = 0.2 # in units of 1/min ve = 0.2 # volume fraction between 0 and 1 -vp = [0.05, 0.2, 0.6] # volume fraction between 0 and 1 +vp = [0.05, 0.2, 0.6] # volume fraction between 0 and 1 ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[0]) -plt.plot(t, ct, 'b-', label=f'vp = {vp[0]}') +plt.plot(t, ct, "b-", label=f"vp = {vp[0]}") ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[1]) -plt.plot(t, ct, 'g-', label=f'vp = {vp[1]}') +plt.plot(t, ct, "g-", label=f"vp = {vp[1]}") ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[2]) -plt.plot(t, ct, 'm-', label=f'vp = {vp[2]}') -plt.xlabel('Time (sec)') -plt.ylabel('Tissue concentration (mM)') +plt.plot(t, ct, "m-", label=f"vp = {vp[2]}") +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, Ktrans of 0.2 /min and vp of 0.05 -ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[0]) # Defaults to Convolution -plt.plot(t, ct, 'b-', label='Convolution') -ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[0], discretization_method='exp') -plt.plot(t, ct, 'g-', label='Exponential Convolution') -plt.title(f'Ktrans = {Ktrans} /min') -plt.xlabel('Time (sec)') -plt.ylabel('Tissue concentration (mM)') +# Comparing different discretization methods for an extracellular +# volume fraction of 0.2, Ktrans of 0.2 /min and vp of 0.05 +ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[0]) # Defaults to Convolution +plt.plot(t, ct, "b-", label="Convolution") +ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp[0], discretization_method="exp") +plt.plot(t, ct, "g-", label="Exponential Convolution") +plt.title(f"Ktrans = {Ktrans} /min") +plt.xlabel("Time (sec)") +plt.ylabel("Tissue concentration (mM)") plt.legend() plt.show() diff --git a/docs/source/generated/examples/tissue/plot_extended_tofts.py.md5 b/docs/source/generated/examples/tissue/plot_extended_tofts.py.md5 index 0a6c85b..ec1a537 100755 --- a/docs/source/generated/examples/tissue/plot_extended_tofts.py.md5 +++ b/docs/source/generated/examples/tissue/plot_extended_tofts.py.md5 @@ -1 +1 @@ -fe125cfe0a7b9603e82149422673111f \ No newline at end of file +fe125cfe0a7b9603e82149422673111f diff --git a/docs/source/generated/examples/tissue/plot_extended_tofts.rst b/docs/source/generated/examples/tissue/plot_extended_tofts.rst index 1223560..3fc70b2 100755 --- a/docs/source/generated/examples/tissue/plot_extended_tofts.rst +++ b/docs/source/generated/examples/tissue/plot_extended_tofts.rst @@ -18,9 +18,9 @@ .. _sphx_glr_generated_examples_tissue_plot_extended_tofts.py: -==================== +======================== The Extended Tofts model -==================== +======================== Simulating tissue concentrations from extended Tofts model with different settings. diff --git a/docs/source/generated/examples/tissue/plot_tofts.ipynb b/docs/source/generated/examples/tissue/plot_tofts.ipynb index e937d8c..91cda66 100755 --- a/docs/source/generated/examples/tissue/plot_tofts.ipynb +++ b/docs/source/generated/examples/tissue/plot_tofts.ipynb @@ -1,104 +1,132 @@ { - "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" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# The Tofts model\n\nSimulating tissue concentrations from Tofts model with different settings.\n" + ] }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file + { + "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\n", + "import matplotlib.pyplot as plt\n", + "import 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.\n", + "t = np.arange(0, 6 * 60, 1)\n", + "\n", + "# Create an AIF with default settings\n", + "ca = 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\n", + "ve = 0.2 # volume fraction between 0 and 1\n", + "ct = osipi.tofts(t, ca, Ktrans=Ktrans[0], ve=ve)\n", + "plt.plot(t, ct, \"b-\", label=f\"Ktrans = {Ktrans[0]} /min\")\n", + "ct = osipi.tofts(t, ca, Ktrans[1], ve)\n", + "plt.plot(t, ct, \"g-\", label=f\"Ktrans = {Ktrans[1]} /min\")\n", + "ct = osipi.tofts(t, ca, Ktrans[2], ve)\n", + "plt.plot(t, ct, \"m-\", label=f\"Ktrans = {Ktrans[2]} /min\")\n", + "plt.xlabel(\"Time (sec)\")\n", + "plt.ylabel(\"Tissue concentration (mM)\")\n", + "plt.legend()\n", + "plt.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\n", + "plt.plot(t, ct, \"b-\", label=\"Convolution\")\n", + "ct = osipi.tofts(t, ca, Ktrans=Ktrans[1], ve=ve, discretization_method=\"exp\")\n", + "plt.plot(t, ct, \"g-\", label=\"Exponential Convolution\")\n", + "plt.title(f\"Ktrans = {Ktrans[1]} /min\")\n", + "plt.xlabel(\"Time (sec)\")\n", + "plt.ylabel(\"Tissue concentration (mM)\")\n", + "plt.legend()\n", + "plt.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 +} diff --git a/docs/source/generated/examples/tissue/plot_tofts.py b/docs/source/generated/examples/tissue/plot_tofts.py index dc22974..d623da9 100755 --- a/docs/source/generated/examples/tissue/plot_tofts.py +++ b/docs/source/generated/examples/tissue/plot_tofts.py @@ -6,45 +6,50 @@ Simulating tissue concentrations from Tofts model with different settings. """ +import matplotlib.pyplot as plt + # %% # 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) +# 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 +# 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') +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') +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.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)') +# 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() diff --git a/docs/source/generated/examples/tissue/plot_tofts.py.md5 b/docs/source/generated/examples/tissue/plot_tofts.py.md5 index 3a415ba..10b2daf 100755 --- a/docs/source/generated/examples/tissue/plot_tofts.py.md5 +++ b/docs/source/generated/examples/tissue/plot_tofts.py.md5 @@ -1 +1 @@ -562f87655efc9f9b885f617d06bc5dba \ No newline at end of file +562f87655efc9f9b885f617d06bc5dba diff --git a/docs/source/index.rst b/docs/source/index.rst index 9093c9f..e5fbb99 100755 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -7,7 +7,7 @@ osipi documentation ################### -``osipi`` is the authorative python package for perfusion MRI. +``osipi`` is the authorative python package for perfusion MRI. ``osipi`` is developed by the Open Source Initiative for Perfusion Imaging (OSIPI), an initiative of the perfusion study group of the International Society for Magnetic Resonance in Medicine (ISMRM). @@ -20,9 +20,9 @@ The ``osipi`` package structure and logic follows the lexicon defined by OSIPI, .. toctree:: :maxdepth: 2 - + user_guide/index reference/index generated/examples/index developers_guide/index - about/index \ No newline at end of file + about/index diff --git a/docs/source/reference/general.averaging.rst b/docs/source/reference/general.averaging.rst index 3282c25..95c59ee 100755 --- a/docs/source/reference/general.averaging.rst +++ b/docs/source/reference/general.averaging.rst @@ -5,12 +5,8 @@ Averaging .. 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.convolution.rst b/docs/source/reference/general.convolution.rst index 0c9d9b0..99e5a65 100755 --- a/docs/source/reference/general.convolution.rst +++ b/docs/source/reference/general.convolution.rst @@ -10,5 +10,3 @@ Convolution 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.deconvolution.rst b/docs/source/reference/general.deconvolution.rst index 82e9867..d03775a 100755 --- a/docs/source/reference/general.deconvolution.rst +++ b/docs/source/reference/general.deconvolution.rst @@ -5,10 +5,8 @@ Deconvolution .. 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.descriptive.rst b/docs/source/reference/general.descriptive.rst index 813ab6c..8d3e5ef 100755 --- a/docs/source/reference/general.descriptive.rst +++ b/docs/source/reference/general.descriptive.rst @@ -10,6 +10,3 @@ Descriptive 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.forward.rst b/docs/source/reference/general.forward.rst index d79b191..6c45ceb 100755 --- a/docs/source/reference/general.forward.rst +++ b/docs/source/reference/general.forward.rst @@ -5,11 +5,8 @@ Forward .. 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.optimization.rst b/docs/source/reference/general.optimization.rst index a53ff21..f10864f 100755 --- a/docs/source/reference/general.optimization.rst +++ b/docs/source/reference/general.optimization.rst @@ -5,12 +5,8 @@ Optimization .. 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 df0ee2f..eaf4841 100755 --- a/docs/source/reference/general.rst +++ b/docs/source/reference/general.rst @@ -13,6 +13,3 @@ General general.optimization general.segmentation general.uncertainty - - - diff --git a/docs/source/reference/general.segmentation.rst b/docs/source/reference/general.segmentation.rst index 2b5f880..3d73eb0 100755 --- a/docs/source/reference/general.segmentation.rst +++ b/docs/source/reference/general.segmentation.rst @@ -5,10 +5,8 @@ Segmentation .. 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.uncertainty.rst b/docs/source/reference/general.uncertainty.rst index a7e50e9..1d2b54b 100755 --- a/docs/source/reference/general.uncertainty.rst +++ b/docs/source/reference/general.uncertainty.rst @@ -5,12 +5,8 @@ Uncertainty .. 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/index.rst b/docs/source/reference/index.rst index 56fc89f..293b008 100755 --- a/docs/source/reference/index.rst +++ b/docs/source/reference/index.rst @@ -2,7 +2,7 @@ Reference ######### -This reference manual details all functions included in ``osipi``, describing what they are and what they do. Documentation of individual functions contains self-contained example code that demonstrates basic usage of the function. +This reference manual details all functions included in ``osipi``, describing what they are and what they do. Documentation of individual functions contains self-contained example code that demonstrates basic usage of the function. The ``osipi`` package currently only includes methods for the dynamic contrast (DC) approach to perfusion MRI (DC-MRI, a unifying term for the separate fields DCE-MRI and DSC-MRI). In particular arterial spin labelling (ASL) solutions are not currently included, but may be added in the future. @@ -20,4 +20,3 @@ This reference guide, like the ``osipi`` package itself, adheres closely to the models processes general - diff --git a/docs/source/reference/models.concentration.aif.rst b/docs/source/reference/models.concentration.aif.rst index c5c0634..495ab2a 100755 --- a/docs/source/reference/models.concentration.aif.rst +++ b/docs/source/reference/models.concentration.aif.rst @@ -14,6 +14,3 @@ Population AIFs aif_parker aif_georgiou aif_weinmann - - - diff --git a/docs/source/reference/models.concentration.rst b/docs/source/reference/models.concentration.rst index fc8480c..3f8fc54 100755 --- a/docs/source/reference/models.concentration.rst +++ b/docs/source/reference/models.concentration.rst @@ -7,5 +7,3 @@ Concentrations models.concentration.aif models.concentration.tissue - - diff --git a/docs/source/reference/models.descriptive.rst b/docs/source/reference/models.descriptive.rst index 72b8cd9..5f07373 100755 --- a/docs/source/reference/models.descriptive.rst +++ b/docs/source/reference/models.descriptive.rst @@ -5,13 +5,8 @@ Descriptive models .. 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/models.empproperties.rst b/docs/source/reference/models.empproperties.rst index eabb70e..62ad397 100755 --- a/docs/source/reference/models.empproperties.rst +++ b/docs/source/reference/models.empproperties.rst @@ -5,11 +5,8 @@ Models for electromagnetic tissue properties .. 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/models.identity.rst b/docs/source/reference/models.identity.rst index 13a37ba..a7dfd62 100755 --- a/docs/source/reference/models.identity.rst +++ b/docs/source/reference/models.identity.rst @@ -6,10 +6,8 @@ Perfusion identities .. 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/models.leakage.rst b/docs/source/reference/models.leakage.rst index 71e7bf9..868549f 100755 --- a/docs/source/reference/models.leakage.rst +++ b/docs/source/reference/models.leakage.rst @@ -5,11 +5,8 @@ Leakage correction models .. 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/models.rst b/docs/source/reference/models.rst index 5d667f6..bcfa068 100755 --- a/docs/source/reference/models.rst +++ b/docs/source/reference/models.rst @@ -11,6 +11,3 @@ Models models.identity models.leakage models.signal - - - diff --git a/docs/source/reference/models.signal.rst b/docs/source/reference/models.signal.rst index 64ceb80..8e1d402 100755 --- a/docs/source/reference/models.signal.rst +++ b/docs/source/reference/models.signal.rst @@ -5,11 +5,8 @@ Signal models .. 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/processes.R1.rst b/docs/source/reference/processes.R1.rst index 9b47b8e..d4d60dc 100755 --- a/docs/source/reference/processes.R1.rst +++ b/docs/source/reference/processes.R1.rst @@ -5,11 +5,8 @@ R1 measurement .. 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/processes.aif.rst b/docs/source/reference/processes.aif.rst index 8a705ca..ab72f30 100755 --- a/docs/source/reference/processes.aif.rst +++ b/docs/source/reference/processes.aif.rst @@ -5,11 +5,8 @@ AIF .. 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/processes.baseline.rst b/docs/source/reference/processes.baseline.rst index 153c59b..0ce0470 100755 --- a/docs/source/reference/processes.baseline.rst +++ b/docs/source/reference/processes.baseline.rst @@ -5,10 +5,8 @@ Baseline .. 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/processes.bat.rst b/docs/source/reference/processes.bat.rst index 4eb383c..758e670 100755 --- a/docs/source/reference/processes.bat.rst +++ b/docs/source/reference/processes.bat.rst @@ -5,11 +5,8 @@ Bolus Arrival Time .. 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/processes.calibration.rst b/docs/source/reference/processes.calibration.rst index 8a5fde1..3c49b98 100755 --- a/docs/source/reference/processes.calibration.rst +++ b/docs/source/reference/processes.calibration.rst @@ -5,11 +5,8 @@ Calibration .. 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/processes.concentration.rst b/docs/source/reference/processes.concentration.rst index 1b60160..3527ac2 100755 --- a/docs/source/reference/processes.concentration.rst +++ b/docs/source/reference/processes.concentration.rst @@ -5,11 +5,8 @@ Concentration .. 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/processes.leakage.rst b/docs/source/reference/processes.leakage.rst index b57e5f7..5fa2240 100755 --- a/docs/source/reference/processes.leakage.rst +++ b/docs/source/reference/processes.leakage.rst @@ -5,11 +5,8 @@ Leakage .. 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/processes.parameters.rst b/docs/source/reference/processes.parameters.rst index 0043b7d..231adcc 100755 --- a/docs/source/reference/processes.parameters.rst +++ b/docs/source/reference/processes.parameters.rst @@ -5,11 +5,8 @@ Parameters .. 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/processes.rst b/docs/source/reference/processes.rst index abe4046..a12e51e 100755 --- a/docs/source/reference/processes.rst +++ b/docs/source/reference/processes.rst @@ -13,6 +13,3 @@ Processes processes.leakage processes.parameters processes.R1 - - - diff --git a/docs/source/user_guide/forward/forward.rst b/docs/source/user_guide/forward/forward.rst index 894964e..efd4c8f 100755 --- a/docs/source/user_guide/forward/forward.rst +++ b/docs/source/user_guide/forward/forward.rst @@ -46,5 +46,3 @@ Adding measurement error ^^^^^^^^^^^^^^^^^^^^^^^^ Coming soon.. - - diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst index 095bd45..b3a9ebd 100755 --- a/docs/source/user_guide/index.rst +++ b/docs/source/user_guide/index.rst @@ -13,6 +13,3 @@ User guide installation forward/index inverse/index - - - \ No newline at end of file diff --git a/docs/source/user_guide/installation.rst b/docs/source/user_guide/installation.rst index 1ac0aa5..d6a9fb1 100755 --- a/docs/source/user_guide/installation.rst +++ b/docs/source/user_guide/installation.rst @@ -7,6 +7,3 @@ Installing osipi .. code-block:: console pip install osipi - - - \ No newline at end of file diff --git a/docs/source/user_guide/inverse/index.rst b/docs/source/user_guide/inverse/index.rst index e2f18f9..8a48688 100755 --- a/docs/source/user_guide/inverse/index.rst +++ b/docs/source/user_guide/inverse/index.rst @@ -6,4 +6,3 @@ Fitting data :maxdepth: 2 inverse - \ No newline at end of file diff --git a/docs/source/user_guide/inverse/inverse.rst b/docs/source/user_guide/inverse/inverse.rst index 884cbf0..1f9476e 100755 --- a/docs/source/user_guide/inverse/inverse.rst +++ b/docs/source/user_guide/inverse/inverse.rst @@ -1,8 +1,8 @@ .. 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. @@ -26,4 +26,4 @@ Coming soon.. All in one go: signal to tissue parameters ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Coming soon.. \ No newline at end of file +Coming soon.. diff --git a/manage.py b/manage.py index 70ea14b..d7f40f9 100755 --- a/manage.py +++ b/manage.py @@ -1,11 +1,13 @@ +import logging import os import sys import venv -import logging # logging root configuration -logging.basicConfig(level=logging.INFO, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", +) def set_debug_mode(debug_mode): @@ -16,53 +18,54 @@ def set_debug_mode(debug_mode): def distribute(): - """Create new version on PyPI - - IMPORTANT! First increment your version number in pyproject.toml: - - Increment the MAJOR version when you make incompatible API changes. - - Increment the MINOR version when you add functionality in a backwards compatible manner. - - Increment the PATCH version when you make backwards compatible bug fixes. - - You need: PyPI username and password. - You need to type in the PyPI password rather than copy-pasting. + """Create new version on PyPI. + + IMPORTANT! First increment your version number in pyproject.toml: - Increment the MAJOR version + when you make incompatible API changes. - Increment the MINOR version when you add functionality + in a backwards compatible manner. - Increment the PATCH version when you make backwards + compatible bug fixes. You need: PyPI username and password. You need to type in the PyPI + password rather than copy-pasting. + """ create_venv() install() - os.system(activate() + ' && ' + 'pip install --upgrade build') - os.system(activate() + ' && ' + 'python -m build') - os.system(activate() + ' && ' + 'pip install --upgrade twine') - os.system(activate() + ' && ' + 'twine upload dist/*') + os.system(activate() + " && " + "pip install --upgrade build") + os.system(activate() + " && " + "python -m build") + os.system(activate() + " && " + "pip install --upgrade twine") + os.system(activate() + " && " + "twine upload dist/*") + def create_venv(): venv_dir = os.path.join(os.getcwd(), ".venv") if not os.path.exists(venv_dir): logging.info("Creating virtual environment...") - os.system('py -3 -m venv .venv') + os.system("py -3 -m venv .venv") else: logging.info("Virtual environment already exists.") + def activate(): - """Active virtual environment""" + """Active virtual environment.""" venv_dir = os.path.join(os.getcwd(), ".venv") os.makedirs(venv_dir, exist_ok=True) venv.create(venv_dir, with_pip=True) - windows = (sys.platform == "win32") or (sys.platform == "win64") or (os.name == 'nt') + windows = (sys.platform == "win32") or (sys.platform == "win64") or (os.name == "nt") if windows: return os.path.join(venv_dir, "Scripts", "activate") - else: # MacOS and Linux + else: # MacOS and Linux return '. "' + os.path.join(venv_dir, "bin", "activate") + def install(): - """Install requirements to a virtual environment""" + """Install requirements to a virtual environment.""" logging.info("Installing requirements...") - os.system(activate() + ' && ' + 'py -m pip install -r requirements.txt') - + os.system(activate() + " && " + "py -m pip install -r requirements.txt") -if __name__ == '__main__': - #install() +if __name__ == "__main__": + # install() set_debug_mode(debug_mode=True) - distribute() \ No newline at end of file + distribute() diff --git a/pyproject.toml b/pyproject.toml index 6f31a1f..0004659 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ [project] name = "osipi" version = "0.1.2" -dependencies = [ +dependencies = [ "numpy", "scipy" ] @@ -14,7 +14,7 @@ dependencies = [ description = "The authorative python package for perfusion MRI" readme = "README.md" authors = [ - { name = "Luis Torres", email = "luistorres@flywheel.io" }, + { name = "Luis Torres", email = "luistorres@flywheel.io" }, { name = "Steven Sourbron", email = "s.sourbron@sheffield.ac.uk" }, ] license = { file = "LICENSE" } @@ -53,7 +53,7 @@ tests = [ docs = [ "sphinx", "pydata-sphinx-theme", - "myst-parser", + "myst-parser", "sphinx-copybutton", "sphinx-design", "sphinx-remove-toctrees", @@ -68,12 +68,38 @@ docs = [ "sphinx-gallery", ] +[tool.ruff] +# Exclude a variety of commonly ignored directories. +exclude = [ + ".direnv", ".eggs", ".git", ".git-rewrite","__init__.py", + ".mypy_cache", ".nox", ".pants.d", ".pyenv", ".pytest_cache", ".pytype", + ".ruff_cache", ".svn", ".venv", ".vscode", "__pypackages__", "_build", + "buck-out", "build", "dist", "site-packages", "venv", +] + +line-length = 100 + + +[tool.ruff.lint] +# Enable Pyflakes (`F`), a subset of the pycodestyle (`E`) codes, and isort (`I`). +select = ["E4", "E7", "E9", "F", "I"] +ignore = [] +fixable = ["ALL"] +unfixable = [] +[tool.ruff.format] +# Like Black, use double quotes for strings. +quote-style = "double" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto" +docstring-code-format = false +docstring-code-line-length = "dynamic" [tool.poetry] name = "osipi" diff --git a/requirements.txt b/requirements.txt index 9991eaa..96602a9 100755 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ scipy matplotlib logging poetry +pre-commit diff --git a/setup.py b/setup.py index 7f4c001..dc07ceb 100755 --- a/setup.py +++ b/setup.py @@ -5,4 +5,4 @@ from setuptools import setup -setup() \ No newline at end of file +setup() diff --git a/src/osipi/__init__.py b/src/osipi/__init__.py index ff2f20b..2e15879 100755 --- a/src/osipi/__init__.py +++ b/src/osipi/__init__.py @@ -1,11 +1,2 @@ - -from ._aif import ( - aif_parker, - aif_georgiou, - aif_weinmann, -) - -from ._tissue import ( - tofts, - extended_tofts -) \ No newline at end of file +from ._aif import aif_georgiou, aif_parker, aif_weinmann +from ._tissue import extended_tofts, tofts diff --git a/src/osipi/_aif.py b/src/osipi/_aif.py index ec98f8c..9ad270a 100755 --- a/src/osipi/_aif.py +++ b/src/osipi/_aif.py @@ -1,13 +1,15 @@ import numpy as np -def aif_parker(t:np.ndarray, BAT:float=0.0, Hct:float=0.0)->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 0. [OSIPI code Q.BA1.001] - Hct (float, optional): Hematocrit. Defaults to 0.0. [OSIPI code Q.PH1.012] + 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] Returns: np.ndarray: Concentrations in mM for each time point in t. @@ -17,14 +19,15 @@ def aif_parker(t:np.ndarray, BAT:float=0.0, Hct:float=0.0)->np.ndarray: `aif_weinmann` References: - - Lexicon url: https://osipi.github.io/OSIPI_CAPLEX/perfusionModels/#arterial-input-function-models + - Lexicon url: + https://osipi.github.io/OSIPI_CAPLEX/perfusionModels/#arterial-input-function-models - Lexicon code: M.IC2.001 - OSIPI name: Parker AIF model - Adapted from contribution by: MB_QBI_UoManchester_UK Example: - - 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. + 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: @@ -37,45 +40,46 @@ def aif_parker(t:np.ndarray, BAT:float=0.0, Hct:float=0.0)->np.ndarray: >>> 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 + t_min = t / 60 + bat_min = BAT / 60 t_offset = t_min - bat_min - #A1/(SD1*sqrt(2*PI)) * exp(-(t_offset-m1)^2/(2*var1)) - #A1 = 0.833, SD1 = 0.055, m1 = 0.171 + # A1/(SD1*sqrt(2*PI)) * exp(-(t_offset-m1)^2/(2*var1)) + # A1 = 0.833, SD1 = 0.055, m1 = 0.171 gaussian1 = 5.73258 * np.exp( - -1.0 * - (t_offset - 0.17046) * (t_offset - 0.17046) / - (2.0 * 0.0563 * 0.0563) ) - - #A2/(SD2*sqrt(2*PI)) * exp(-(t_offset-m2)^2/(2*var2)) - #A2 = 0.336, SD2 = 0.134, m2 = 0.364 + -1.0 * (t_offset - 0.17046) * (t_offset - 0.17046) / (2.0 * 0.0563 * 0.0563) + ) + + # A2/(SD2*sqrt(2*PI)) * exp(-(t_offset-m2)^2/(2*var2)) + # A2 = 0.336, SD2 = 0.134, m2 = 0.364 gaussian2 = 0.997356 * np.exp( - -1.0 * - (t_offset - 0.365) * (t_offset - 0.365) / - (2.0 * 0.132 * 0.132)) + -1.0 * (t_offset - 0.365) * (t_offset - 0.365) / (2.0 * 0.132 * 0.132) + ) # alpha*exp(-beta*t_offset) / (1+exp(-s(t_offset-tau))) # 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 = ((gaussian1 + gaussian2 + sigmoid)) / \ - (1.0 - Hct) - + pop_aif = (gaussian1 + gaussian2 + sigmoid) / (1.0 - Hct) + return pop_aif -def aif_georgiou(t:np.ndarray, BAT:float=0.0)->np.ndarray: +def aif_georgiou(t: np.ndarray, BAT: float = 0.0) -> np.ndarray: """AIF model as defined by Georgiou et al. 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! + 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] - BAT (float, optional): Time in seconds before the bolus arrives. Defaults to 0sec. [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. @@ -85,14 +89,16 @@ def aif_georgiou(t:np.ndarray, BAT:float=0.0)->np.ndarray: `aif_weinmann` References: - - Lexicon url: https://osipi.github.io/OSIPI_CAPLEX/perfusionModels/#arterial-input-function-models + - Lexicon url: + https://osipi.github.io/OSIPI_CAPLEX/perfusionModels/#arterial-input-function-models - Lexicon code: M.IC2.002 - OSIPI name: Georgiou AIF model - Adapted from contribution by: TBC Example: - Create an array of time points covering 6min in steps of 1sec, calculate the Georgiou AIF at these time points and plot the results. + Create an array of time points covering 6min in steps of 1sec, + calculate the Georgiou AIF at these time points and plot the results. Import packages: @@ -108,20 +114,26 @@ def aif_georgiou(t:np.ndarray, BAT:float=0.0)->np.ndarray: """ - msg = 'This function is not yet implemented \n' - msg += 'If you implement it yourself, please consider submitting it as an OSIPI code contribution' + 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) -def aif_weinmann(t:np.ndarray, BAT:float=0.0)->np.ndarray: +def aif_weinmann(t: np.ndarray, BAT: float = 0.0) -> np.ndarray: """AIF model as defined by Weinmann et al. 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! + 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] - BAT (float, optional): Time in seconds before the bolus arrives. Defaults to 0sec. [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. @@ -131,14 +143,16 @@ def aif_weinmann(t:np.ndarray, BAT:float=0.0)->np.ndarray: `aif_georgiou` References: - - Lexicon url: https://osipi.github.io/OSIPI_CAPLEX/perfusionModels/#arterial-input-function-models + - Lexicon url: + https://osipi.github.io/OSIPI_CAPLEX/perfusionModels/#arterial-input-function-models - Lexicon code: M.IC2.003 - OSIPI name: Weinmann AIF model - Adapted from contribution by: TBC Example: - Create an array of time points covering 6min in steps of 1sec, calculate the Weinmann AIF at these time points and plot the results. + Create an array of time points covering 6min in steps of 1sec, + calculate the Weinmann AIF at these time points and plot the results. Import packages: @@ -150,11 +164,12 @@ def aif_weinmann(t:np.ndarray, BAT:float=0.0)->np.ndarray: >>> t = np.arange(0, 6*60, 0.1) >>> ca = osipi.aif_weinmann(t) >>> plt.plot(t,ca) - """ - 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) - - + """ + 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/src/osipi/_convolution.py b/src/osipi/_convolution.py index c6d925e..5b07cdd 100755 --- a/src/osipi/_convolution.py +++ b/src/osipi/_convolution.py @@ -2,15 +2,12 @@ def exp_conv(T: float, t: np.ndarray, a: np.ndarray) -> np.ndarray: - """Exponential convolution operation of (1/T)exp(-t/T) with a + """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 + 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 - Returns: - np.ndarray: convolved array """ if T == 0: return a @@ -18,18 +15,17 @@ def exp_conv(T: float, t: np.ndarray, a: np.ndarray) -> np.ndarray: 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 + 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 + 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 index a4643a1..3e9e13d 100755 --- a/src/osipi/_tissue.py +++ b/src/osipi/_tissue.py @@ -1,19 +1,34 @@ +import warnings + 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: +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] + 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] @@ -28,14 +43,16 @@ def tofts(t: np.ndarray, ca: np.ndarray, Ktrans: float, ve: float, Ta: float = 3 `extended_tofts` References: - - Lexicon url: https://osipi.github.io/OSIPI_CAPLEX/perfusionModels/#indicator-kinetic-models + - 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 + 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: @@ -55,21 +72,31 @@ def tofts(t: np.ndarray, ca: np.ndarray, Ktrans: float, ve: float, Ta: float = 3 >>> 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) + warnings.warn( + ("Non-uniform time spacing detected. Time array may be" " resampled."), + stacklevel=2, + ) if Ktrans <= 0 or ve <= 0: ct = 0 * ca - + else: # Convert units - Ktrans = Ktrans/60 # from 1/min to 1/sec + Ktrans = Ktrans / 60 # from 1/min to 1/sec - if discretization_method == 'exp': # Use exponential convolution + 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) + f = interp1d( + t, + ca, + kind="linear", + bounds_error=False, + fill_value=0, + ) ca = (t > Ta) * f(t - Ta) Tc = ve / Ktrans @@ -82,7 +109,13 @@ def tofts(t: np.ndarray, ca: np.ndarray, Ktrans: float, ve: float, Ta: float = 3 # 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) + 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 @@ -90,41 +123,78 @@ def tofts(t: np.ndarray, ca: np.ndarray, Ktrans: float, ve: float, Ta: float = 3 # 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] + # 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) + 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] + # 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_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, vp: float, Ta: float = 30.0, - discretization_method: str = "conv") -> np.ndarray: +def extended_tofts( + t: np.ndarray, + ca: np.ndarray, + Ktrans: float, + ve: float, + vp: float, + Ta: float = 30.0, + discretization_method: str = "conv", +) -> np.ndarray: """Extended tofts model as defined by Tofts (1997) 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]] - vp (float): Relative volyme fraction of the plasma compartment (p). [OSIPI code Q.PH1.001.[p]] - 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 + 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]] + vp (float): + Relative volyme fraction of the plasma compartment (p). [OSIPI code Q.PH1.001.[p]] + 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] @@ -138,14 +208,16 @@ def extended_tofts(t: np.ndarray, ca: np.ndarray, Ktrans: float, ve: float, vp: `tofts` References: - - Lexicon url: https://osipi.github.io/OSIPI_CAPLEX/perfusionModels/#indicator-kinetic-models + - 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: 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 + 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 Extended Tofts model and plot the results. Import packages: @@ -165,29 +237,39 @@ def extended_tofts(t: np.ndarray, ca: np.ndarray, Ktrans: float, ve: float, vp: >>> vp = 0.3 # takes values from 0 to 1 >>> ct = osipi.extended_tofts(t, ca, Ktrans, ve, vp) >>> 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) + warnings.warn( + ("Non-uniform time spacing detected. Time array may be" " resampled."), + stacklevel=2, + ) if Ktrans <= 0 or ve <= 0: ct = vp * ca - + else: # Convert units - Ktrans = Ktrans/60 # from 1/min to 1/sec + Ktrans = Ktrans / 60 # from 1/min to 1/sec - if discretization_method == 'exp': # Use exponential convolution + 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) + f = interp1d( + t, + ca, + kind="linear", + bounds_error=False, + fill_value=0, + ) ca = (t > Ta) * f(t - Ta) Tc = ve / Ktrans - # expconv calculates convolution of ca and (1/Tc)exp(-t/Tc), add vp*ca term for extended model + # expconv calculates convolution of ca and + # (1/Tc)exp(-t/Tc), add vp*ca term for extended model ct = (vp * ca) + ve * exp_conv(Tc, t, ca) - else: # Use convolution by default # Calculate the impulse response function kep = Ktrans / ve @@ -195,7 +277,13 @@ def extended_tofts(t: np.ndarray, ca: np.ndarray, Ktrans: float, ve: float, vp: # 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) + 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 @@ -203,25 +291,46 @@ def extended_tofts(t: np.ndarray, ca: np.ndarray, Ktrans: float, ve: float, vp: # Convolve impulse response with AIF convolution = np.convolve(ca, imp) - # Discard unwanted points, make sure time spacing is correct and add vp*ca term for extended model - ct = convolution[0:len(t)] * t[1] + (vp * ca) + # Discard unwanted points, make sure time spacing is + # correct and add vp*ca term for extended model + ct = convolution[0 : len(t)] * t[1] + (vp * ca) 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) + 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, make sure time spacing is correct and add vp*ca term for extended model - ct_resampled = convolution[0:len(t_resampled)] * t_resampled[1] + (vp * ca_resampled) + # Discard unwanted points, make sure time spacing is + # correct and add vp*ca term for extended model + ct_resampled = convolution[0 : len(t_resampled)] * t_resampled[1] + ( + vp * ca_resampled + ) # Restore time grid spacing - ct_func = interp1d(t_resampled, ct_resampled, kind='quadratic', bounds_error=False, fill_value=0) + ct_func = interp1d( + t_resampled, + ct_resampled, + kind="quadratic", + bounds_error=False, + fill_value=0, + ) ct = ct_func(t) return ct - diff --git a/tests/test_aif.py b/tests/test_aif.py index ecafab7..af44420 100755 --- a/tests/test_aif.py +++ b/tests/test_aif.py @@ -1,40 +1,40 @@ import numpy as np import osipi -def test_aif_parker(): - t = np.arange(0, 6*60, 1) +def test_aif_parker(): + t = np.arange(0, 6 * 60, 1) ca = osipi.aif_parker(t) # Test that this generates values in the right range assert np.round(np.amax(ca)) == 6 -def test_aif_georgiou(): +def test_aif_georgiou(): # Not implemented yet so need to raise an error - t = np.arange(0, 6*60, 1) + t = np.arange(0, 6 * 60, 1) try: - ca = osipi.aif_georgiou(t) - except: + osipi.aif_georgiou(t) + except Exception: assert True else: assert False -def test_aif_weinmann(): +def test_aif_weinmann(): # Not implemented yet so need to raise an error - t = np.arange(0, 6*60, 1) + t = np.arange(0, 6 * 60, 1) try: - ca = osipi.aif_weinmann(t) - except: + osipi.aif_weinmann(t) + except BaseException: assert True else: assert False -if __name__ == "__main__": +if __name__ == "__main__": test_aif_parker() test_aif_georgiou() test_aif_weinmann() - print('All AIF tests passed!!') \ No newline at end of file + print("All AIF tests passed!!") diff --git a/tests/test_tissue.py b/tests/test_tissue.py index 06caa64..304cba9 100755 --- a/tests/test_tissue.py +++ b/tests/test_tissue.py @@ -1,42 +1,52 @@ +import math + 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 + + """ - # 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 + # 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 + # 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 + 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 + # 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') + 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 + # 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) + 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) @@ -44,70 +54,78 @@ def test_tissue_tofts(): 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') + 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') + 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(): -# 1. Basic operation of the function - test that the peak tissue concentration is less than the peak AIF +def test_tissue_extended_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.extended_tofts(t, ca, Ktrans=0.6, ve=0.2, vp=0.3) 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 + # 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.extended_tofts(t, ca, Ktrans=0.6, ve=0.2, vp=0.3) 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 + # 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.extended_tofts(t, ca, Ktrans=0.6, ve=0.2, vp=0.3, Ta=60.0) - assert (np.min(np.where(ct>0.0)) - np.min(np.where(ca>0.0)) - 1)*1 == 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 + # 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.extended_tofts(t, ca, Ktrans=0.6, ve=0.2, vp=0.3) - ct_exp = osipi.extended_tofts(t, ca, Ktrans=0.6, ve=0.2, vp=0.3, discretization_method='exp') + ct_exp = osipi.extended_tofts(t, ca, Ktrans=0.6, ve=0.2, vp=0.3, 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 plus the plasma volume + # 5. Test that the ratio of the area under the ct and ca curves is + # approximately the extracellular volume plus the plasma volume + t = np.arange(0, 6 * 60, 1) ca = osipi.aif_parker(t) ct_conv = osipi.extended_tofts(t, ca, Ktrans=0.6, ve=0.2, vp=0.3) - ct_exp = osipi.extended_tofts(t, ca, Ktrans=0.6, ve=0.2, vp=0.3, discretization_method='exp') - assert math.isclose(np.trapz(ct_conv, t)/np.trapz(ca, t), 0.2+0.3, abs_tol=1e-1) - assert math.isclose(np.trapz(ct_exp, t)/np.trapz(ca, t), 0.2+0.3, abs_tol=1e-1) + ct_exp = osipi.extended_tofts(t, ca, Ktrans=0.6, ve=0.2, vp=0.3, discretization_method="exp") + assert math.isclose( + np.trapz(ct_conv, t) / np.trapz(ca, t), + 0.2 + 0.3, + abs_tol=1e-1, + ) + assert math.isclose(np.trapz(ct_exp, t) / np.trapz(ca, t), 0.2 + 0.3, abs_tol=1e-1) # 6. Test specific use cases t = np.arange(0, 6 * 60, 1) ca = osipi.aif_parker(t) ct_conv = osipi.extended_tofts(t, ca, Ktrans=0, ve=0.2, vp=0.3) - assert np.allclose(ct_conv,ca*0.3,rtol=1e-4, atol=1e-3) + assert np.allclose(ct_conv, ca * 0.3, rtol=1e-4, atol=1e-3) - ct_exp = osipi.extended_tofts(t, ca, Ktrans=0, ve=0.2, vp=0.3, discretization_method='exp') - assert np.allclose(ct_conv,ca*0.3,rtol=1e-4, atol=1e-3) + ct_exp = osipi.extended_tofts(t, ca, Ktrans=0, ve=0.2, vp=0.3, discretization_method="exp") + assert np.allclose(ct_conv, ca * 0.3, rtol=1e-4, atol=1e-3) ct_conv = osipi.extended_tofts(t, ca, Ktrans=0.6, ve=0, vp=0.3) - assert np.allclose(ct_conv,ca*0.3,rtol=1e-4, atol=1e-3) + assert np.allclose(ct_conv, ca * 0.3, rtol=1e-4, atol=1e-3) - ct_exp = osipi.extended_tofts(t, ca, Ktrans=0.6, ve=0, vp=0.3, discretization_method='exp') - assert np.allclose(ct_conv,ca*0.3,rtol=1e-4, atol=1e-3) + ct_exp = osipi.extended_tofts(t, ca, Ktrans=0.6, ve=0, vp=0.3, discretization_method="exp") + assert np.allclose(ct_conv, ca * 0.3, rtol=1e-4, atol=1e-3) if __name__ == "__main__": - test_tissue_tofts() test_tissue_extended_tofts() - print('All tissue concentration model tests passed!!') - + print("All tissue concentration model tests passed!!")