From 9af85ce754d63cd3eae7bfe4c021e5ad0b70436c Mon Sep 17 00:00:00 2001 From: Mohamed Nasser Date: Wed, 5 Jun 2024 15:14:07 +0300 Subject: [PATCH] added flake with 100 size --- .flake8 | 2 + .gitignore | 1 + .pre-commit-config.yaml | 23 ++++-- docs/examples/aif/plot_aif_parker.py | 15 ++-- docs/examples/aif/plot_dummy.py | 6 +- docs/examples/tissue/plot_extended_tofts.py | 9 ++- docs/examples/tissue/plot_tofts.py | 10 ++- docs/source/conf.py | 44 +++++++---- .../generated/examples/aif/plot_aif_parker.py | 15 ++-- .../generated/examples/aif/plot_dummy.py | 6 +- .../examples/tissue/plot_extended_tofts.py | 9 ++- .../generated/examples/tissue/plot_tofts.py | 10 ++- manage.py | 17 ++--- src/osipi/_aif.py | 42 ++++++---- src/osipi/_convolution.py | 17 ++--- src/osipi/_tissue.py | 76 +++++++++++++------ tests/test_aif.py | 4 +- tests/test_tissue.py | 37 +++++---- 18 files changed, 222 insertions(+), 121 deletions(-) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..51b50a0 --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 100 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 874816c..022bc54 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea/ /.venv +dist diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a0fb973..34f4439 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,14 +6,27 @@ repos: - id: end-of-file-fixer - id: check-yaml -# - repo: https://github.com/pycqa/flake8 -# rev: 6.0.0 -# hooks: -# - id: flake8 -# args: [ '--max-line-length=70' ] + - repo: https://github.com/pycqa/flake8 + rev: 6.0.0 + hooks: + - id: flake8 + args: [ '--max-line-length=100' ] + - repo: https://github.com/psf/black rev: 23.3.0 hooks: - id: black args: [--line-length=70] + + - repo: https://github.com/PyCQA/docformatter + rev: v1.4 + hooks: + - id: docformatter + args: [--in-place, --recursive, --blank, --force-wrap, --wrap-summaries=100,--wrap-descriptions=100] + + - repo: https://github.com/pre-commit/mirrors-autopep8 + rev: v1.5.7 + hooks: + - id: autopep8 + args: [--in-place, --aggressive, --aggressive, --recursive, --max-line-length=100] diff --git a/docs/examples/aif/plot_aif_parker.py b/docs/examples/aif/plot_aif_parker.py index 8b2e7d2..df58db5 100755 --- a/docs/examples/aif/plot_aif_parker.py +++ b/docs/examples/aif/plot_aif_parker.py @@ -1,9 +1,8 @@ -""" -====================================== -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 @@ -17,7 +16,8 @@ # %% # 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. +# 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 @@ -31,7 +31,8 @@ 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") diff --git a/docs/examples/aif/plot_dummy.py b/docs/examples/aif/plot_dummy.py index cb89ded..9dd4f1c 100755 --- a/docs/examples/aif/plot_dummy.py +++ b/docs/examples/aif/plot_dummy.py @@ -17,7 +17,8 @@ # %% # 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. +# 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 @@ -31,7 +32,8 @@ 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") diff --git a/docs/examples/tissue/plot_extended_tofts.py b/docs/examples/tissue/plot_extended_tofts.py index 87f135c..5b8811f 100755 --- a/docs/examples/tissue/plot_extended_tofts.py +++ b/docs/examples/tissue/plot_extended_tofts.py @@ -17,14 +17,16 @@ # %% # 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. +# 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 +# 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 @@ -40,7 +42,8 @@ plt.show() # %% -# Comparing different discretization methods for an extracellular volume fraction of 0.2, Ktrans of 0.2 /min and vp of 0.05 +# 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 diff --git a/docs/examples/tissue/plot_tofts.py b/docs/examples/tissue/plot_tofts.py index 7eb8782..1906d39 100755 --- a/docs/examples/tissue/plot_tofts.py +++ b/docs/examples/tissue/plot_tofts.py @@ -17,14 +17,17 @@ # %% # 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. +# 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) @@ -39,7 +42,8 @@ plt.show() # %% -# Comparing different discretization methods for an extracellular volume fraction of 0.2 and Ktrans of 0.2 /min +# 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 diff --git a/docs/source/conf.py b/docs/source/conf.py index df79d9d..ee5f9f1 100755 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -5,7 +5,7 @@ import os -# -- Project information ----------------------------------------------------- +# -- Project information --------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information import sys @@ -14,24 +14,28 @@ author = "OSIPI" release = "0.1.1" -# -- Path setup -------------------------------------------------------------- +# -- 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 +# documentation root, use os.path.abspath to make it absolute, like +# shown here sys.path.insert(0, os.path.abspath("../../src")) -# -- General configuration --------------------------------------------------- +# -- 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 + # 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 + # generate links to the documentation of objects in external + # projects "sphinx.ext.intersphinx", "autodocsumm", "myst_parser", # parser for markdown language @@ -61,10 +65,13 @@ "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 +# 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 +# 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 @@ -72,8 +79,9 @@ # 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), @@ -86,16 +94,19 @@ 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 +# 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 = { @@ -111,7 +122,10 @@ "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" +# 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. diff --git a/docs/source/generated/examples/aif/plot_aif_parker.py b/docs/source/generated/examples/aif/plot_aif_parker.py index 8b2e7d2..df58db5 100755 --- a/docs/source/generated/examples/aif/plot_aif_parker.py +++ b/docs/source/generated/examples/aif/plot_aif_parker.py @@ -1,9 +1,8 @@ -""" -====================================== -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 @@ -17,7 +16,8 @@ # %% # 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. +# 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 @@ -31,7 +31,8 @@ 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") diff --git a/docs/source/generated/examples/aif/plot_dummy.py b/docs/source/generated/examples/aif/plot_dummy.py index cb89ded..9dd4f1c 100755 --- a/docs/source/generated/examples/aif/plot_dummy.py +++ b/docs/source/generated/examples/aif/plot_dummy.py @@ -17,7 +17,8 @@ # %% # 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. +# 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 @@ -31,7 +32,8 @@ 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") diff --git a/docs/source/generated/examples/tissue/plot_extended_tofts.py b/docs/source/generated/examples/tissue/plot_extended_tofts.py index 87f135c..5b8811f 100755 --- a/docs/source/generated/examples/tissue/plot_extended_tofts.py +++ b/docs/source/generated/examples/tissue/plot_extended_tofts.py @@ -17,14 +17,16 @@ # %% # 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. +# 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 +# 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 @@ -40,7 +42,8 @@ plt.show() # %% -# Comparing different discretization methods for an extracellular volume fraction of 0.2, Ktrans of 0.2 /min and vp of 0.05 +# 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 diff --git a/docs/source/generated/examples/tissue/plot_tofts.py b/docs/source/generated/examples/tissue/plot_tofts.py index 7eb8782..1906d39 100755 --- a/docs/source/generated/examples/tissue/plot_tofts.py +++ b/docs/source/generated/examples/tissue/plot_tofts.py @@ -17,14 +17,17 @@ # %% # 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. +# 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) @@ -39,7 +42,8 @@ plt.show() # %% -# Comparing different discretization methods for an extracellular volume fraction of 0.2 and Ktrans of 0.2 /min +# 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 diff --git a/manage.py b/manage.py index 8d6390a..5e3a2ed 100755 --- a/manage.py +++ b/manage.py @@ -18,15 +18,14 @@ def set_debug_mode(debug_mode): def distribute(): - """Create new version on PyPI + """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. + 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. - You need: PyPI username and password. - You need to type in the PyPI password rather than copy-pasting. """ create_venv() install() @@ -47,7 +46,7 @@ def create_venv(): def activate(): - """Active virtual environment""" + """Active virtual environment.""" venv_dir = os.path.join(os.getcwd(), ".venv") os.makedirs(venv_dir, exist_ok=True) @@ -64,7 +63,7 @@ def activate(): def install(): - """Install requirements to a virtual environment""" + """Install requirements to a virtual environment.""" logging.info("Installing requirements...") os.system( diff --git a/src/osipi/_aif.py b/src/osipi/_aif.py index 24fdfc9..cfe9135 100755 --- a/src/osipi/_aif.py +++ b/src/osipi/_aif.py @@ -8,8 +8,10 @@ def aif_parker( 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. @@ -19,14 +21,15 @@ def aif_parker( `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: @@ -39,6 +42,7 @@ def aif_parker( >>> 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 @@ -80,11 +84,14 @@ 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. @@ -94,14 +101,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: @@ -129,11 +138,14 @@ 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. @@ -143,14 +155,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: @@ -162,7 +176,9 @@ 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" diff --git a/src/osipi/_convolution.py b/src/osipi/_convolution.py index d072d32..6a6c0f0 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,14 +15,14 @@ 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] diff --git a/src/osipi/_tissue.py b/src/osipi/_tissue.py index c5f5d5e..a6100aa 100755 --- a/src/osipi/_tissue.py +++ b/src/osipi/_tissue.py @@ -18,10 +18,17 @@ def tofts( 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] @@ -36,14 +43,16 @@ def tofts( `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: @@ -63,6 +72,7 @@ def tofts( >>> 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( @@ -118,8 +128,9 @@ def tofts( # 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)) @@ -145,9 +156,10 @@ def tofts( # Convolve impulse response with AIF convolution = np.convolve(ca_resampled, imp_resampled) - # Discard unwanted points and make sure time spacing is correct + # Discard unwanted points and make sure time spacing + # is correct ct_resampled = ( - convolution[0 : len(t_resampled)] * t_resampled[1] + convolution[0: len(t_resampled)] * t_resampled[1] ) # Restore time grid spacing @@ -175,13 +187,23 @@ def extended_tofts( """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] @@ -195,14 +217,16 @@ def extended_tofts( `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: @@ -222,6 +246,7 @@ def extended_tofts( >>> 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]): @@ -255,7 +280,8 @@ def extended_tofts( 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 @@ -279,8 +305,9 @@ def extended_tofts( # 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)) @@ -306,9 +333,10 @@ def extended_tofts( # 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 + # Discard unwanted points, make sure time spacing is + # correct and add vp*ca term for extended model ct_resampled = convolution[ - 0 : len(t_resampled) + 0: len(t_resampled) ] * t_resampled[1] + (vp * ca_resampled) # Restore time grid spacing diff --git a/tests/test_aif.py b/tests/test_aif.py index ffb086e..94f57a2 100755 --- a/tests/test_aif.py +++ b/tests/test_aif.py @@ -26,8 +26,8 @@ def test_aif_weinmann(): # Not implemented yet so need to raise an error 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 diff --git a/tests/test_tissue.py b/tests/test_tissue.py index 74b963b..c74019f 100755 --- a/tests/test_tissue.py +++ b/tests/test_tissue.py @@ -6,9 +6,11 @@ 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) @@ -16,13 +18,15 @@ def test_tissue_tofts(): 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 + # 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) @@ -30,7 +34,8 @@ def test_tissue_tofts(): 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) @@ -39,7 +44,8 @@ def test_tissue_tofts(): ) 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) @@ -74,19 +80,22 @@ def test_tissue_tofts(): def test_tissue_extended_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.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 + # 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( @@ -96,7 +105,8 @@ def test_tissue_extended_tofts(): 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) @@ -105,8 +115,9 @@ def test_tissue_extended_tofts(): ) 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)