diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml
new file mode 100644
index 00000000..36e4e919
--- /dev/null
+++ b/.github/workflows/build_docs.yml
@@ -0,0 +1,68 @@
+# Simple workflow for deploying static content to GitHub Pages
+name: Deploy static content to Pages
+
+on:
+ # Runs on pushes targeting the default branch
+ push:
+ branches:
+ - "**"
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+# Allow one concurrent deployment
+concurrency:
+ group: "pages"
+ cancel-in-progress: true
+
+jobs:
+
+ build:
+ runs-on: ubuntu-22.04
+ container: ghcr.io/scientificcomputing/fenics:2023-01-16
+ env:
+ DEB_PYTHON_INSTALL_LAYOUT: deb_system
+ PUBLISH_DIR: ./_build/html
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Install dependencies
+ run: python3 -m pip install --no-binary=h5py ".[docs,dev]"
+
+ - name: Build docs
+ run: jupyter book build .
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v1
+ with:
+ path: ${{ env.PUBLISH_DIR }}
+
+ # Single deploy job since we're just deploying
+ deploy:
+ if: github.ref == 'refs/heads/development'
+ needs: build
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Setup Pages
+ uses: actions/configure-pages@v2
+
+
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v1
diff --git a/.github/workflows/test_fenics_stubs.yml b/.github/workflows/test_fenics_stubs.yml
index ca8ff286..3c5a3cb4 100644
--- a/.github/workflows/test_fenics_stubs.yml
+++ b/.github/workflows/test_fenics_stubs.yml
@@ -23,12 +23,13 @@ jobs:
- uses: actions/checkout@v3
- name: "Install code"
- run: python3 -m pip install --no-binary=h5py .[dev]
+ run: python3 -m pip install --no-binary=h5py .[dev] jupytext
- name: Run example 1
run: |
cd examples/example1/
- python3 example1_driver.py
+ jupytext example1.ipynb --to py
+ python3 example1.py
# - name: Run tests
# run: |
diff --git a/.gitignore b/.gitignore
index c246a814..cca8406e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+_build
+examples/example1/mesh/
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
diff --git a/.readthedocs.yml b/.readthedocs.yml
deleted file mode 100644
index 937e1b5f..00000000
--- a/.readthedocs.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-# .readthedocs.yml
-# Read the Docs configuration file
-# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
-
-# Required
-version: 2
-
-build:
- image: latest
-
-# Build documentation in the docs/ directory with Sphinx
-sphinx:
- builder: html
- configuration: docs/conf.py
-
-formats:
- - pdf
-
-# Optionally set the version of Python and requirements required to build your docs
-python:
- version: 3.7
- install:
- - requirements: docs/rtd-requirements.txt
- - method: setuptools
- path: .
diff --git a/_config.yml b/_config.yml
new file mode 100644
index 00000000..a391c1b4
--- /dev/null
+++ b/_config.yml
@@ -0,0 +1,38 @@
+# Book settings
+# Learn more at https://jupyterbook.org/customize/config.html
+
+title: fenics-subs
+author: Justin Laughlin
+copyright: "2022"
+only_build_toc_files: true
+
+# Force re-execution of notebooks on each build.
+# See https://jupyterbook.org/content/execute.html
+execute:
+ execute_notebooks: cache
+ timeout: 3000
+
+# Information about where the book exists on the web
+repository:
+ url: https://github.com/justinlaughlin/stubs # Online location of your book
+ branch: development
+
+
+html:
+ use_issues_button: true
+ use_repository_button: true
+
+parse:
+ myst_enable_extensions:
+ - amsmath
+ - dollarmath
+ - linkify
+
+sphinx:
+
+ extra_extensions:
+ - 'sphinx.ext.autodoc'
+ - 'sphinx.ext.napoleon'
+ - 'sphinx.ext.viewcode'
+ - 'sphinx.ext.autosummary'
+
diff --git a/_toc.yml b/_toc.yml
new file mode 100644
index 00000000..5dd34808
--- /dev/null
+++ b/_toc.yml
@@ -0,0 +1,15 @@
+format: jb-book
+root: docs/index
+
+parts:
+ - caption: Getting started
+ chapters:
+ - file: docs/install
+ - file: docs/math
+ - file: docs/faq
+ - caption: Demo
+ chapters:
+ - file: examples/example1/example1
+ - caption: API documentation
+ chapters:
+ - file: "docs/pystubs_verbose.rst"
diff --git a/docs/Makefile b/docs/Makefile
deleted file mode 100644
index d4bb2cbb..00000000
--- a/docs/Makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-# Minimal makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line, and also
-# from the environment for the first two.
-SPHINXOPTS ?=
-SPHINXBUILD ?= sphinx-build
-SOURCEDIR = .
-BUILDDIR = _build
-
-# Put it first so that "make" without argument is like "make help".
-help:
- @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
-
-.PHONY: help Makefile
-
-# Catch-all target: route all unknown targets to Sphinx using the new
-# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
-%: Makefile
- @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/_pythonapi/stubs.common.rst b/docs/_pythonapi/stubs.common.rst
new file mode 100644
index 00000000..97f8211d
--- /dev/null
+++ b/docs/_pythonapi/stubs.common.rst
@@ -0,0 +1,65 @@
+stubs.common
+============
+
+.. automodule:: stubs.common
+
+
+
+
+
+
+
+ .. rubric:: Functions
+
+ .. autosummary::
+
+ DemoCuboidsMesh
+ append_meshfunction_to_meshdomains
+ bmesh_to_parent
+ convert_xml_to_hdf5
+ cube_condition
+ data_path
+ empty_sbmodel
+ face_topology
+ find_steady_state
+ insert_dataframe_col
+ interp_limit_dy
+ json_to_ObjectContainer
+ mesh_vertex_to_dof
+ nan_to_none
+ np_smart_hstack
+ pint_quantity_to_unit
+ pint_unit_to_quantity
+ read_hdf5
+ read_sbmodel
+ round_to_n
+ sbmodel_from_locals
+ stubs_expressions
+ sub
+ submesh_dof_to_mesh_dof
+ submesh_dof_to_vertex
+ submesh_to_bmesh
+ sum_discrete_signals
+ write_mesh
+ write_sbmodel
+ zplane_condition
+
+
+
+
+
+ .. rubric:: Classes
+
+ .. autosummary::
+
+ Stopwatch
+ ref
+
+
+
+
+
+
+
+
+
diff --git a/docs/_pythonapi/stubs.config.rst b/docs/_pythonapi/stubs.config.rst
new file mode 100644
index 00000000..06703334
--- /dev/null
+++ b/docs/_pythonapi/stubs.config.rst
@@ -0,0 +1,29 @@
+stubs.config
+============
+
+.. automodule:: stubs.config
+
+
+
+
+
+
+
+
+
+
+
+ .. rubric:: Classes
+
+ .. autosummary::
+
+ Config
+
+
+
+
+
+
+
+
+
diff --git a/docs/_pythonapi/stubs.data_manipulation.rst b/docs/_pythonapi/stubs.data_manipulation.rst
new file mode 100644
index 00000000..964bd087
--- /dev/null
+++ b/docs/_pythonapi/stubs.data_manipulation.rst
@@ -0,0 +1,31 @@
+stubs.data\_manipulation
+========================
+
+.. automodule:: stubs.data_manipulation
+
+
+
+
+
+
+
+
+
+
+
+ .. rubric:: Classes
+
+ .. autosummary::
+
+ Data
+ PostProcessor
+ Probe
+
+
+
+
+
+
+
+
+
diff --git a/docs/_pythonapi/stubs.mesh.rst b/docs/_pythonapi/stubs.mesh.rst
new file mode 100644
index 00000000..305ab845
--- /dev/null
+++ b/docs/_pythonapi/stubs.mesh.rst
@@ -0,0 +1,30 @@
+stubs.mesh
+==========
+
+.. automodule:: stubs.mesh
+
+
+
+
+
+
+
+
+
+
+
+ .. rubric:: Classes
+
+ .. autosummary::
+
+ ChildMesh
+ ParentMesh
+
+
+
+
+
+
+
+
+
diff --git a/docs/_pythonapi/stubs.model.rst b/docs/_pythonapi/stubs.model.rst
new file mode 100644
index 00000000..6967b098
--- /dev/null
+++ b/docs/_pythonapi/stubs.model.rst
@@ -0,0 +1,29 @@
+stubs.model
+===========
+
+.. automodule:: stubs.model
+
+
+
+
+
+
+
+
+
+
+
+ .. rubric:: Classes
+
+ .. autosummary::
+
+ Model
+
+
+
+
+
+
+
+
+
diff --git a/docs/_pythonapi/stubs.model_assembly.rst b/docs/_pythonapi/stubs.model_assembly.rst
new file mode 100644
index 00000000..9e171f44
--- /dev/null
+++ b/docs/_pythonapi/stubs.model_assembly.rst
@@ -0,0 +1,43 @@
+stubs.model\_assembly
+=====================
+
+.. automodule:: stubs.model_assembly
+
+
+
+
+
+
+
+
+
+
+
+ .. rubric:: Classes
+
+ .. autosummary::
+
+ Compartment
+ CompartmentContainer
+ FieldVariable
+ Flux
+ FluxContainer
+ Form
+ FormContainer
+ ObjectContainer
+ ObjectInstance
+ Parameter
+ ParameterContainer
+ Reaction
+ ReactionContainer
+ Species
+ SpeciesContainer
+
+
+
+
+
+
+
+
+
diff --git a/docs/_pythonapi/stubs.solvers.rst b/docs/_pythonapi/stubs.solvers.rst
new file mode 100644
index 00000000..0657a461
--- /dev/null
+++ b/docs/_pythonapi/stubs.solvers.rst
@@ -0,0 +1,29 @@
+stubs.solvers
+=============
+
+.. automodule:: stubs.solvers
+
+
+
+
+
+
+
+
+
+
+
+ .. rubric:: Classes
+
+ .. autosummary::
+
+ stubsSNESProblem
+
+
+
+
+
+
+
+
+
diff --git a/docs/_templates/base.rst b/docs/_templates/base.rst
deleted file mode 100644
index 986dd9a3..00000000
--- a/docs/_templates/base.rst
+++ /dev/null
@@ -1,12 +0,0 @@
-{% set fullname = fullname | replace(module ~ ".", "")%}
-{% if objtype == "method" %}
- {% set fullname = fullname ~ "()" %}
-{% endif %}
-
-{{ fullname | escape | underline }}
-
-.. currentmodule:: {{ module }}
-
-Lives in: {{ module }}
-
-.. auto{{ objtype }}:: {{ objname }}
\ No newline at end of file
diff --git a/docs/_templates/class.rst b/docs/_templates/class.rst
deleted file mode 100644
index 5fa64b3b..00000000
--- a/docs/_templates/class.rst
+++ /dev/null
@@ -1,31 +0,0 @@
-{% set fullname = fullname | replace(module ~ ".", "") %}
-{{ fullname | escape | underline }}
-
-.. currentmodule:: {{ module }}
-
-.. autoclass:: {{ objname }}
-
-{% block methods %}
-{% if methods %}
- .. rubric:: Methods
-
- .. autosummary::
- :toctree: .
- {% for item in methods %}
- {{ name }}.{{ item }}
- {% endfor %}
-{% endif %}
-{% endblock %}
-
-
-{% block attributes %}
-{% if attributes %}
- .. rubric:: Attributes
-
- .. autosummary::
- :toctree: .
- {% for item in attributes %}
- {{ name }}.{{ item }}
- {% endfor %}
-{% endif %}
-{% endblock %}
\ No newline at end of file
diff --git a/docs/_templates/module.rst b/docs/_templates/module.rst
deleted file mode 100644
index cd56fc1d..00000000
--- a/docs/_templates/module.rst
+++ /dev/null
@@ -1,29 +0,0 @@
-{{ fullname | escape | underline }}
-
-.. rubric:: Description
-
-.. automodule:: {{ fullname }}
-
-{% block classes %}
-{% if classes %}
- .. rubric:: Classes
-
- .. autosummary::
- :toctree: .
- {% for class in classes %}
- {{ class }}
- {% endfor %}
-{% endif %}
-{% endblock %}
-
-{% block functions %}
-{% if functions %}
- .. rubric:: Functions
-
- .. autosummary::
- :toctree: .
- {% for function in functions %}
- {{ function }}
- {% endfor %}
-{% endif %}
-{% endblock %}
\ No newline at end of file
diff --git a/docs/conf.py b/docs/conf.py
deleted file mode 100644
index f51647ad..00000000
--- a/docs/conf.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# Configuration file for the Sphinx documentation builder.
-#
-# This file only contains a selection of the most common options. For a full
-# list see the documentation:
-# https://www.sphinx-doc.org/en/master/usage/configuration.html
-
-# -- 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('.'))
-#import stubs
-
-
-# -- Project information -----------------------------------------------------
-
-project = 'stubs'
-copyright = '2021, Justin Laughlin'
-author = 'Justin Laughlin'
-
-# The full version, including alpha/beta/rc tags
-release = '0.1.10'
-
-
-# -- 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 = [
- 'sphinx.ext.autosummary', # auto generates function/method/attribute summary lists
- 'sphinx.ext.autodoc', # auto generate documentation from docstrings
- 'sphinx.ext.viewcode',
- 'sphinx.ext.mathjax', # tex math rendered with java
- 'sphinx.ext.napoleon', # numpy/google style docstrings
- 'sphinx.ext.intersphinx',
-]
-
-##############################
-# Napoleon Settings
-##############################
-napoleon_google_docstring = True
-napoleon_numpy_docstring = True
-napoleon_include_init_with_doc = True
-napoleon_include_private_with_doc = False
-napoleon_include_special_with_doc = True
-napoleon_use_admonition_for_examples = False
-napoleon_use_admonition_for_notes = False
-napoleon_use_admonition_for_references = False
-napoleon_use_ivar = True
-napoleon_use_param = True
-napoleon_use_rtype = True
-
-# 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 = ['_build', 'Thumbs.db', '.DS_Store']
-
-
-autodoc_mock_imports = ["dolfin", "petsc4py", "mpi4py"]
-
-
-# -- 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 = 'alabaster'
-
-# 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']
-
-##############################
-# Autosummary Settings
-##############################
-
-autosummary_generate = True
-# autodoc_default_flags = ['members', 'inherited-members']
-
-##############################
-# HTML Output Settings
-##############################
-
-# Try to load sphinx_rtd_theme otherwise fallback on default
-try:
- import sphinx_rtd_theme
- html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
- html_theme = 'sphinx_rtd_theme'
-except ImportError:
- html_theme = 'default'
-
-##############################
-# Intersphinx Settings
-##############################
-
-# Example configuration for intersphinx: refer to the Python standard library.
-intersphinx_mapping = {'python': ('https://docs.python.org/3', None),
- 'numpy': ('https://numpy.org/doc/stable/', None)}
diff --git a/docs/faq.md b/docs/faq.md
new file mode 100644
index 00000000..0fe2ab86
--- /dev/null
+++ b/docs/faq.md
@@ -0,0 +1,6 @@
+# Frequently Asked Questions
+
+1. How do I use `pip` to install `stubs`?
+
+> The name of the package is `fenics-stubs` which unfortunately does not
+> match the module name. Run `pip install fenics-stubs`.
diff --git a/docs/faq.rst b/docs/faq.rst
deleted file mode 100644
index 58c7915e..00000000
--- a/docs/faq.rst
+++ /dev/null
@@ -1,8 +0,0 @@
-##########################
-Frequently Asked Questions
-##########################
-
-#. How do I use ``pip`` to install ``stubs``?
-
- The name of the package is ``fenics-stubs`` which unfortunately does not match the module name.
- Run ``pip install fenics-stubs``.
\ No newline at end of file
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 00000000..8db3e6a4
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,18 @@
+# Setup Tool for Unified Biophysical Simulations
+
+STUBS is a biophysical simulation library that provides a level of
+abstraction to models, making it easier for users to develop, share, and
+simulate their mathematical models. STUBS is highly suited for building
+systems biology models and simulating them as deterministic partial
+differential equations [\[PDEs\]]{.title-ref} in realistic geometries
+using the Finite Element Method [\[FEM\]]{.title-ref} - the integration
+of additional physics such as electro-diffusion or stochasticity may
+come in future updates. Systems biology models are converted by STUBS
+into the appropriate systems of reaction-diffusion PDEs with proper
+boundary conditions. [FEniCS](https://fenicsproject.org/) is a core
+dependency of STUBS which handles the assembly of finite element
+matrices as well as solving the resultant linear algebra systems.
+
+## Contents
+```{tableofcontents}
+```
\ No newline at end of file
diff --git a/docs/index.rst b/docs/index.rst
deleted file mode 100644
index 90fcd1ad..00000000
--- a/docs/index.rst
+++ /dev/null
@@ -1,35 +0,0 @@
-###############################################
-Setup Tool for Unified Biophysical Simulations
-###############################################
-
-STUBS is a biophysical simulation library that provides a level of abstraction to models, making it easier for users to develop, share, and simulate their mathematical models.
-STUBS is highly suited for building systems biology models and simulating them as deterministic partial differential equations `[PDEs]` in realistic geometries using the Finite Element Method `[FEM]` - the integration of additional physics such as electro-diffusion or stochasticity may come in future updates.
-Systems biology models are converted by STUBS into the appropriate systems of reaction-diffusion PDEs with proper boundary conditions.
-`FEniCS `_ is a core dependency of STUBS which handles the assembly of finite element matrices as well as solving the resultant linear algebra systems.
-
-.. toctree::
- :maxdepth: 1
- :caption: Getting Started:
-
- install
- math
- faq
-
-.. toctree::
- :maxdepth: 3
- :caption: API Documentation:
-
- pystubs
-
-.. toctree::
- :maxdepth: 3
- :caption: API Documentation (verbose):
-
- pystubs_verbose
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
diff --git a/docs/install.md b/docs/install.md
new file mode 100644
index 00000000..dd441af3
--- /dev/null
+++ b/docs/install.md
@@ -0,0 +1,6 @@
+# Installation
+
+Simply run `pip install fenics-stubs` in an environment with FEniCS
+installed. We recommend using a [FEniCS docker
+container](https://github.com/scientificcomputing/packages/pkgs/container/fenics) to minimize
+installation issues.
diff --git a/docs/install.rst b/docs/install.rst
deleted file mode 100644
index e87d031e..00000000
--- a/docs/install.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-##########################
-Installation
-##########################
-
-Simply run ``pip install fenics-stubs`` in an environment with FEniCS installed.
-We recommend using a `FEniCS docker container `_ to minimize installation issues.
diff --git a/docs/make.bat b/docs/make.bat
deleted file mode 100644
index 153be5e2..00000000
--- a/docs/make.bat
+++ /dev/null
@@ -1,35 +0,0 @@
-@ECHO OFF
-
-pushd %~dp0
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
- set SPHINXBUILD=sphinx-build
-)
-set SOURCEDIR=.
-set BUILDDIR=_build
-
-if "%1" == "" goto help
-
-%SPHINXBUILD% >NUL 2>NUL
-if errorlevel 9009 (
- echo.
- echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
- echo.installed, then set the SPHINXBUILD environment variable to point
- echo.to the full path of the 'sphinx-build' executable. Alternatively you
- echo.may add the Sphinx directory to PATH.
- echo.
- echo.If you don't have Sphinx installed, grab it from
- echo.https://www.sphinx-doc.org/
- exit /b 1
-)
-
-%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
-goto end
-
-:help
-%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
-
-:end
-popd
diff --git a/docs/math.md b/docs/math.md
new file mode 100644
index 00000000..eec3876b
--- /dev/null
+++ b/docs/math.md
@@ -0,0 +1,23 @@
+# Mathematics
+
+Mathematics related to stubs.
+
+## Multi-Dimensional Reaction-Diffusion Equations
+
+Volumetric partial differential equations
+
+```{math}
+\begin{aligned}
+\partial_t u^{(a)}_{i} &= \nabla \cdot (D^{(a)}_{i} \nabla u^{(a)}_{i}) + f^{(a)}_{i}(u^{(a)}) ~~\text{in}~~ \Omega^{(a)}\\
+D^{(a)}_{i} (\nabla u^{(a)}_{i} \cdot n) &= r^{(abc)}_{i}(u^{(a)}, u^{(b)}, v^{(abc)}) ~~\text{on}~~ \Gamma^{(abc)}
+\end{aligned}
+```
+
+Surface partial differential equations
+
+```{math}
+\begin{aligned}
+\partial_t v^{(abc)}_{i} &= \nabla_S \cdot (D^{(abc)}_{i} \nabla_S v^{(abc)}_{i}) + g^{(abc)}_{i}(u^{(a)}, u^{(b)}, v^{(abc)}) ~~\text{on}~~ \Gamma^{(abc)} \\
+D^{(abc)}_{i} (\nabla_S v^{(abc)}_i \cdot n) &= 0 ~~\text{on}~~ \partial\Gamma^{(abc)}
+\end{aligned}
+```
diff --git a/docs/math.rst b/docs/math.rst
deleted file mode 100644
index 7f83d96e..00000000
--- a/docs/math.rst
+++ /dev/null
@@ -1,23 +0,0 @@
-##########################
-Mathematics
-##########################
-
-Mathematics related to stubs.
-
-.. _Multi-Dimensional Reaction-Diffusion Equations:
-
-**************************************************
-Multi-Dimensional Reaction-Diffusion Equations
-**************************************************
-
-Volumetric partial differential equations
-
-.. math::
- \partial_t u^{(a)}_{i} &= \nabla \cdot (D^{(a)}_{i} \nabla u^{(a)}_{i}) + f^{(a)}_{i}(u^{(a)}) ~~\text{in}~~ \Omega^{(a)}\\
- D^{(a)}_{i} (\nabla u^{(a)}_{i} \cdot n) &= r^{(abc)}_{i}(u^{(a)}, u^{(b)}, v^{(abc)}) ~~\text{on}~~ \Gamma^{(abc)}
-
-Surface partial differential equations
-
-.. math::
- \partial_t v^{(abc)}_{i} &= \nabla_S \cdot (D^{(abc)}_{i} \nabla_S v^{(abc)}_{i}) + g^{(abc)}_{i}(u^{(a)}, u^{(b)}, v^{(abc)}) ~~\text{on}~~ \Gamma^{(abc)} \\
- D^{(abc)}_{i} (\nabla_S v^{(abc)}_i \cdot n) &= 0 ~~\text{on}~~ \partial\Gamma^{(abc)}
\ No newline at end of file
diff --git a/docs/pystubs_verbose.rst b/docs/pystubs_verbose.rst
index dabd6a86..e7b2af03 100644
--- a/docs/pystubs_verbose.rst
+++ b/docs/pystubs_verbose.rst
@@ -4,17 +4,6 @@ Pystubs Test
Testing more verbose documentation.
-.. automodule:: stubs
-.. automodule:: stubs.model_assembly
-.. automodule:: stubs.common
-.. automodule:: stubs.data_manipulation
-.. automodule:: stubs.model
-.. automodule:: stubs.solvers
.. automodule:: stubs.config
-
-Testing explicit members
-========================
-
-.. automodule:: stubs.model_assembly
- :members: ObjectContainer
-
+ :members:
+ :show-inheritance:
\ No newline at end of file
diff --git a/docs/rtd-requirements.txt b/docs/rtd-requirements.txt
deleted file mode 100644
index 78a94132..00000000
--- a/docs/rtd-requirements.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-sphinx
-matplotlib
-numpy
-pandas
-pint
-scipy
-sympy
-dataclasses
-tabulate
-termcolor
-
-#petsc4py==3.12.0
-#mpi4py==3.0.3
-#dolfin
diff --git a/examples/example1/example1.ipynb b/examples/example1/example1.ipynb
new file mode 100644
index 00000000..1a14e6cd
--- /dev/null
+++ b/examples/example1/example1.ipynb
@@ -0,0 +1,321 @@
+{
+ "cells": [
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "f65f18d7",
+ "metadata": {},
+ "source": [
+ "# Simple example showcasing some of the features of STUBS\n",
+ "\n",
+ "Geometry is divided into 4 domains; two volumes, and two surfaces:\n",
+ "- PM\n",
+ "- Cytosol\n",
+ "- Cytosol\n",
+ "\n",
+ "There are three function-spaces on the three domains:\n",
+ "```\n",
+ "- u[Cyto] = [A, B]\n",
+ "- u[ERm] = [R, Ro]\n",
+ "- u[ER] = [AER]\n",
+ "```\n",
+ "\n",
+ "Roughly, this model is similar to an IP3 pulse at the PM, leading to Ca2+ release at the ER"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "cc398816",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "Authorization required, but no authorization protocol specified\n",
+ "Authorization required, but no authorization protocol specified\n"
+ ]
+ }
+ ],
+ "source": [
+ "import os\n",
+ "\n",
+ "import dolfin as d\n",
+ "import sympy as sym\n",
+ "\n",
+ "from stubs import unit, config, common, mesh, model\n",
+ "from stubs.model_assembly import Compartment, Parameter, Reaction, Species"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "95b9d865",
+ "metadata": {},
+ "source": [
+ "First, we define the various units for the inputs"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "4f4023cf",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Aliases - base units\n",
+ "uM = unit.uM\n",
+ "um = unit.um\n",
+ "molecule = unit.molecule\n",
+ "sec = unit.sec\n",
+ "# Aliases - units used in model\n",
+ "D_unit = um**2 / sec\n",
+ "flux_unit = molecule / (um**2 * sec)\n",
+ "vol_unit = uM\n",
+ "surf_unit = molecule / um**2"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "46582d26",
+ "metadata": {},
+ "source": [
+ "Next we generate the model."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "09079b17",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def make_model():\n",
+ " # =============================================================================================\n",
+ " # Species\n",
+ " # =============================================================================================\n",
+ " # name, initial concentration, concentration units, diffusion, diffusion units, compartment\n",
+ " A = Species(\"A\", 0.01, vol_unit, 1.0, D_unit, \"Cyto\")\n",
+ " B = Species(\"B\", 0.0, vol_unit, 1.0, D_unit, \"Cyto\")\n",
+ " AER = Species(\"AER\", 200.0, vol_unit, 5.0, D_unit, \"ER\")\n",
+ "\n",
+ " # Lets create an algebraic expression to define the initial condition of R\n",
+ " Rinit = \"(sin(40*y) + cos(40*z) + sin(40*x) + 3) * (y-x)**2\"\n",
+ " R1 = Species(\"R1\", Rinit, surf_unit, 0.02, D_unit, \"ERm\")\n",
+ " R1o = Species(\"R1o\", 0.0, surf_unit, 0.02, D_unit, \"ERm\")\n",
+ " # R2 = Species('R2' , Rinit, surf_unit, 0 , D_unit, 'ERm')\n",
+ "\n",
+ " # =============================================================================================\n",
+ " # Compartments\n",
+ " # =============================================================================================\n",
+ " # name, topological dimensionality, length scale units, marker value\n",
+ " Cyto = Compartment(\"Cyto\", 3, um, 1)\n",
+ " PM = Compartment(\"PM\", 2, um, 10)\n",
+ " ER = Compartment(\"ER\", 3, um, 2)\n",
+ " ERm = Compartment(\"ERm\", 2, um, 12)\n",
+ "\n",
+ " # =============================================================================================\n",
+ " # Parameters and Reactions\n",
+ " # =============================================================================================\n",
+ " # Pulse function for B input at the PM\n",
+ " # One way to prescribe a \"pulse-like\" flux is to define the flux as the derivative of a sigmoid\n",
+ " # (here we choose atan as the sigmoid because of its simple derivative)\n",
+ " Vmax, t0, m = 500, 0.1, 200\n",
+ " t = sym.symbols(\"t\")\n",
+ " pulseI = Vmax * sym.atan(m * (t - t0))\n",
+ " pulse = sym.diff(pulseI, t)\n",
+ " j1pulse = Parameter.from_expression(\n",
+ " \"j1pulse\", pulse, flux_unit, use_preintegration=True, preint_sym_expr=pulseI\n",
+ " )\n",
+ " r1 = Reaction(\n",
+ " \"r1\",\n",
+ " [],\n",
+ " [\"B\"],\n",
+ " param_map={\"J\": \"j1pulse\"},\n",
+ " eqn_f_str=\"J\",\n",
+ " explicit_restriction_to_domain=\"PM\",\n",
+ " )\n",
+ "\n",
+ " # Degradation of B in the cytosol\n",
+ " k2f = Parameter(\"k2f\", 10, 1 / sec)\n",
+ " r2 = Reaction(\n",
+ " \"r2\", [\"B\"], [], param_map={\"on\": \"k2f\"}, reaction_type=\"mass_action_forward\"\n",
+ " )\n",
+ "\n",
+ " # Activating receptors on ERm with B\n",
+ " k3f = Parameter(\"k3f\", 100, 1 / (uM * sec))\n",
+ " k3r = Parameter(\"k3r\", 100, 1 / sec)\n",
+ " r3 = Reaction(\"r3\", [\"B\", \"R1\"], [\"R1o\"], {\"on\": \"k3f\", \"off\": \"k3r\"})\n",
+ "\n",
+ " # Release of A from ERm to cytosol\n",
+ " k4Vmax = Parameter(\"k4Vmax\", 2000, 1 / (uM * sec))\n",
+ " r4 = Reaction(\n",
+ " \"r4\",\n",
+ " [\"AER\"],\n",
+ " [\"A\"],\n",
+ " param_map={\"Vmax\": \"k4Vmax\"},\n",
+ " species_map={\"R1o\": \"R1o\", \"uER\": \"AER\", \"u\": \"A\"},\n",
+ " eqn_f_str=\"Vmax*R1o*(uER-u)\",\n",
+ " )\n",
+ " # explicit_restriction_to_domain='ERm')\n",
+ "\n",
+ " # =============================================================================================\n",
+ " # Gather all parameters, species, compartments and reactions\n",
+ " # =============================================================================================\n",
+ " return common.sbmodel_from_locals(locals().values())"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "15c35d39",
+ "metadata": {},
+ "source": [
+ "We load the model generated above, and load in the mesh we will use in this example."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "fe56e162",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\u001b[32m[2023-01-20 time=02:35:42] Creating dolfin object for space-dependent initial condition R1\u001b[0m \u001b[97m\u001b[0m\n",
+ "\n",
+ "\u001b[35m!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\u001b[0m\n",
+ "\u001b[35m!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\u001b[0m\n",
+ "\u001b[35m!!!!!\u001b[0m \u001b[31m[2023-01-20 time=02:35:42] Warning! Pre-integrating parameter j1pulse. Make sure that expressions j1pulse appears in have no other time-dependent variables.\u001b[0m \u001b[35m!!!!!\u001b[0m\n",
+ "\u001b[35m!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\u001b[0m\n",
+ "\u001b[35m!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\u001b[0m\n",
+ "\n",
+ "\n",
+ "\u001b[35m!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\u001b[0m\n",
+ "\u001b[35m!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\u001b[0m\n",
+ "\u001b[35m!!!!!\u001b[0m \u001b[31m[2023-01-20 time=02:35:42] Warning! Pre-integrating parameter j1pulse. Make sure that expressions j1pulse appears in have no other time-dependent variables.\u001b[0m \u001b[35m!!!!!\u001b[0m\n",
+ "\u001b[35m!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\u001b[0m\n",
+ "\u001b[35m!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\u001b[0m\n",
+ "\n",
+ "\u001b[32m[2023-01-20 time=02:35:42] Time-dependent parameter j1pulse evaluated from expression.\u001b[0m \u001b[97m\u001b[0m\n",
+ "HDF5 mesh, \"parent_mesh\", successfully loaded from file: mesh/DemoCuboidsMesh.h5!\n",
+ "Object `config.solver.update` not found.\n"
+ ]
+ },
+ {
+ "ename": "AttributeError",
+ "evalue": "'SolverConfig' object has no attribute 'update'",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)",
+ "Cell \u001b[0;32mIn[4], line 25\u001b[0m\n\u001b[1;32m 23\u001b[0m model \u001b[39m=\u001b[39m model\u001b[39m.\u001b[39mModel(pc, sc, cc, rc, config, parent_mesh)\n\u001b[1;32m 24\u001b[0m get_ipython()\u001b[39m.\u001b[39mrun_line_magic(\u001b[39m'\u001b[39m\u001b[39mpinfo\u001b[39m\u001b[39m'\u001b[39m, \u001b[39m'\u001b[39m\u001b[39mconfig.solver.update\u001b[39m\u001b[39m'\u001b[39m)\n\u001b[0;32m---> 25\u001b[0m config\u001b[39m.\u001b[39;49msolver\u001b[39m.\u001b[39;49mupdate(\n\u001b[1;32m 26\u001b[0m {\n\u001b[1;32m 27\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mfinal_t\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39m1\u001b[39m,\n\u001b[1;32m 28\u001b[0m \u001b[39m\"\u001b[39m\u001b[39minitial_dt\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39m0.01\u001b[39m,\n\u001b[1;32m 29\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mtime_precision\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39m6\u001b[39m,\n\u001b[1;32m 30\u001b[0m \u001b[39m\"\u001b[39m\u001b[39muse_snes\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mTrue\u001b[39;00m,\n\u001b[1;32m 31\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mprint_assembly\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mFalse\u001b[39;00m,\n\u001b[1;32m 32\u001b[0m }\n\u001b[1;32m 33\u001b[0m )\n\u001b[1;32m 35\u001b[0m model\u001b[39m.\u001b[39minitialize(initialize_solver\u001b[39m=\u001b[39m\u001b[39mFalse\u001b[39;00m)\n\u001b[1;32m 36\u001b[0m model\u001b[39m.\u001b[39minitialize_discrete_variational_problem_and_solver()\n",
+ "\u001b[0;31mAttributeError\u001b[0m: 'SolverConfig' object has no attribute 'update'"
+ ]
+ }
+ ],
+ "source": [
+ "\n",
+ "pc, sc, cc, rc = make_model()\n",
+ "\n",
+ "# =============================================================================================\n",
+ "# Create/load in mesh\n",
+ "# =============================================================================================\n",
+ "# Base mesh\n",
+ "domain, facet_markers, cell_markers = common.DemoCuboidsMesh()\n",
+ "# Turn off \"PM\" on all sides of the cube except x=0\n",
+ "for face in d.faces(domain):\n",
+ " if face.midpoint().x() > d.DOLFIN_EPS and facet_markers[face] == 10:\n",
+ " facet_markers[face] = 0\n",
+ "# Write mesh and meshfunctions to file\n",
+ "os.makedirs(\"mesh\", exist_ok=True)\n",
+ "common.write_mesh(domain, facet_markers, cell_markers, filename=\"mesh/DemoCuboidsMesh\")\n",
+ "\n",
+ "# # Define solvers\n",
+ "parent_mesh = mesh.ParentMesh(\n",
+ " mesh_filename=\"mesh/DemoCuboidsMesh.h5\",\n",
+ " mesh_filetype=\"hdf5\",\n",
+ " name=\"parent_mesh\",\n",
+ ")\n",
+ "config = config.Config()\n",
+ "model = model.Model(pc, sc, cc, rc, config, parent_mesh)\n",
+ "config.solver.update(\n",
+ " {\n",
+ " \"final_t\": 1,\n",
+ " \"initial_dt\": 0.01,\n",
+ " \"time_precision\": 6,\n",
+ " \"use_snes\": True,\n",
+ " \"print_assembly\": False,\n",
+ " }\n",
+ ")\n",
+ "\n",
+ "model.initialize(initialize_solver=False)\n",
+ "model.initialize_discrete_variational_problem_and_solver()\n",
+ "\n",
+ "# Write initial condition(s) to file\n",
+ "results = dict()\n",
+ "os.makedirs(\"results\", exist_ok=True)\n",
+ "for species_name, species in model.sc.items:\n",
+ " results[species_name] = d.XDMFFile(\n",
+ " model.mpi_comm_world, f\"results/{species_name}.xdmf\"\n",
+ " )\n",
+ " results[species_name].parameters[\"flush_output\"] = True\n",
+ " results[species_name].write(model.sc[species_name].u[\"u\"], model.t)\n",
+ "\n",
+ "# Solve\n",
+ "while True:\n",
+ " # Solve the system\n",
+ " model.monolithic_solve()\n",
+ " # Save results for post processing\n",
+ " for species_name, species in model.sc.items:\n",
+ " results[species_name].write(model.sc[species_name].u[\"u\"], model.t)\n",
+ " # End if we've passed the final time\n",
+ " if model.t >= model.final_t:\n",
+ " break"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b54d28ca",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "jupytext": {
+ "cell_metadata_filter": "-all",
+ "main_language": "python",
+ "notebook_metadata_filter": "-all"
+ },
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.6"
+ },
+ "vscode": {
+ "interpreter": {
+ "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1"
+ }
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/examples/example1/example1_driver.py b/examples/example1/example1_driver.py
deleted file mode 100755
index f5898ff4..00000000
--- a/examples/example1/example1_driver.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# =============================================================================================
-# STUBS driver script example
-# =============================================================================================
-# Imports
-import os
-
-import dolfin as d
-
-import example1_model
-
-import stubs
-
-
-# =============================================================================================
-# Load model
-# =============================================================================================
-pc, sc, cc, rc = example1_model.make_model()
-
-# =============================================================================================
-# Create/load in mesh
-# =============================================================================================
-# Base mesh
-mesh, mf2, mf3 = stubs.common.DemoCuboidsMesh()
-# Turn off "PM" on all sides of the cube except x=0
-for face in d.faces(mesh):
- if face.midpoint().x() > d.DOLFIN_EPS and mf2[face]==10:
- mf2[face] = 0
-# Write mesh and meshfunctions to file
-os.makedirs('mesh', exist_ok=True)
-stubs.common.write_mesh(mesh, mf2, mf3, filename='mesh/DemoCuboidsMesh')
-
-# # Define solvers
-parent_mesh = stubs.mesh.ParentMesh(mesh_filename='mesh/DemoCuboidsMesh.h5', mesh_filetype='hdf5', name='parent_mesh')
-config = stubs.config.Config()
-model = stubs.model.Model(pc, sc, cc, rc, config, parent_mesh)
-config.solver.update({'final_t':1, 'initial_dt':.01, 'time_precision': 6, 'use_snes': True, 'print_assembly': False})
-model.initialize(initialize_solver=False)
-model.initialize_discrete_variational_problem_and_solver()
-
-# Write initial condition(s) to file
-results = dict()
-os.makedirs('results', exist_ok=True)
-for species_name, species in model.sc.items:
- results[species_name] = d.XDMFFile(model.mpi_comm_world, f'results/{species_name}.xdmf')
- results[species_name].parameters['flush_output'] = True
- results[species_name].write(model.sc[species_name].u['u'], model.t)
-
-# Solve
-while True:
- # Solve the system
- model.monolithic_solve()
- # Save results for post processing
- for species_name, species in model.sc.items:
- results[species_name].write(model.sc[species_name].u['u'], model.t)
- # End if we've passed the final time
- if model.t >= model.final_t:
- break
diff --git a/examples/example1/example1_model.py b/examples/example1/example1_model.py
deleted file mode 100755
index 0572f8a3..00000000
--- a/examples/example1/example1_model.py
+++ /dev/null
@@ -1,95 +0,0 @@
-"""
-Simple example showcasing some of the features of STUBS
-
-(Will make this into a jupyter notebook later)
-
-Geometry is divdied into 4 domains, 2 volumes, and 2 surfaces:
- - PM
- - Cytosol
- - Cytosol
-
-3 function spaces on 3 domains:
- - u[Cyto] = [A, B]
- - u[ERm] = [R, Ro]
- - u[ER] = [AER]
-
-Roughly, this model is similar to an IP3 pulse at the PM, leading to Ca2+ release at the ER
-"""
-
-import sympy as sym
-
-import stubs
-from stubs.common import sbmodel_from_locals
-from stubs.model_assembly import Compartment, Parameter, Reaction, Species
-
-# Aliases - base units
-unit = stubs.unit # unit registry
-uM = unit.uM
-um = unit.um
-molecule = unit.molecule
-sec = unit.sec
-# Aliases - units used in model
-D_unit = um**2/sec
-flux_unit = molecule/(um**2 * sec)
-vol_unit = uM
-surf_unit = molecule/um**2
-
-def make_model():
- # =============================================================================================
- # Species
- # =============================================================================================
- # name, initial concentration, concentration units, diffusion, diffusion units, compartment
- A = Species('A' , 0.01 , vol_unit , 1.0, D_unit, 'Cyto')
- B = Species('B' , 0.0 , vol_unit , 1.0, D_unit, 'Cyto')
- AER = Species('AER', 200.0, vol_unit , 5.0, D_unit, 'ER')
-
- # Lets create an algebraic expression to define the initial condition of R
- Rinit = "(sin(40*y) + cos(40*z) + sin(40*x) + 3) * (y-x)**2"
- R1 = Species('R1' , Rinit, surf_unit, 0.02, D_unit, 'ERm')
- R1o = Species('R1o' , 0.0 , surf_unit, 0.02, D_unit, 'ERm')
- #R2 = Species('R2' , Rinit, surf_unit, 0 , D_unit, 'ERm')
-
- # =============================================================================================
- # Compartments
- # =============================================================================================
- # name, topological dimensionality, length scale units, marker value
- Cyto = Compartment('Cyto', 3, um, 1)
- PM = Compartment('PM' , 2, um, 10)
- ER = Compartment('ER' , 3, um, 2)
- ERm = Compartment('ERm' , 2, um, 12)
-
- # =============================================================================================
- # Parameters and Reactions
- # =============================================================================================
- # Pulse function for B input at the PM
- # One way to prescribe a "pulse-like" flux is to define the flux as the derivative of a sigmoid
- # (here we choose atan as the sigmoid because of its simple derivative)
- Vmax, t0, m = 500, 0.1, 200
- t = sym.symbols('t')
- pulseI = Vmax*sym.atan(m*(t-t0))
- pulse = sym.diff(pulseI,t)
- j1pulse = Parameter.from_expression('j1pulse', pulse, flux_unit, use_preintegration=True,
- preint_sym_expr=pulseI)
- r1 = Reaction('r1', [], ['B'], param_map={"J": "j1pulse"}, eqn_f_str='J',
- explicit_restriction_to_domain='PM')
-
- # Degradation of B in the cytosol
- k2f = Parameter('k2f', 10, 1/sec)
- r2 = Reaction('r2', ['B'], [], param_map={"on": "k2f"}, reaction_type='mass_action_forward')
-
- # Activating receptors on ERm with B
- k3f = Parameter('k3f', 100, 1/(uM*sec))
- k3r = Parameter('k3r', 100, 1/sec)
- r3 = Reaction('r3', ['B', 'R1'], ['R1o'], {"on": "k3f", "off": "k3r"})
-
- # Release of A from ERm to cytosol
- k4Vmax = Parameter('k4Vmax', 2000, 1/(uM*sec))
- r4 = Reaction('r4', ['AER'], ['A'], param_map={"Vmax": "k4Vmax"},
- species_map={'R1o': 'R1o', 'uER': 'AER', 'u': 'A'},
- eqn_f_str='Vmax*R1o*(uER-u)',)
- # explicit_restriction_to_domain='ERm')
-
- # =============================================================================================
- # Gather all parameters, species, compartments and reactions
- # =============================================================================================
- return sbmodel_from_locals(locals().values())
diff --git a/setup.py b/setup.py
index 14e96a72..5a49b787 100644
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,5 @@
from setuptools import setup
+
# from setuptools import find_packages
with open("README.md", "r") as handle:
@@ -7,48 +8,44 @@
setup(
# Self-descriptive entries which should always be present
- name='fenics-stubs',
- author='Justin Laughlin',
- author_email='justinglaughlin@gmail.com',
- url='https://github.com/justinlaughlin/stubs',
- description='STUBS is a biophysical simulation library that provides a level of abstraction to models, making it easier for users to develop, share, and simulate their mathematical models.',
+ name="fenics-stubs",
+ author="Justin Laughlin",
+ author_email="justinglaughlin@gmail.com",
+ url="https://github.com/justinlaughlin/stubs",
+ description="STUBS is a biophysical simulation library that provides a level of abstraction to models, making it easier for users to develop, share, and simulate their mathematical models.",
long_description=long_description,
- long_description_content_type='text/markdown',
- platforms=['Linux', 'Mac OS-X'],
- version='0.1.10',
- license='LGPLv3',
-
+ long_description_content_type="text/markdown",
+ platforms=["Linux", "Mac OS-X"],
+ version="0.1.10",
+ license="LGPLv3",
# Which Python importable modules should be included when your package is installed
# Handled automatically by setuptools. Use 'exclude' to prevent some specific
# subpackage(s) from being added, if needed
# packages=find_packages(),
- packages=['stubs'],
-
+ packages=["stubs"],
# Optional include package data to ship with your package
# Customize MANIFEST.in if the general case does not suit your needs
# Comment out this line to prevent the files from being packaged with your software
# include_package_data=True,
-
# Additional entries you may want simply uncomment the lines you want and fill in the data
install_requires=[
- 'matplotlib',
- 'numpy>=1.16.0',
- 'pandas',
- 'Pint',
- 'scipy>=1.1.0',
- 'sympy',
- 'dataclasses',
- 'cached-property',
- 'pydantic',
- 'tabulate',
- 'termcolor',
- 'pytest',
- 'sphinx==4.3.0',
- 'termplotlib'
- ], # Required packages, pulls from pip if needed; do not use for Conda deployment
+ "matplotlib",
+ "numpy>=1.16.0",
+ "pandas",
+ "Pint",
+ "scipy>=1.1.0",
+ "sympy",
+ "dataclasses",
+ "cached-property",
+ "pydantic",
+ "tabulate",
+ "termcolor",
+ "pytest",
+ "termplotlib",
+ ], # Required packages, pulls from pip if needed; do not use for Conda deployment
# python_requires=">=3.5", # Python version restrictions
- extras_require={'dev': ['h5py']},
+ extras_require={"dev": ["h5py"], "docs": [
+ "jupyter-book@git+https://github.com/executablebooks/jupyter-book.git@master"]},
# Manual control if final package is compressible or not, set False to prevent the .egg from being made
# zip_safe=False,
-
)
diff --git a/stubs/__init__.py b/stubs/__init__.py
index f900533f..568aac5b 100644
--- a/stubs/__init__.py
+++ b/stubs/__init__.py
@@ -1,15 +1,6 @@
-"""
-stubs
-"""
-# Add imports here
-# import config first
from . import config
-from . import data_manipulation
-from . import model_assembly
-from . import solvers
-from . import model
-from . import common
-from . import mesh
+from . import (common, data_manipulation, mesh, model, model_assembly,
+ solvers, post_process)
from .units import unit
__all__ = [
@@ -20,6 +11,6 @@
"model",
"common",
"mesh",
- "unit"
+ "unit",
+ "post_process"
]
-from . import post_process
\ No newline at end of file
diff --git a/stubs/config.py b/stubs/config.py
index 0754fd6c..2f3b9c5c 100644
--- a/stubs/config.py
+++ b/stubs/config.py
@@ -1,9 +1,29 @@
"""
Configuration settings for simulation: plotting, reaction types, solution output, etc.
"""
+import logging
+from dataclasses import dataclass, field
+from typing import Any, Dict, Optional, Tuple
+
import dolfin as d
+import numpy as np
+import numpy.typing as npt
import ufl
-import logging
+
+__all__ = ["global_settings", "dolfin_expressions", "Config",
+ "SolverConfig", "BaseConfig", "FlagsConfig", "OutputConfig",
+ "LogLevelConfig", "PlottingConfig"
+ ]
+
+_valid_filetypes = ["xdmf", "vtk", None]
+_loglevel_to_int: Dict[str, int] = {
+ "CRITICAL": int(d.LogLevel.CRITICAL),
+ "ERROR": int(d.LogLevel.ERROR),
+ "WARNING": int(d.LogLevel.WARNING),
+ "INFO": int(d.LogLevel.INFO),
+ "DEBUG": int(d.LogLevel.DEBUG),
+ "NOTSET": 0,
+}
global_settings = {
"main_dir": None,
@@ -30,118 +50,192 @@
},
}
-dolfin_expressions = {
- "exp": d.exp,
- "cos": d.cos,
- "sin": d.sin,
- "tan": d.tan,
- "cosh": ufl.cosh,
- "sinh": ufl.sinh,
- "tanh": ufl.tanh,
- "acos": d.acos,
- "asin": d.asin,
- "atan": d.atan,
- "atan2": ufl.atan_2,
- "sqrt": d.sqrt,
- "ln": d.ln,
- "abs": ufl.algebra.Abs,
- "sign": ufl.sign,
- "pi": d.pi,
- "erf": d.erf,
-}
+class BaseConfig():
+ """
+ Base-class for setting configuration
+ """
+
+ def update(self, values: Dict[str, Any]):
+ """
+ Given a dictionary of input keys and values, update the named tuple.
+
+ :throws AttributeError: If input key does not exist
+ """
+ for value, item in values.items():
+ self.__setattr__(value, item)
+
+ def __setattr__(self, key: str, value: Any):
+ if key not in self.__annotations__:
+ raise AttributeError(f"{key} not defined in {self.__annotations__.keys()}")
+ else:
+ object.__setattr__(self, key, value)
-class Config:
+ def __getitem__(self, key):
+ return self.__getattribute__(key)
+
+
+@dataclass
+class SolverConfig(BaseConfig):
"""
- Refactored config
- - directories
- - plot settings
- - reactions
- - logging
+ Parameters for solver.
+
+ :param final_t: End time of simulation
+ :param use_snes: Use PETScSNES solver if true, else use DOLFINs NewtonSolver
+ :param snes_preassemble_linear_system: If True separate linear components during assembly
+ :param initial_dt: Initial time-stepping
+ :param adjust_dt: A tuple (t, dt) of floats indicating when to next adjust the
+ time-stepping and to what value
+ :param dt: Number of digits for rounding `dt`
+ :param print_assembly: Print information during assembly process
+ :param dt_decrease_factor:
+ :param dt_increase_factor:
+ :param attempt_timestep_restart_on_divergence: Restart snes solver if it diverges
"""
- def __init__(self):
- self.solver = {
- "final_t": None,
- "use_snes": True,
- "snes_preassemble_linear_system": True,
- "initial_dt": None,
- "adjust_dt": None,
- "time_precision": 6,
- "print_assembly": True,
- "dt_decrease_factor": 1.0,
- "dt_increase_factor": 1.0,
- "attempt_timestep_restart_on_divergence": False, # Currently needs to be looked int
- }
-
- # initialize with default values
- self.flags = {
- "store_solutions": True,
- "allow_unused_components": False,
- "print_verbose_info": True,
- }
-
- self.directory = {"solutions": "solutions", "plots": "plots"}
-
- self.output_type = "xdmf"
-
- self.plot_settings = {
- "lineopacity": 0.6,
- "linewidth_small": 0.6,
- "linewidth_med": 2.2,
- "fontsize_small": 3.5,
- "fontsize_med": 4.5,
- "figname": "figure",
- }
-
- # self.probe_plot = {'A': [(0.5,0.0), (1.0,0.0)]}
- self.probe_plot = {}
-
- self.reaction_database = {
- "prescribed": "k",
- "prescribed_linear": "k*u",
- }
-
- self.loglevel = {
- "FFC": "DEBUG",
- "UFL": "DEBUG",
- "dijitso": "DEBUG",
- "dolfin": "INFO",
- }
-
- self._loglevel_to_int = {
- "CRITICAL": 50,
- "ERROR": 40,
- "WARNING": 30,
- "INFO": 20,
- "DEBUG": 10,
- "NOTSET": 0,
- }
+ final_t: Optional[float] = None
+ use_snes: bool = True
+ snes_preassemble_linear_system: bool = False #: .. warning:: FIXME Currently untested
+ initial_dt: Optional[float] = None
+ adjust_dt: Optional[Tuple[float, float]] = None
+ time_precision: int = 6
+ print_assembly: bool = True
+ dt_decrease_factor: float = 1.0 #: .. warning:: FIXME Currently unused parameter
+ dt_increase_factor: float = 1.0 #: .. warning:: FIXME Currently unused parameter
+ attempt_timestep_restart_on_divergence: bool = False #: .. warning:: FIXME Currently untested
- def check_config_validity(self):
- valid_filetypes = ["xdmf", "vtk", None]
- if self.output_type not in valid_filetypes:
- raise ValueError(f"Only filetypes: '{valid_filetypes}' are supported.")
- if self.solver["final_t"] is None:
- raise ValueError(f"Please provide a final time in config.solver")
- if self.solver["initial_dt"] is None:
- raise ValueError(
- f"Please provide an initial time-step size in config.solver"
- )
+@dataclass
+class FlagsConfig(BaseConfig):
+ """
+ Various flags
+
+ :param store_solutions: Store solutions to file.
+ :param allow_unused_components: Allow parameters not defined in any reaction to be
+ defined in any model.
+ :param print_verbose_info: Print detailed information about a model
+ """
+ store_solutions: bool = True
+ allow_unused_components: bool = False
+ print_verbose_info: bool = True
+
+
+@dataclass
+class OutputConfig(BaseConfig):
+ """
+ Settings for output
+
+ :param solutions: Name of directory to store solutions to
+ :param plots: Name of directory to store plots to
+ :param output_type: Format of output
+ """
+ solutions: str = "solutions"
+ plots: str = "plots"
+ output_type: str = "xdmf"
+
+
+@dataclass
+class LogLevelConfig(BaseConfig):
+ """
+ Settings for logging
+
+ :param FFC: LogLevel for FFC
+ :param UFL: LogLevel for UFL
+ :param dijitso: LogLevel for dijitso
+ :param dolfin: LogLevel for dolfin
+
+ """
+ FFC: str = "DEBUG"
+ UFL: str = "DEBUG"
+ djitso: str = "DEBUG"
+ dolfin: str = "INFO"
def set_logger_levels(self):
+ """
+ For each of the loggers, set the appropriate log-level
+ """
# set for dolfin
- d.set_log_level(self._loglevel_to_int[self.loglevel["dolfin"]])
+ d.set_log_level(_loglevel_to_int[self.dolfin])
+
# set for others
- other_loggers = list(self.loglevel.keys())
+ other_loggers = list(self.__annotations__)
+ print(other_loggers)
other_loggers.remove("dolfin")
for logger_name in other_loggers:
- logging.getLogger(logger_name).setLevel(self.loglevel[logger_name])
+ logging.getLogger(logger_name).setLevel(
+ _loglevel_to_int[self.__getattribute__(logger_name)])
+
+
+@dataclass
+class PlottingConfig(BaseConfig):
+ """
+ Options for matplotlib plotting
+ """
+
+ lineopacity: float = 0.6 # . Opacity of lines
+ linewidth_small: float = 0.6 # . Thickness of small lines
+ linewidth_med: float = 2.2 # . Thickness of medium lines
+ fontsize_small: float = 3.5 # . Fontsize of small text
+ fontsize_med: float = 4.5 # . Fontsize of large text
+ figname: str = "figure" # . Name of figure
+
+
+@dataclass
+class Config():
+ """
+ Configuration settings.
+
+ :param solver: Options for the solvers
+ :param flags: Various options
+ :param directory: Outputting options
+ :param loglevel: Logging options for FEniCS modules
+ :param plot_settings: Options for matplotlib plotting
+ :param probe_plot: Dictionary mapping a Function (by its name) to a
+ set of coordinates where the function should be mapped
+ """
+ solver: SolverConfig = field(default_factory=SolverConfig)
+ flags: FlagsConfig = field(default_factory=FlagsConfig)
+ loglevel: LogLevelConfig = field(default_factory=LogLevelConfig)
+ plot_settings: PlottingConfig = field(default_factory=PlottingConfig)
+ directory: OutputConfig = field(default_factory=OutputConfig)
+ probe_plot: Dict[str, npt.NDArray[np.float64]] = field(default_factory=dict)
- def set_all_logger_levels(self, log_level):
- for logger_name in self.loglevel.keys():
- self.loglevel[logger_name] = log_level
+ def __init__(self):
+ self.solver = SolverConfig()
+ self.flags = FlagsConfig()
+ self.directory = OutputConfig()
+ self.loglevel = LogLevelConfig()
+ self.plot_settings = PlottingConfig()
+
+ @property
+ def reaction_database(self) -> Dict[str, str]:
+ """
+ Return database of known reactions
+ """
+ return {"prescribed": "k",
+ "prescribed_linear": "k*u"}
+
+ def output_type(self):
+ return self.directory["output_type"]
+
+ def check_config_validity(self):
+ if self.output_type not in _valid_filetypes:
+ raise ValueError(f"Only filetypes: '{_valid_filetypes}' are supported.")
+ if self.solver.final_t is None:
+ raise ValueError("Please provide a final time in config.solver")
+ if self.solver.initial_dt is None:
+ raise ValueError("Please provide an initial time-step size in config.solver")
+
+ def set_logger_levels(self):
+ self.loglevel.set_logger_levels()
+
+ def set_all_logger_levels(self, log_level: int):
+ """
+ Set all loggers to a given loglevel
+ :param log_level: The loglevel: `(0,10,20,30,40,50)`
+ """
+ # Update LogLevel class
+ for logger_name in self.loglevel.__annotations__:
+ self.loglevel.__setattr__(logger_name, log_level)
+ # Set LogLevels to logger
self.set_logger_levels()
- # for logger_name in self.loglevel.keys():
- # logging.getLogger(logger_name).setLevel(log_level)