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)