Skip to content

Commit

Permalink
Added mkdocs structure
Browse files Browse the repository at this point in the history
  • Loading branch information
MohamedNasser8 committed Jun 20, 2024
1 parent 1f586f8 commit cf69511
Show file tree
Hide file tree
Showing 28 changed files with 663 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ junit
docs/build
docs/source/generated
docs/source/sg_execution_times.rst
docs-mk/site
docs-mk/docs/generated
12 changes: 12 additions & 0 deletions docs-mk/docs/about/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# About

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)**.

The osipi package structure and logic follow the lexicon defined by OSIPI, and wrap around selected implementations collected in the code contributions of OSIPI.

## 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.

!!! note "Future Updates"
The inclusion of arterial spin labelling (ASL) solutions may change in the future.
84 changes: 84 additions & 0 deletions docs-mk/docs/contribution/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# 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!

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 GitHub in your browser, edit it manually and follow the prompts to create a fork and pull request.

## How to Contribute Examples

One way to contribute is by providing examples of how you used `osipi` for a specific task. These are usually real-world examples with a relevant aim, perhaps to derive some results that you have published. To package these up as an example, follow these steps:

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 their 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 subfolder.

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.

## 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 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.

## 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 destabilize 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.

The tests are defined in the folder `osipi-tests`.

## 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.

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 the section on design principles below for general requirements from `osipi` code snippets.

## 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

### 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?

### 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.

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 organized into modules, but these may evolve over time and should not be accessed directly. Module names all start with an underscore `_module.py` to emphasize their private and transient nature. Equally, subfolders may be added in the future as the package grows.



### 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 arguments and returns other OSIPI variables as results. 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 consist 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:

1. Each function must be defined in the lexicon, and the docstring must include a reference section containing the following four items:
- Lexicon URL: webpage in the Lexicon where the function is defined.
- Lexicon code: machine-readable code identifying the entry in the Lexicon.
- OSIPI name: human-readable name for the function as defined in the Lexicon.
- Adapted from contribution: module.py in the original snippet in the code contribution repository
2. Each argument to the function as well as each return value *must* be defined in the Lexicon. The docstring of the function must provide the following data on each argument and return value:
- Python data type (include type hint in the function definition)
- Lexicon code: machine-readable code identifying the corresponding quantity in the Lexicon.
- OSIPI name: human-readable name for the quantity as defined in the Lexicon.
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
If your function addresses entirely novel functionality or uses new variables that are not yet described in the Lexicon, then you should first contact the Lexicon maintainers and request that it is added as an entry to the Lexicon. Only afterwards can it be considered as a contribution to the `osipi` package.

!!!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
5 changes: 5 additions & 0 deletions docs-mk/docs/examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

# Examples


### Illustrating common use cases of osipi.
3 changes: 3 additions & 0 deletions docs-mk/docs/examples/aif/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
************************
Arterial Input Functions
************************
52 changes: 52 additions & 0 deletions docs-mk/docs/examples/aif/plot_aif_parker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""
======================================
The Parker AIF - a play with variables
======================================
Simulating a Parker AIF with different settings.
"""

import matplotlib.pyplot as plt

# %%
# Import necessary packages
import numpy as np
import osipi

# %%
# Generate synthetic AIF with default settings and plot the result.

# Define time points in units of seconds - in this case we use a time
# resolution of 0.5 sec and a total duration of 6 minutes.
t = np.arange(0, 6 * 60, 0.5)

# Create an AIF with default settings
ca = osipi.aif_parker(t)

# Plot the AIF over the full range
plt.plot(t, ca, "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:

ca = osipi.aif_parker(t, BAT=0)
plt.plot(t, ca, "b-", label="BAT = 0s")
ca = osipi.aif_parker(t, BAT=30)
plt.plot(t, ca, "r-", label="BAT = 30s")
ca = osipi.aif_parker(t, BAT=60)
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.legend()
plt.show()

# Choose the last image as a thumbnail for the gallery
# sphinx_gallery_thumbnail_number = -1
51 changes: 51 additions & 0 deletions docs-mk/docs/examples/aif/plot_dummy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
==============
A dummy script
==============
Dummy script to illustrate structure of examples folder
"""

import matplotlib.pyplot as plt

# %%
# Import necessary packages
import numpy as np
import osipi

# %%
# Generate synthetic AIF with default settings and plot the result.

# Define time points in units of seconds - in this case we use a time
# resolution of 0.5 sec and a total duration of 6 minutes.
t = np.arange(0, 6 * 60, 0.5)

# Create an AIF with default settings
ca = osipi.aif_parker(t)

# Plot the AIF over the full range
plt.plot(t, ca, "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:

ca = osipi.aif_parker(t, BAT=0)
plt.plot(t, ca, "b-", label="BAT = 0s")
ca = osipi.aif_parker(t, BAT=30)
plt.plot(t, ca, "r-", label="BAT = 30s")
ca = osipi.aif_parker(t, BAT=60)
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.legend()
plt.show()

# Choose the last image as a thumbnail for the gallery
# sphinx_gallery_thumbnail_number = -1
3 changes: 3 additions & 0 deletions docs-mk/docs/examples/tissue/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*********************
Tissue concentrations
*********************
57 changes: 57 additions & 0 deletions docs-mk/docs/examples/tissue/plot_extended_tofts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""
====================
The Extended Tofts model
====================
Simulating tissue concentrations from extended Tofts model with different settings.
"""

import matplotlib.pyplot as plt

# %%
# Import necessary packages
import numpy as np
import osipi

# %%
# Generate Parker AIF with default settings.

# Define time points in units of seconds - in this case we use a time
# resolution of 1 sec and a total duration of 6 minutes.
t = np.arange(0, 6 * 60, 1)

# Create an AIF with default settings
ca = osipi.aif_parker(t)

# %%
# Plot the tissue concentrations for an extracellular volume fraction
# of 0.2 and 3 different 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
ct = osipi.extended_tofts(t, ca, Ktrans, ve, 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]}")
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.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)")
plt.legend()
plt.show()

# Choose the last image as a thumbnail for the gallery
# sphinx_gallery_thumbnail_number = -1
57 changes: 57 additions & 0 deletions docs-mk/docs/examples/tissue/plot_tofts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""
====================
The Tofts model
====================
Simulating tissue concentrations from Tofts model with different settings.
"""

import matplotlib.pyplot as plt

# %%
# Import necessary packages
import numpy as np
import osipi

# %%
# Generate Parker AIF with default settings.

# Define time points in units of seconds - in this case we use a time
# resolution of 1 sec and a total duration of 6 minutes.
t = np.arange(0, 6 * 60, 1)

# Create an AIF with default settings
ca = osipi.aif_parker(t)

# %%
# Plot the tissue concentrations for an extracellular volume fraction
# of 0.2 and 3 different transfer rate constants of 0.05, 0.2 and 0.6
# /min
Ktrans = [0.05, 0.2, 0.6] # in units of 1/min
ve = 0.2 # volume fraction between 0 and 1
ct = osipi.tofts(t, ca, Ktrans=Ktrans[0], ve=ve)
plt.plot(t, ct, "b-", label=f"Ktrans = {Ktrans[0]} /min")
ct = osipi.tofts(t, ca, Ktrans[1], ve)
plt.plot(t, ct, "g-", label=f"Ktrans = {Ktrans[1]} /min")
ct = osipi.tofts(t, ca, Ktrans[2], ve)
plt.plot(t, ct, "m-", label=f"Ktrans = {Ktrans[2]} /min")
plt.xlabel("Time (sec)")
plt.ylabel("Tissue concentration (mM)")
plt.legend()
plt.show()

# %%
# Comparing different discretization methods for an extracellular
# volume fraction of 0.2 and Ktrans of 0.2 /min
ct = osipi.tofts(t, ca, Ktrans=Ktrans[1], ve=ve) # Defaults to Convolution
plt.plot(t, ct, "b-", label="Convolution")
ct = osipi.tofts(t, ca, Ktrans=Ktrans[1], ve=ve, discretization_method="exp")
plt.plot(t, ct, "g-", label="Exponential Convolution")
plt.title(f"Ktrans = {Ktrans[1]} /min")
plt.xlabel("Time (sec)")
plt.ylabel("Tissue concentration (mM)")
plt.legend()
plt.show()

# Choose the last image as a thumbnail for the gallery
# sphinx_gallery_thumbnail_number = -1
Binary file added docs-mk/docs/img/osipi.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions docs-mk/docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Osipi Documentation

Welcome to the Osipi documentation! This guide is intended to help you get started with Osipi and provide the information you need to maximize its potential.

## Warning

!!! warning
`osipi` is developed in public but is a work in progress. This version contains mostly temporary placeholder functionality.

## About Osipi

`osipi` is the authoritative Python package for perfusion MRI. It is developed by the Open Source Initiative for Perfusion Imaging (OSIPI), which is a study group initiative of the International Society for Magnetic Resonance in Medicine (ISMRM).

## Purpose

The purpose of `osipi` is to standardize and streamline the use of perfusion MRI, ensuring consistency and reliability across different implementations and studies. The package follows the lexicon defined by OSIPI and integrates selected implementations from the original code contributions made by the OSIPI community.


Explore the documentation to learn more about how to use `osipi` and leverage its full capabilities for your perfusion MRI projects.

## Sections

- [User Guide](user-guide/index.md)
- [Contribution Guidelines](contribution/index.md)
- [References](references/index.md)
- [About](about/index.md)
- [Examples](examples/index.md)
10 changes: 10 additions & 0 deletions docs-mk/docs/references/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# 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.

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.

This reference guide, like the `osipi` package itself, adheres closely to the structure and language of the [OSIPI Lexicon](https://osipi.github.io/OSIPI_CAPLEX/).

!!!warning
`osipi` is in development and this reference guide currently contains mainly empty placeholder entries.
Empty file.
3 changes: 3 additions & 0 deletions docs-mk/docs/references/models/aif_models/aif_georgiou.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# osipi.aif_georgiou

::: osipi.aif_georgiou
Loading

0 comments on commit cf69511

Please sign in to comment.