Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
199a2eb
add comments and data
MaHaWo Feb 5, 2025
090f954
improve pyvista plotting functions
MaHaWo Feb 18, 2025
a6eeab8
add tests for pyvista plotting utils
MaHaWo Mar 11, 2025
2ab4fa6
finish utils test
MaHaWo Mar 12, 2025
d6e6d31
reorganize tests
MaHaWo Mar 12, 2025
5df8906
make test compatible with coverage reporting
MaHaWo Mar 12, 2025
b4c51a8
remove superfluous function, make docstring better
MaHaWo Mar 12, 2025
6d9b68b
adjust python versions
MaHaWo Mar 12, 2025
ab05b25
add pyvista headless display to
MaHaWo Mar 12, 2025
677e766
make code compatible with old python versions
MaHaWo Mar 12, 2025
45c1de6
try to fix docstring issues
MaHaWo Mar 12, 2025
9520ac8
fix documentation issue
MaHaWo Mar 12, 2025
f14453c
fix issues with default args
MaHaWo Mar 12, 2025
efee1f6
fix bug in constructor call
MaHaWo Mar 12, 2025
6d5ff5a
add functions that eat sme.SimulationResult
MaHaWo Apr 3, 2025
8a579f7
add 3D wrapper functions and docs notebook stuff
MaHaWo Apr 3, 2025
7c1fc44
correct type annotations
MaHaWo Apr 3, 2025
1a23b49
correct typos
MaHaWo Apr 3, 2025
57901a7
make notebook work
MaHaWo Apr 3, 2025
beb7026
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 3, 2025
9a8279e
try to add static backend to make pv work
MaHaWo Apr 3, 2025
f91081b
Merge branch 'add-pyvista-3D-visualization' of github.com:spatial-mod…
MaHaWo Apr 3, 2025
aca5a2d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 3, 2025
7cced94
try to make docs thing work again damnit
MaHaWo Apr 3, 2025
76a8f85
Merge branch 'add-pyvista-3D-visualization' of github.com:spatial-mod…
MaHaWo Apr 3, 2025
4d92d5c
fix import issue
MaHaWo Apr 4, 2025
24de02d
fix improve README section in notebook
MaHaWo Apr 4, 2025
6cd63e1
use client mode for pv to try and make pipeline work
MaHaWo Apr 4, 2025
723b4fc
try to make doc build on ci work
MaHaWo Apr 4, 2025
35e5218
try once more to make docs ci work
MaHaWo Apr 4, 2025
9ea64e7
adjust headings
MaHaWo Apr 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- uses: pyvista/setup-headless-display-action@v3
- run: pip install -e .[tests]
- run: python -m pytest --cov=sme_contrib --cov-report=xml -v
- uses: codecov/codecov-action@v3
Expand Down
2 changes: 2 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"pyvista.ext.plot_directive",
"pyvista.ext.viewer_directive",
"nbsphinx",
"sphinx_rtd_theme",
"sphinx.ext.autodoc",
Expand Down
311 changes: 311 additions & 0 deletions docs/notebooks/model.xml

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/notebooks/optimize.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@
"provenance": []
},
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": ".venv39",
"language": "python",
"name": "python3"
},
Expand All @@ -223,7 +223,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
"version": "3.9.21"
}
},
"nbformat": 4,
Expand Down
298 changes: 293 additions & 5 deletions docs/notebooks/plot.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,19 @@
},
"outputs": [],
"source": [
"!pip install -q sme_contrib\n",
"# !pip install -q sme_contrib\n",
"import pyvista as pv\n",
"from pyvista import examples\n",
"\n",
"pv.set_jupyter_backend(\"static\")\n",
"\n",
"import sme\n",
"import sme_contrib.plot as smeplot\n",
"from matplotlib import pyplot as plt\n",
"from IPython.display import HTML"
"import numpy as np\n",
"import tempfile\n",
"from IPython.display import HTML\n",
"from IPython.display import Video\n",
"from matplotlib import pyplot as plt"
]
},
{
Expand Down Expand Up @@ -184,6 +192,286 @@
"HTML(anim.to_jshtml())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Plot concentrations on 3D grid"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The API for 3D plotting has two layers: \n",
"- the low-level function provide a high degree of customisability, but need more code to get them to work:\n",
" - `facet_grid_3D`: two low-level functions that map a dictionary of name->plotfunctions over a dictionary name->data, where both have to have the same keys. \n",
" For each key in the data dictionary, a single plot pane will be created. \n",
" - `facet_grid_animate_3D` is an animated version of this function that creates an .mp4 file for and receives a dictionary of name->data for each frame of the animation over which the dictionary containing the plot functions is then mapped in each frame. \n",
"- `concentrations3D` and `concentrationsAnimate3D`: These high-level API directly uses `sme.SimulationResult` objects as data input, but only plots concentrations by default. These are wrappers around the low-level functions that provide default plotting functions for each pane and handle the data preparation for each pane automatically.\n",
"\n",
"- for switching between interactive and static plotting, use `pv.set_jupyter_backend('trame')` for interactive plotting, or `pv.set_jupyter_backend('static')` for static plotting. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**README:** Using `import pyvista as pv` in both a notebook and a module imported in the notebook will cause a premature initialization of `pyvista` when the imported module is initialized and in turn a configuration conflict that will prevent the notebook from working properly. To work around this, you have to make sure that the `pyvista` module is only imported in one of the two. Since notebooks often have specific requirements for how the plots can be shown (statically or interactively) it is best to only import the needed objects from pyvista when developing a module and only importing the package-level module (`import pyvista...`) within notebooks that use this module. If a complete import of pyvista in a module is unavoidable, make sure to only import the needed functions in the notebook and not the entire module, to prevent pyvista's `__init__` from running prematurely."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"model_file = \"./model.xml\"\n",
"model = sme.open_sbml_file(model_file)\n",
"results = model.simulate(500, 10)\n",
"\n",
"species = list(results[0].species_concentration.keys())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def exampledata():\n",
" armadillo = examples.download_armadillo()\n",
" bloodvessel = examples.download_blood_vessels()\n",
" brain = examples.download_brain()\n",
"\n",
" return {\n",
" \"armadillo\": armadillo,\n",
" \"bloodvessel\": bloodvessel,\n",
" \"brain\": brain,\n",
" }\n",
"\n",
"\n",
"datasets = exampledata()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"tempdir = tempfile.TemporaryDirectory()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Low level functions"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We are plotting some example functions here to show the functionality of the `facetGrid` functions.\n",
"We first define the functions that are applied to each pane in the grid:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def plot_bloodvessel(label, data, plotter, panel, **kwargs):\n",
" plotter.subplot(*panel)\n",
" plotter.add_mesh(data)\n",
"\n",
"\n",
"def plot_brain(label, data, plotter, panel, **kwargs):\n",
" plotter.subplot(*panel)\n",
" plotter.add_volume(\n",
" data,\n",
" cmap=\"viridis\",\n",
" opacity=\"sigmoid\", # Common opacity mapping for volume rendering\n",
" shade=True,\n",
" ambient=0.3,\n",
" diffuse=0.6,\n",
" specular=0.5,\n",
" )\n",
"\n",
"\n",
"def plot_armadillo(label, data, plotter, panel, **kwargs):\n",
" plotter.subplot(*panel)\n",
" plotter.add_mesh(data)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Then we put the it all together in the call to `facet_grid_3D`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"facetgrid = smeplot.facet_grid_3D(\n",
" data={\n",
" \"armadillo\": datasets[\"armadillo\"],\n",
" \"bloodvessel\": datasets[\"bloodvessel\"],\n",
" \"brain\": datasets[\"brain\"],\n",
" },\n",
" plotfuncs={\n",
" \"armadillo\": plot_armadillo,\n",
" \"bloodvessel\": plot_bloodvessel,\n",
" \"brain\": plot_brain,\n",
" },\n",
" linked_views=False,\n",
")\n",
"\n",
"facetgrid.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Making an animation functions in much the same way, but we use a list of data dictionaries now, one for each frame."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We define a general plotting function that is used for each pane in each frame here. these can be different too, like shown above: "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def plotfunc(\n",
" label,\n",
" data,\n",
" plotter,\n",
" panel,\n",
" show_cmap,\n",
" cmap,\n",
" **kwargs,\n",
"):\n",
" # create a pyvista grid\n",
" plotter.subplot(*panel)\n",
" plotter.title = label\n",
" plotter.add_mesh(\n",
" data,\n",
" scalars=data,\n",
" label=label,\n",
" cmap=cmap,\n",
" show_scalar_bar=show_cmap,\n",
" **kwargs,\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Then use it in the call to `facet_grid_animate_3D` call. Note that we pass a list of data dictionaries now, while the `plotfuncs` dict stays a dictionary. You can use the plot function to customize lighting, perspective and more. See [the pyvista documentation for more information](https://docs.pyvista.org/api/plotting/_autosummary/pyvista.plotter.add_mesh)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vidpath = smeplot.facet_grid_animate_3D(\n",
" \"testvid.mp4\",\n",
" data=[\n",
" {species[i]: res.species_concentration[species[i]] for i in range(len(species))}\n",
" for res in results\n",
" ],\n",
" plotfuncs={species[i]: plotfunc for i in range(len(species))},\n",
" cmap=\"tab10\",\n",
" portrait=True,\n",
" linked_views=True,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"Video(vidpath, embed=True, width=800, height=600)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### High-level functions"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The high level functions wrap the low-level functions such that we don´t have to specify the plotting functions ourselves."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"smeplot.concentrations3D(\n",
" simulation_result=results[10],\n",
" species=[\"A_nucl\"],\n",
" cmap=\"tab10\",\n",
" show_cmap=True,\n",
").show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Animate concentations on a 3D grid"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vidpath = smeplot.concentrationsAnimate3D(\n",
" filename=\"test.mp4\",\n",
" simulation_results=results,\n",
" species=[\"A_nucl\"],\n",
" cmap=\"tab10\",\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"Video(vidpath, embed=True, width=800, height=600)"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand All @@ -199,7 +487,7 @@
"provenance": []
},
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": ".venv313",
"language": "python",
"name": "python3"
},
Expand All @@ -213,7 +501,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
"version": "3.13.1"
}
},
"nbformat": 4,
Expand Down
10 changes: 6 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ classifiers = [
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",

]
dependencies = ["matplotlib", "numpy", "pillow", "pyswarms", "sme>=1.4.0"]
dependencies = ["matplotlib", "numpy", "pillow", "pyswarms", "sme>=1.4.0","pyvista[all]", "imageio[ffmpeg]"]

dynamic = ["version"]

[project.urls]
Expand All @@ -42,7 +44,7 @@ docs = [
"nbsphinx",
"pandoc",
"sphinx>=4.5.0",
"sphinx_rtd_theme>=1.0.0"
"sphinx_rtd_theme>=1.0.0",
]

[tool.setuptools.dynamic]
Expand Down
Loading
Loading