diff --git a/.github/workflows/test_python_ver_matrix.yml b/.github/workflows/test_python_ver_matrix.yml
index ed00fc407e..9635c655fa 100644
--- a/.github/workflows/test_python_ver_matrix.yml
+++ b/.github/workflows/test_python_ver_matrix.yml
@@ -24,7 +24,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- python-version: [3.8, 3.9, '3.10']
+ python-version: [3.8, 3.9, '3.10', '3.11']
experimental: [false]
steps:
diff --git a/.readthedocs.yml b/.readthedocs.yml
index ef6f1ef59d..876248c0a3 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -24,5 +24,7 @@ build:
# for custom doxygen
- libclang-cpp9
- libclang1-9
+ - libatlas-base-dev
+ - swig
tools:
python: "3.8"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 30429dd960..582a78972a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,49 @@
## v0.X Series
+### v0.16.0 (2023-01-25)
+
+Features
+* Python 3.11 compatibility
+ by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1876
+* AMICI now runs on binder (https://mybinder.org/v2/gh/AMICI-dev/AMICI/develop?labpath=binder%2Foverview.ipynb)
+ by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1935,
+ https://github.com/AMICI-dev/AMICI/pull/1937,
+ https://github.com/AMICI-dev/AMICI/pull/1939
+* More informative `Solver.__repr__` and `ExpData.__repr__`
+ by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1928
+ and @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/1948
+* `simulate_petab` returns the generated/used `ExpData`s
+ by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1933
+* Model module is now accessible from model instance
+ by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1932
+* Added `plot_jacobian`
+ by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1930
+* Now logs all nested execution times as debug
+ by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/1947
+* Always check for finite initial states, not only with
+ `Model.setAlwaysCheckFinite(True)`
+ by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1955
+
+Fixes
+* `ReturnDataView.status` now returns `int` instead of `float`
+ by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1929
+* Updated simulation status codes
+ by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1931
+* Skip irrelevant frames in stacktraces
+ by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1934
+* Fixed compiler warning (matlab)
+ by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1954
+
+Documentation:
+* Added a notebook demonstrating common simulation failures and show how to
+ analyze / fix them
+ by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1946
+* various minor fixes / updates
+
+**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.15.0...v0.16.0
+
+
### v0.15.0 (2023-01-11)
Features
diff --git a/README.md b/README.md
index 52856ae214..f9f1aea0d2 100644
--- a/README.md
+++ b/README.md
@@ -85,7 +85,8 @@ To install AMICI, first read the installation instructions for
[Matlab](https://amici.readthedocs.io/en/develop/matlab_installation.html).
To get you started with Python-AMICI, the best way might be checking out this
-[Jupyter notebook](https://github.com/AMICI-dev/AMICI/blob/master/documentation/GettingStarted.ipynb).
+[Jupyter notebook](https://github.com/AMICI-dev/AMICI/blob/master/documentation/GettingStarted.ipynb)
+[](https://mybinder.org/v2/gh/AMICI-dev/AMICI/develop?labpath=documentation%2FGettingStarted.ipynb).
To get started with Matlab-AMICI, various examples are available
in [matlab/examples/](https://github.com/AMICI-dev/AMICI/tree/master/matlab/examples).
diff --git a/binder/README.md b/binder/README.md
new file mode 100644
index 0000000000..4739de23aa
--- /dev/null
+++ b/binder/README.md
@@ -0,0 +1,5 @@
+# binder/repo2docker configuration
+
+Configuration files for [binder](https://mybinder.org/) / repo2docker.
+
+Doc: https://mybinder.readthedocs.io/en/latest/using/config_files.html
diff --git a/binder/apt.txt b/binder/apt.txt
new file mode 100644
index 0000000000..4fce5fecdc
--- /dev/null
+++ b/binder/apt.txt
@@ -0,0 +1,5 @@
+clang
+libatlas-base-dev
+libboost-serialization-dev
+libhdf5-serial-dev
+swig
diff --git a/binder/overview.ipynb b/binder/overview.ipynb
new file mode 100644
index 0000000000..8902a05cbb
--- /dev/null
+++ b/binder/overview.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "f7ebed12-4309-4c92-a54e-da80ccd2d5e7",
+ "metadata": {},
+ "source": [
+ "# AMICI example notebooks\n",
+ "\n",
+ "* [Getting started](../documentation/GettingStarted.ipynb)\n",
+ "\n",
+ " Brief intro to AMICI for first-time users.\n",
+ "\n",
+ "* [Example \"steadystate\"](../python/examples/example_steadystate/ExampleSteadystate.ipynb)\n",
+ "\n",
+ " A more detailed introduction to the AMICI interface, demonstrating sensitivity analysis, various options, finite difference checks, ...\n",
+ "\n",
+ "* [PEtab import / simulation](../python/examples/example_petab/petab.ipynb)\n",
+ "\n",
+ " How to import and simulate PEtab problems.\n",
+ "\n",
+ "* [Experimental conditions](../python/examples/example_presimulation/ExampleExperimentalConditions.ipynb)\n",
+ "\n",
+ " How to represent different experimental conditions in AMICI and how to use preequilibration.\n",
+ "\n",
+ "* [Steadystate (sensitivities)](../python/examples/example_constant_species/ExampleEquilibrationLogic.ipynb)\n",
+ "\n",
+ " Describes and demonstrates the various algorithms for computing steady states and steady-state sensitivities.\n",
+ "\n",
+ "* [Simulation errors](../python/examples/example_errors.ipynb)\n",
+ "\n",
+ " Demonstrates common simulation failures and gives some hints for interpreting, debugging, and fixing them.\n"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "",
+ "name": ""
+ },
+ "language_info": {
+ "name": ""
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/binder/postBuild b/binder/postBuild
new file mode 100755
index 0000000000..87220b6c2e
--- /dev/null
+++ b/binder/postBuild
@@ -0,0 +1,7 @@
+#!/bin/bash
+set -eou pipefail
+
+pip install -e "git+https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab.git@master#subdirectory=src/python&egg=benchmark_models_petab"
+pip install -e python/sdist[petab,pysb]
+
+scripts/buildBNGL.sh
diff --git a/binder/runtime.txt b/binder/runtime.txt
new file mode 100644
index 0000000000..55090899d0
--- /dev/null
+++ b/binder/runtime.txt
@@ -0,0 +1 @@
+python-3.10
diff --git a/binder/start b/binder/start
new file mode 100755
index 0000000000..fd8bf31a7e
--- /dev/null
+++ b/binder/start
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -eou pipefail
+
+export BNGPATH="$(pwd)/ThirdParty/BioNetGen-2.7.0"
+
+exec "$@"
diff --git a/documentation/amici_refs.bib b/documentation/amici_refs.bib
index 1c8921ea06..f725e1b480 100644
--- a/documentation/amici_refs.bib
+++ b/documentation/amici_refs.bib
@@ -999,19 +999,6 @@ @Article{AdlungSta2021
url = {https://www.sciencedirect.com/science/article/pii/S2211124721009372},
}
-@article {Sundqvist2022.02.15.480629,
- author = {Sundqvist, Nicolas and Sten, Sebastian and Engstr{\"o}m, Maria and Cedersund, Gunnar},
- title = {Mechanistic model for human brain metabolism and the neurovascular coupling.},
- elocation-id = {2022.02.15.480629},
- year = {2022},
- doi = {10.1101/2022.02.15.480629},
- publisher = {Cold Spring Harbor Laboratory},
- abstract = {The neurovascular coupling (NVC) connects cerebral activity, blood flow, and metabolism. This interconnection is used in for instance functional imaging, which analyses the blood-oxygen-dependent (BOLD) signal. The mechanisms underlying the NVC are complex, which warrants a model-based analysis of data. We have previously developed a mechanistically detailed model for the NVC, and others have proposed detailed models for cerebral metabolism. However, existing metabolic models are still not fully utilizing available magnetic resonance spectroscopy (MRS) data and are not connected to detailed models for NVC. Therefore, we herein present a new model that integrates mechanistic modelling of both MRS and BOLD data. The metabolic model covers central metabolism, and can describe time-series data for glucose, lactate, aspartate, and glutamine, measured after visual stimuli. Statistical tests confirm that the model can describe both estimation data and predict independent validation data, not used for model training. The interconnected NVC model can simultaneously describe BOLD data and can be used to predict expected metabolic responses in experiments where metabolism has not been measured. This model is a step towards a useful and mechanistically detailed model for cerebral blood flow and metabolism, with potential applications in both basic research and clinical applications.Competing Interest StatementThe authors have declared no competing interest.},
- URL = {https://www.biorxiv.org/content/early/2022/02/19/2022.02.15.480629},
- eprint = {https://www.biorxiv.org/content/early/2022/02/19/2022.02.15.480629.full.pdf},
- journal = {bioRxiv}
-}
-
@article {Froehlich2022.02.17.480899,
author = {Fr{\"o}hlich, Fabian and Gerosa, Luca and Muhlich, Jeremy and Sorger, Peter K.},
title = {Mechanistic model of MAPK signaling reveals how allostery and rewiring contribute to drug resistance},
@@ -1039,19 +1026,6 @@ @Article{SluijsMaa2022
refid = {van Sluijs2022},
}
-@Article{LakrisenkoSta2022,
- author = {Lakrisenko, Polina and Stapor, Paul and Grein, Stephan and Paszkowski, {\L}ukasz and Pathirana, Dilan and Fr{\"o}hlich, Fabian and Lines, Glenn Terje and Weindl, Daniel and Hasenauer, Jan},
- journal = {bioRxiv},
- title = {Efficient computation of adjoint sensitivities at steady-state in {ODE} models of biochemical reaction networks},
- year = {2022},
- abstract = {Dynamical models in the form of systems of ordinary differential equations have become a standard tool in systems biology. Many parameters of such models are usually unknown and have to be inferred from experimental data. Gradient-based optimization has proven to be effective for parameter estimation. However, computing gradients becomes increasingly costly for larger models, which are required for capturing the complex interactions of multiple biochemical pathways. Adjoint sensitivity analysis has been pivotal for working with such large models, but methods tailored for steady-state data are currently not available. We propose a new adjoint method for computing gradients, which is applicable if the experimental data include steady-state measurements. The method is based on a reformulation of the backward integration problem to a system of linear algebraic equations. The evaluation of the proposed method using real-world problems shows a speedup of total simulation time by a factor of up to 4.4. Our results demonstrate that the proposed approach can achieve a substantial improvement in computation time, in particular for large-scale models, where computational efficiency is critical.Competing Interest StatementThe authors have declared no competing interest.},
- doi = {10.1101/2022.08.08.503176},
- elocation-id = {2022.08.08.503176},
- eprint = {https://www.biorxiv.org/content/early/2022/08/11/2022.08.08.503176.full.pdf},
- publisher = {Cold Spring Harbor Laboratory},
- url = {https://www.biorxiv.org/content/early/2022/08/11/2022.08.08.503176},
-}
-
@Article{VillaverdeRai2022,
author = {Villaverde, Alejandro F. and Raimúndez, Elba and Hasenauer, Jan and Banga, Julio R.},
journal = {IEEE/ACM Transactions on Computational Biology and Bioinformatics},
@@ -1075,6 +1049,90 @@ @Article{MishraWan2023
url = {https://www.sciencedirect.com/science/article/pii/S1096717622001380},
}
+@Article{MassonisVil2022,
+ author = {Massonis, Gemma and Villaverde, Alejandro F and Banga, Julio R},
+ journal = {Bioinformatics},
+ title = {{Improving dynamic predictions with ensembles of observable models}},
+ year = {2022},
+ issn = {1367-4803},
+ month = {11},
+ note = {btac755},
+ abstract = {{Dynamic mechanistic modelling in systems biology has been hampered by the complexity and variability associated with the underlying interactions, and by uncertain and sparse experimental measurements. Ensemble modelling, a concept initially developed in statistical mechanics, has been introduced in biological applications with the aim of mitigating those issues. Ensemble modelling uses a collection of different models compatible with the observed data to describe the phenomena of interest. However, since systems biology models often suffer from lack of identifiability and observability, ensembles of models are particularly unreliable when predicting non-observable states.We present a strategy to assess and improve the reliability of a class of model ensembles. In particular, we consider kinetic models described using ordinary differential equations (ODEs) with a fixed structure. Our approach builds an ensemble with a selection of the parameter vectors found when performing parameter estimation with a global optimization metaheuristic. This technique enforces diversity during the sampling of parameter space and it can quantify the uncertainty in the predictions of state trajectories. We couple this strategy with structural identifiability and observability analysis, and when these tests detect possible prediction issues we obtain model reparameterizations that surmount them. The end result is an ensemble of models with the ability to predict the internal dynamics of a biological process. We demonstrate our approach with models of glucose regulation, cell division, circadian oscillations, and the JAK-STAT signalling pathway.The code that implements the methodology and reproduces the results is available at https://doi.org/10.5281/zenodo.6782638Supplementary data are available at Bioinformatics online.}},
+ doi = {10.1093/bioinformatics/btac755},
+ eprint = {https://academic.oup.com/bioinformatics/advance-article-pdf/doi/10.1093/bioinformatics/btac755/47214747/btac755.pdf},
+ url = {https://doi.org/10.1093/bioinformatics/btac755},
+}
+
+@Article{RaimundezFed2022,
+ author = {Raimundez, Elba and Fedders, Michael and Hasenauer, Jan},
+ journal = {bioRxiv},
+ title = {Posterior marginalization accelerates Bayesian inference for dynamical systems},
+ year = {2022},
+ abstract = {Bayesian inference is an important method in the life and natural sciences for learning from data. It provides information about parameter uncertainties, and thereby the reliability of models and their predictions. Yet, generating representative samples from the Bayesian posterior distribution is often computationally challenging. Here, we present an approach that lowers the computational complexity of sample generation for problems with scaling, offset and noise parameters. The proposed method is based on the marginalization of the posterior distribution, which reduces the dimensionality of the sampling problem. We provide analytical results for a broad class of problems and show that the method is suitable for a large number of applications. Subsequently, we demonstrate the benefit of the approach for various application examples from the field of systems biology. We report a substantial improvement up to 50 times in the effective sample size per unit of time, in particular when applied to multi-modal posterior problems. As the scheme is broadly applicable, it will facilitate Bayesian inference in different research fields.Competing Interest StatementThe authors have declared no competing interest.},
+ doi = {10.1101/2022.12.02.518841},
+ elocation-id = {2022.12.02.518841},
+ eprint = {https://www.biorxiv.org/content/early/2022/12/03/2022.12.02.518841.full.pdf},
+ publisher = {Cold Spring Harbor Laboratory},
+ url = {https://www.biorxiv.org/content/early/2022/12/03/2022.12.02.518841},
+}
+
+@Article{AlbadryHoe2022,
+ author = {Albadry, Mohamed and Höpfl, Sebastian and Ehteshamzad, Nadia and König, Matthias and Böttcher, Michael and Neumann, Jasna and Lupp, Amelie and Dirsch, Olaf and Radde, Nicole and Christ, Bruno and Christ, Madlen and Schwen, Lars Ole and Laue, Hendrik and Klopfleisch, Robert and Dahmen, Uta},
+ journal = {Scientific Reports},
+ title = {Periportal steatosis in mice affects distinct parameters of pericentral drug metabolism},
+ year = {2022},
+ issn = {2045-2322},
+ number = {1},
+ pages = {21825},
+ volume = {12},
+ abstract = {Little is known about the impact of morphological disorders in distinct zones on metabolic zonation. It was described recently that periportal fibrosis did affect the expression of CYP proteins, a set of pericentrally located drug-metabolizing enzymes. Here, we investigated whether periportal steatosis might have a similar effect. Periportal steatosis was induced in C57BL6/J mice by feeding a high-fat diet with low methionine/choline content for either two or four weeks. Steatosis severity was quantified using image analysis. Triglycerides and CYP activity were quantified in photometric or fluorometric assay. The distribution of CYP3A4, CYP1A2, CYP2D6, and CYP2E1 was visualized by immunohistochemistry. Pharmacokinetic parameters of test drugs were determined after injecting a drug cocktail (caffeine, codeine, and midazolam). The dietary model resulted in moderate to severe mixed steatosis confined to periportal and midzonal areas. Periportal steatosis did not affect the zonal distribution of CYP expression but the activity of selected CYPs was associated with steatosis severity. Caffeine elimination was accelerated by microvesicular steatosis, whereas midazolam elimination was delayed in macrovesicular steatosis. In summary, periportal steatosis affected parameters of pericentrally located drug metabolism. This observation calls for further investigations of the highly complex interrelationship between steatosis and drug metabolism and underlying signaling mechanisms.},
+ doi = {10.1038/s41598-022-26483-6},
+ refid = {Albadry2022},
+}
+
+@Article{SundqvistSte2022,
+ author = {Sundqvist, Nicolas and Sten, Sebastian and Thompson, Peter and Andersson, Benjamin Jan and Engström, Maria and Cedersund, Gunnar},
+ journal = {PLOS Computational Biology},
+ title = {Mechanistic model for human brain metabolism and its connection to the neurovascular coupling},
+ year = {2022},
+ month = {12},
+ number = {12},
+ pages = {1-24},
+ volume = {18},
+ abstract = {The neurovascular and neurometabolic couplings (NVC and NMC) connect cerebral activity, blood flow, and metabolism. This interconnection is used in for instance functional imaging, which analyses the blood-oxygen-dependent (BOLD) signal. The mechanisms underlying the NVC are complex, which warrants a model-based analysis of data. We have previously developed a mechanistically detailed model for the NVC, and others have proposed detailed models for cerebral metabolism. However, existing metabolic models are still not fully utilizing available magnetic resonance spectroscopy (MRS) data and are not connected to detailed models for NVC. Therefore, we herein present a new model that integrates mechanistic modelling of both MRS and BOLD data. The metabolic model covers central metabolism, using a minimal set of interactions, and can describe time-series data for glucose, lactate, aspartate, and glutamate, measured after visual stimuli. Statistical tests confirm that the model can describe both estimation data and predict independent validation data, not used for model training. The interconnected NVC model can simultaneously describe BOLD data and can be used to predict expected metabolic responses in experiments where metabolism has not been measured. This model is a step towards a useful and mechanistically detailed model for cerebral blood flow and metabolism, with potential applications in both basic research and clinical applications.},
+ doi = {10.1371/journal.pcbi.1010798},
+ publisher = {Public Library of Science},
+}
+
+@Article{MeyerSoe2022,
+ author = {Kristian Meyer and Mikkel {Søes Ibsen} and Lisa Vetter-Joss and Ernst {Broberg Hansen} and Jens Abildskov},
+ journal = {Journal of Chromatography A},
+ title = {Industrial ion-exchange chromatography development using discontinuous Galerkin methods coupled with forward sensitivity analysis},
+ year = {2022},
+ issn = {0021-9673},
+ pages = {463741},
+ abstract = {In this work, a discontinuous Galerkin method coupled with forward sensitivity analysis (DG-FSA) is presented. The DG-FSA method is used to reduce computational cost required for model-based ion-exchange chromatography development using industrial load samples. As an example, the design of an anion-exchange chromatography step is considered. This step is used to purify an experimental peptide product called Protein G from Novo Nordisk A/S (Bagsvrd, Denmark). The results demonstrate, that a fourth order DG-FSA method can reduce computational cost of inverse problems by a factor ×16 compared to a second (low) order DG-FSA method. Furthermore, the fourth-order DG-FSA method enable the computation of probability distributions of optimized processing conditions given uncertainty in model parameters or inputs. This analysis is not possible within a reasonable timeframe when applying the second (low) order DG-FSA method. The design procedure facilitates the optimization of the Protein G purification step. In an experimental validation run, the productivity is increased by 70% while sacrificing 4% yield at a similar purity constraint compared to an experiment with baseline performance.},
+ doi = {10.1016/j.chroma.2022.463741},
+ keywords = {Discontinuous Galerkin method, Forward sensitivity analysis, Model calibration, Uncertainty quantification, Process optimization},
+ url = {https://www.sciencedirect.com/science/article/pii/S0021967322009323},
+}
+
+@Article{LakrisenkoSta2023,
+ author = {Lakrisenko, Polina and Stapor, Paul and Grein, Stephan and Paszkowski, Łukasz and Pathirana, Dilan and Fröhlich, Fabian and Lines, Glenn Terje and Weindl, Daniel and Hasenauer, Jan},
+ journal = {PLOS Computational Biology},
+ title = {Efficient computation of adjoint sensitivities at steady-state in ODE models of biochemical reaction networks},
+ year = {2023},
+ month = {01},
+ number = {1},
+ pages = {1-19},
+ volume = {19},
+ abstract = {Dynamical models in the form of systems of ordinary differential equations have become a standard tool in systems biology. Many parameters of such models are usually unknown and have to be inferred from experimental data. Gradient-based optimization has proven to be effective for parameter estimation. However, computing gradients becomes increasingly costly for larger models, which are required for capturing the complex interactions of multiple biochemical pathways. Adjoint sensitivity analysis has been pivotal for working with such large models, but methods tailored for steady-state data are currently not available. We propose a new adjoint method for computing gradients, which is applicable if the experimental data include steady-state measurements. The method is based on a reformulation of the backward integration problem to a system of linear algebraic equations. The evaluation of the proposed method using real-world problems shows a speedup of total simulation time by a factor of up to 4.4. Our results demonstrate that the proposed approach can achieve a substantial improvement in computation time, in particular for large-scale models, where computational efficiency is critical.},
+ creationdate = {2023-01-09T08:36:51},
+ doi = {10.1371/journal.pcbi.1010783},
+ publisher = {Public Library of Science},
+ url = {https://doi.org/10.1371/journal.pcbi.1010783},
+}
+
@Comment{jabref-meta: databaseType:bibtex;}
@Comment{jabref-meta: grouping:
diff --git a/documentation/conf.py b/documentation/conf.py
index 7be93c9c75..9ceb0317c1 100644
--- a/documentation/conf.py
+++ b/documentation/conf.py
@@ -84,43 +84,6 @@ def install_mtocpp():
raise RuntimeError('downloadAndBuildMtocpp.sh failed')
-def install_amici_deps_rtd():
- """Install AMICI dependencies and set up environment for use on RTD"""
-
- # cblas -- manually install ubuntu deb package
- cblas_root = os.path.join(amici_dir, 'ThirdParty', 'libatlas-base-dev',
- 'usr')
-
- if os.path.isdir(cblas_root):
- # If this exists, it means this has been run before. On RTD, sphinx is
- # being run several times and we don't want to reinstall dependencies
- # every time.
- return
-
- cblas_inc_dir = os.path.join(cblas_root, "include", "x86_64-linux-gnu")
- cblas_lib_dir = os.path.join(cblas_root, "lib", "x86_64-linux-gnu")
- cmd = (f"cd '{os.path.join(amici_dir, 'ThirdParty')}' "
- "&& apt download libatlas-base-dev && mkdir libatlas-base-dev "
- "&& cd libatlas-base-dev "
- "&& ar x ../libatlas-base-dev_3.10.3-8ubuntu7_amd64.deb "
- "&& tar -xJf data.tar.xz "
- f"&& ln -s {cblas_inc_dir}/cblas-atlas.h {cblas_inc_dir}/cblas.h "
- )
- subprocess.run(cmd, shell=True, check=True)
- os.environ['BLAS_CFLAGS'] = f'-I{cblas_inc_dir}'
- os.environ['BLAS_LIBS'] = (f'-L{cblas_lib_dir}/atlas -L{cblas_lib_dir} '
- '-lcblas -latlas -lblas -lm')
-
- # build swig4.0
- subprocess.run(os.path.join(amici_dir, 'scripts',
- 'downloadAndBuildSwig.sh'), check=True)
-
- # add swig to path
- swig_dir = os.path.join(amici_dir, 'ThirdParty', 'swig-4.0.2', 'install',
- 'bin')
- os.environ['SWIG'] = os.path.join(swig_dir, 'swig')
-
-
def install_doxygen():
"""Get a more recent doxygen"""
version = '1.9.6'
@@ -156,7 +119,6 @@ def install_doxygen():
# only execute those commands when running from RTD
if 'READTHEDOCS' in os.environ and os.environ['READTHEDOCS']:
- install_amici_deps_rtd()
install_doxygen()
# Required for matlab doxygen processing
@@ -164,26 +126,7 @@ def install_doxygen():
# Install AMICI if not already present
typing.TYPE_CHECKING = True
-
-try:
- import amici
-except ModuleNotFoundError:
- subprocess.run([
- 'python', '-m', 'pip', 'install', '--verbose', '-e',
- os.path.join(amici_dir, 'python', 'sdist')
- ], check=True)
-
- from importlib import invalidate_caches
-
- invalidate_caches()
-
- sys.path.insert(0, amici_dir)
- sys.path.insert(0, os.path.join(amici_dir, 'python', 'sdist'))
-
- import amici
-# Works around some cyclic dependency issue with amici.petab_import_pysb
-import amici.petab_import
-
+import amici
typing.TYPE_CHECKING = False
@@ -248,6 +191,27 @@ def install_doxygen():
'python': ('https://docs.python.org/3', None),
}
+# Add notebooks prolog with binder links
+# get current git reference
+ret = subprocess.run(
+ "git rev-parse HEAD".split(" "),
+ capture_output=True
+)
+ref = ret.stdout.rstrip().decode()
+nbsphinx_prolog = (
+ f"{{% set {ref=} %}}"
+ r"""
+ {% set docname = "documentation/" + env.doc2path(env.docname, base=False) %}
+ .. raw:: html
+
+
+
+ 
+
+
+ """
+)
+
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
diff --git a/documentation/example_errors.ipynb b/documentation/example_errors.ipynb
new file mode 120000
index 0000000000..96b3e97377
--- /dev/null
+++ b/documentation/example_errors.ipynb
@@ -0,0 +1 @@
+../python/examples/example_errors.ipynb
\ No newline at end of file
diff --git a/documentation/python_interface.rst b/documentation/python_interface.rst
index dc57f8a200..4e4c607c65 100644
--- a/documentation/python_interface.rst
+++ b/documentation/python_interface.rst
@@ -137,6 +137,9 @@ We also plan to implement support for the
Examples
========
+.. image:: https://mybinder.org/badge_logo.svg
+ :target: https://mybinder.org/v2/gh/AMICI-dev/AMICI/develop?labpath=binder%2Foverview.ipynb
+
.. toctree::
:maxdepth: 1
@@ -145,6 +148,7 @@ Examples
petab.ipynb
ExampleExperimentalConditions.ipynb
ExampleEquilibrationLogic.ipynb
+ example_errors.ipynb
Environment variables affecting model import
============================================
diff --git a/documentation/references.md b/documentation/references.md
index 2c28ebc355..027546d6bc 100644
--- a/documentation/references.md
+++ b/documentation/references.md
@@ -1,22 +1,34 @@
# References
-List of publications using AMICI. Total number is 70.
+List of publications using AMICI. Total number is 74.
If you applied AMICI in your work and your publication is missing, please let us know via a new Github issue.
2023
+
+
Lakrisenko, Polina, Paul Stapor, Stephan Grein, Łukasz Paszkowski, Dilan Pathirana, Fabian Fröhlich, Glenn Terje Lines, Daniel Weindl, and Jan Hasenauer. 2023. “Efficient Computation of Adjoint Sensitivities at Steady-State in Ode Models of Biochemical Reaction Networks.” PLOS Computational Biology 19 (1): 1–19. https://doi.org/10.1371/journal.pcbi.1010783.
+
2022
+
+
Albadry, Mohamed, Sebastian Höpfl, Nadia Ehteshamzad, Matthias König, Michael Böttcher, Jasna Neumann, Amelie Lupp, et al. 2022. “Periportal Steatosis in Mice Affects Distinct Parameters of Pericentral Drug Metabolism.” Scientific Reports 12 (1): 21825. https://doi.org/10.1038/s41598-022-26483-6.
+
Fröhlich, Fabian, Luca Gerosa, Jeremy Muhlich, and Peter K. Sorger. 2022. “Mechanistic Model of Mapk Signaling Reveals How Allostery and Rewiring Contribute to Drug Resistance.” bioRxiv. https://doi.org/10.1101/2022.02.17.480899.
-
-
Lakrisenko, Polina, Paul Stapor, Stephan Grein, Łukasz Paszkowski, Dilan Pathirana, Fabian Fröhlich, Glenn Terje Lines, Daniel Weindl, and Jan Hasenauer. 2022. “Efficient Computation of Adjoint Sensitivities at Steady-State in ODE Models of Biochemical Reaction Networks.” bioRxiv. https://doi.org/10.1101/2022.08.08.503176.
+
+
+
Meyer, Kristian, Mikkel Søes Ibsen, Lisa Vetter-Joss, Ernst Broberg Hansen, and Jens Abildskov. 2022. “Industrial Ion-Exchange Chromatography Development Using Discontinuous Galerkin Methods Coupled with Forward Sensitivity Analysis.” Journal of Chromatography A, 463741. https://doi.org/10.1016/j.chroma.2022.463741.
+
+
Schmucker, Robin, Gabriele Farina, James Faeder, Fabian Fröhlich, Ali Sinan Saglam, and Tuomas Sandholm. 2022. “Combination Treatment Optimization Using a Pan-Cancer Pathway Model.” PLOS Computational Biology 17 (12): 1–22. https://doi.org/10.1371/journal.pcbi.1009689.
@@ -27,8 +39,8 @@ If you applied AMICI in your work and your publication is missing, please let us
Stapor, Paul, Leonard Schmiester, Christoph Wierling, Simon Merkt, Dilan Pathirana, Bodo M. H. Lange, Daniel Weindl, and Jan Hasenauer. 2022. “Mini-batch optimization enables training of ODE models on large-scale datasets.” Nature Communications 13 (1): 34. https://doi.org/10.1038/s41467-021-27374-6.
-
-
Sundqvist, Nicolas, Sebastian Sten, Maria Engström, and Gunnar Cedersund. 2022. “Mechanistic Model for Human Brain Metabolism and the Neurovascular Coupling.” bioRxiv. https://doi.org/10.1101/2022.02.15.480629.
+
+
Sundqvist, Nicolas, Sebastian Sten, Peter Thompson, Benjamin Jan Andersson, Maria Engström, and Gunnar Cedersund. 2022. “Mechanistic Model for Human Brain Metabolism and Its Connection to the Neurovascular Coupling.” PLOS Computational Biology 18 (12): 1–24. https://doi.org/10.1371/journal.pcbi.1010798.
Villaverde, Alejandro F., Elba Raimúndez, Jan Hasenauer, and Julio R. Banga. 2022. “Assessment of Prediction Uncertainty Quantification Methods in Systems Biology.” IEEE/ACM Transactions on Computational Biology and Bioinformatics, 1–12. https://doi.org/10.1109/TCBB.2022.3213914.
diff --git a/documentation/rtd_requirements.txt b/documentation/rtd_requirements.txt
index 37d6f7663c..c5fe2d49a9 100644
--- a/documentation/rtd_requirements.txt
+++ b/documentation/rtd_requirements.txt
@@ -1,3 +1,4 @@
+# NOTE: relative paths are expected to be relative to the repository root
sphinx==5.1.1
mock>=4.0.3
setuptools==65.5.1
@@ -20,3 +21,6 @@ sphinxcontrib-napoleon>=0.7
pygments==2.13.0
Jinja2==3.1.2
git+https://github.com/readthedocs/readthedocs-sphinx-ext
+ipykernel
+-e git+https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab.git@master#subdirectory=src/python&egg=benchmark_models_petab
+-e python/sdist/
diff --git a/include/amici/amici.h b/include/amici/amici.h
index 3f022361c3..1201b3b1b7 100644
--- a/include/amici/amici.h
+++ b/include/amici/amici.h
@@ -20,10 +20,9 @@ namespace amici {
* @param rethrow rethrow integration exceptions?
* @return rdata pointer to return data object
*/
-std::unique_ptr
runAmiciSimulation(Solver &solver,
- const ExpData *edata,
- Model &model,
- bool rethrow = false);
+std::unique_ptr runAmiciSimulation(
+ Solver& solver, ExpData const* edata, Model& model, bool rethrow = false
+);
/**
* @brief Same as runAmiciSimulation, but for multiple ExpData instances. When
@@ -36,15 +35,15 @@ std::unique_ptr runAmiciSimulation(Solver &solver,
* @param num_threads number of threads for parallel execution
* @return vector of pointers to return data objects
*/
-std::vector>
-runAmiciSimulations(Solver const &solver, const std::vector &edatas,
- Model const &model, bool failfast, int num_threads);
-
+std::vector> runAmiciSimulations(
+ Solver const& solver, std::vector const& edatas,
+ Model const& model, bool failfast, int num_threads
+);
/**
* @brief Get the string representation of the given simulation status code
* (see ReturnData::status).
- * @param return_code
+ * @param status Status code
* @return Name of the variable representing this status code.
*/
std::string simulation_status_to_str(int status);
diff --git a/include/amici/defines.h b/include/amici/defines.h
index e8321e3714..02437f77cb 100644
--- a/include/amici/defines.h
+++ b/include/amici/defines.h
@@ -71,6 +71,7 @@ constexpr int AMICI_TOO_MUCH_ACC= -2;
constexpr int AMICI_ERR_FAILURE= -3;
constexpr int AMICI_CONV_FAILURE= -4;
constexpr int AMICI_RHSFUNC_FAIL= -8;
+constexpr int AMICI_FIRST_RHSFUNC_ERR= -9;
constexpr int AMICI_ILL_INPUT= -22;
constexpr int AMICI_ERROR= -99;
constexpr int AMICI_NO_STEADY_STATE= -81;
diff --git a/include/amici/edata.h b/include/amici/edata.h
index b15e34fc10..1f18cea991 100644
--- a/include/amici/edata.h
+++ b/include/amici/edata.h
@@ -28,7 +28,7 @@ class ExpData : public SimulationParameters {
* @brief Copy constructor, needs to be declared to be generated in
* swig
*/
- ExpData(const ExpData &) = default;
+ ExpData(ExpData const&) = default;
/**
* @brief constructor that only initializes dimensions
@@ -88,7 +88,7 @@ class ExpData : public SimulationParameters {
*
* @param model pointer to model specification object
*/
- explicit ExpData(const Model &model);
+ explicit ExpData(Model const& model);
/**
* @brief constructor that initializes with returnData, adds noise according
@@ -98,7 +98,7 @@ class ExpData : public SimulationParameters {
* @param sigma_y scalar standard deviations for all observables
* @param sigma_z scalar standard deviations for all event observables
*/
- ExpData(const ReturnData &rdata, realtype sigma_y, realtype sigma_z);
+ ExpData(ReturnData const& rdata, realtype sigma_y, realtype sigma_z);
/**
* @brief constructor that initializes with returnData, adds noise according
@@ -110,12 +110,14 @@ class ExpData : public SimulationParameters {
* @param sigma_z vector of standard deviations for event observables
* (dimension: nztrue or nmaxevent x nztrue, row-major)
*/
- ExpData(const ReturnData &rdata, std::vector sigma_y,
- std::vector sigma_z);
+ ExpData(
+ ReturnData const& rdata, std::vector sigma_y,
+ std::vector sigma_z
+ );
~ExpData() = default;
- friend inline bool operator==(const ExpData& lhs, const ExpData& rhs);
+ friend inline bool operator==(ExpData const& lhs, ExpData const& rhs);
/**
* @brief number of observables of the non-augmented model
@@ -150,7 +152,7 @@ class ExpData : public SimulationParameters {
*
* @param ts timepoints
*/
- void setTimepoints(const std::vector &ts);
+ void setTimepoints(std::vector const& ts);
/**
* @brief get function that copies data from ExpData::ts to output
@@ -173,7 +175,7 @@ class ExpData : public SimulationParameters {
*
* @param observedData observed data (dimension: nt x nytrue, row-major)
*/
- void setObservedData(const std::vector &observedData);
+ void setObservedData(std::vector const& observedData);
/**
* @brief set function that copies observed data for specific observable
@@ -181,7 +183,7 @@ class ExpData : public SimulationParameters {
* @param observedData observed data (dimension: nt)
* @param iy observed data index
*/
- void setObservedData(const std::vector &observedData, int iy);
+ void setObservedData(std::vector const& observedData, int iy);
/**
* @brief get function that checks whether data at specified indices has
@@ -208,7 +210,7 @@ class ExpData : public SimulationParameters {
*
* @return pointer to observed data at index (dimension: nytrue)
*/
- const realtype *getObservedDataPtr(int it) const;
+ realtype const* getObservedDataPtr(int it) const;
/**
* @brief set function that copies data from input to
@@ -217,7 +219,7 @@ class ExpData : public SimulationParameters {
* @param observedDataStdDev standard deviation of observed data (dimension:
* nt x nytrue, row-major)
*/
- void setObservedDataStdDev(const std::vector &observedDataStdDev);
+ void setObservedDataStdDev(std::vector const& observedDataStdDev);
/**
* @brief set function that sets all ExpData::observedDataStdDev to the
@@ -235,8 +237,9 @@ class ExpData : public SimulationParameters {
* nt)
* @param iy observed data index
*/
- void setObservedDataStdDev(const std::vector &observedDataStdDev,
- int iy);
+ void setObservedDataStdDev(
+ std::vector const& observedDataStdDev, int iy
+ );
/**
* @brief set function that sets all standard deviation of a specific
@@ -272,7 +275,7 @@ class ExpData : public SimulationParameters {
* @param it timepoint index
* @return pointer to standard deviation of observed data at index
*/
- const realtype *getObservedDataStdDevPtr(int it) const;
+ realtype const* getObservedDataStdDevPtr(int it) const;
/**
* @brief set function that copies observed event data from input to
@@ -281,7 +284,7 @@ class ExpData : public SimulationParameters {
* @param observedEvents observed data (dimension: nmaxevent x nztrue,
* row-major)
*/
- void setObservedEvents(const std::vector &observedEvents);
+ void setObservedEvents(std::vector const& observedEvents);
/**
* @brief set function that copies observed event data for specific event
@@ -290,7 +293,7 @@ class ExpData : public SimulationParameters {
* @param observedEvents observed data (dimension: nmaxevent)
* @param iz observed event data index
*/
- void setObservedEvents(const std::vector &observedEvents, int iz);
+ void setObservedEvents(std::vector const& observedEvents, int iz);
/**
* @brief get function that checks whether event data at specified indices
@@ -317,7 +320,7 @@ class ExpData : public SimulationParameters {
*
* @return pointer to observed event data at ieth occurrence
*/
- const realtype *getObservedEventsPtr(int ie) const;
+ realtype const* getObservedEventsPtr(int ie) const;
/**
* @brief set function that copies data from input to
@@ -326,7 +329,7 @@ class ExpData : public SimulationParameters {
* @param observedEventsStdDev standard deviation of observed event data
*/
void
- setObservedEventsStdDev(const std::vector &observedEventsStdDev);
+ setObservedEventsStdDev(std::vector const& observedEventsStdDev);
/**
* @brief set function that sets all ExpData::observedDataStdDev to the
@@ -344,9 +347,9 @@ class ExpData : public SimulationParameters {
* (dimension: nmaxevent)
* @param iz observed data index
*/
- void
- setObservedEventsStdDev(const std::vector &observedEventsStdDev,
- int iz);
+ void setObservedEventsStdDev(
+ std::vector const& observedEventsStdDev, int iz
+ );
/**
* @brief set function that sets all standard deviation of a specific
@@ -384,7 +387,7 @@ class ExpData : public SimulationParameters {
* @return pointer to standard deviation of observed event data at ie-th
* occurrence
*/
- const realtype *getObservedEventsStdDevPtr(int ie) const;
+ realtype const* getObservedEventsStdDevPtr(int ie) const;
/**
* @brief Arbitrary (not necessarily unique) identifier.
@@ -414,8 +417,9 @@ class ExpData : public SimulationParameters {
* @param input vector input to be checked
* @param fieldname name of the input
*/
- void checkDataDimension(std::vector const &input,
- const char *fieldname) const;
+ void checkDataDimension(
+ std::vector const& input, char const* fieldname
+ ) const;
/**
* @brief checker for dimensions of input observedEvents or
@@ -424,8 +428,9 @@ class ExpData : public SimulationParameters {
* @param input vector input to be checked
* @param fieldname name of the input
*/
- void checkEventsDimension(std::vector const &input,
- const char *fieldname) const;
+ void checkEventsDimension(
+ std::vector const& input, char const* fieldname
+ ) const;
/** @brief number of observables */
int nytrue_{0};
@@ -457,33 +462,34 @@ class ExpData : public SimulationParameters {
std::vector observed_events_std_dev_;
};
-
-inline bool operator==(const ExpData& lhs, const ExpData& rhs) {
- return *dynamic_cast< const SimulationParameters* >(&lhs)
- == *dynamic_cast< const SimulationParameters* >(&rhs)
- && lhs.id == rhs.id
- && lhs.nytrue_ == rhs.nytrue_
- && lhs.nztrue_ == rhs.nztrue_
- && lhs.nmaxevent_ == rhs.nmaxevent_
- && is_equal(lhs.observed_data_,
- rhs.observed_data_)
- && is_equal(lhs.observed_data_std_dev_,
- rhs.observed_data_std_dev_)
- && is_equal(lhs.observed_events_,
- rhs.observed_events_)
- && is_equal(lhs.observed_events_std_dev_,
- rhs.observed_events_std_dev_);
+/**
+ * @brief Equality operator
+ * @param lhs some object
+ * @param rhs another object
+ * @return `true`, if both arguments are equal; `false` otherwise.
+ */
+inline bool operator==(ExpData const& lhs, ExpData const& rhs) {
+ return *dynamic_cast(&lhs)
+ == *dynamic_cast(&rhs)
+ && lhs.id == rhs.id && lhs.nytrue_ == rhs.nytrue_
+ && lhs.nztrue_ == rhs.nztrue_ && lhs.nmaxevent_ == rhs.nmaxevent_
+ && is_equal(lhs.observed_data_, rhs.observed_data_)
+ && is_equal(lhs.observed_data_std_dev_, rhs.observed_data_std_dev_)
+ && is_equal(lhs.observed_events_, rhs.observed_events_)
+ && is_equal(
+ lhs.observed_events_std_dev_, rhs.observed_events_std_dev_
+ );
};
-
/**
* @brief checks input vector of sigmas for not strictly positive values
*
* @param sigmaVector vector input to be checked
* @param vectorName name of the input
*/
-void checkSigmaPositivity(std::vector const &sigmaVector,
- const char *vectorName);
+void checkSigmaPositivity(
+ std::vector const& sigmaVector, char const* vectorName
+);
/**
* @brief checks input scalar sigma for not strictly positive value
@@ -491,7 +497,7 @@ void checkSigmaPositivity(std::vector const &sigmaVector,
* @param sigma input to be checked
* @param sigmaName name of the input
*/
-void checkSigmaPositivity(realtype sigma, const char *sigmaName);
+void checkSigmaPositivity(realtype sigma, char const* sigmaName);
/**
* @brief The ConditionContext class applies condition-specific amici::Model
@@ -508,10 +514,11 @@ class ConditionContext : public ContextManager {
* @param fpc flag indicating which fixedParameter from edata to apply
*/
explicit ConditionContext(
- Model *model, const ExpData *edata = nullptr,
- FixedParameterContext fpc = FixedParameterContext::simulation);
+ Model* model, ExpData const* edata = nullptr,
+ FixedParameterContext fpc = FixedParameterContext::simulation
+ );
- ConditionContext &operator=(const ConditionContext &other) = delete;
+ ConditionContext& operator=(ConditionContext const& other) = delete;
~ConditionContext();
@@ -523,8 +530,7 @@ class ConditionContext : public ContextManager {
* @param edata
* @param fpc flag indicating which fixedParameter from edata to apply
*/
- void applyCondition(const ExpData *edata,
- FixedParameterContext fpc);
+ void applyCondition(ExpData const* edata, FixedParameterContext fpc);
/**
* @brief Restore original settings on constructor-supplied amici::Model.
diff --git a/include/amici/exception.h b/include/amici/exception.h
index 5f35324d41..7ee91d511b 100644
--- a/include/amici/exception.h
+++ b/include/amici/exception.h
@@ -18,8 +18,9 @@ class AmiException : public std::exception {
public:
/**
* @brief Default ctor.
+ * @param first_frame Index of first frame to include
*/
- AmiException();
+ AmiException(int const first_frame = 3);
/**
* @brief Constructor with printf style interface
@@ -43,8 +44,9 @@ class AmiException : public std::exception {
/**
* @brief Stores the current backtrace
* @param nMaxFrames number of frames to go back in stacktrace
+ * @param first_frame Index of first frame to include
*/
- void storeBacktrace(int nMaxFrames);
+ void storeBacktrace(int nMaxFrames, int const first_frame);
protected:
/**
diff --git a/include/amici/forwardproblem.h b/include/amici/forwardproblem.h
index d1aa49caa7..581361dc6c 100644
--- a/include/amici/forwardproblem.h
+++ b/include/amici/forwardproblem.h
@@ -33,8 +33,10 @@ class ForwardProblem {
* @param preeq preequilibration with which to initialize the forward problem,
* pass nullptr for no initialization
*/
- ForwardProblem(const ExpData *edata, Model *model, Solver *solver,
- const SteadystateProblem *preeq);
+ ForwardProblem(
+ ExpData const* edata, Model* model, Solver* solver,
+ SteadystateProblem const* preeq
+ );
~ForwardProblem() = default;
@@ -53,7 +55,7 @@ class ForwardProblem {
* @param model Model instance
* @param edata experimental data
*/
- void getAdjointUpdates(Model &model, const ExpData &edata);
+ void getAdjointUpdates(Model& model, ExpData const& edata);
/**
* @brief Accessor for t
@@ -221,7 +223,7 @@ class ForwardProblem {
* @param it timepoint index
* @return state
*/
- const SimulationState &getSimulationStateTimepoint(int it) const {
+ SimulationState const& getSimulationStateTimepoint(int it) const {
if (model->getTimepoint(it) == initial_state_.t)
return getInitialSimulationState();
return timepoint_states_.find(model->getTimepoint(it))->second;
@@ -233,7 +235,7 @@ class ForwardProblem {
* @param iroot event index
* @return SimulationState
*/
- const SimulationState &getSimulationStateEvent(int iroot) const {
+ SimulationState const& getSimulationStateEvent(int iroot) const {
return event_states_.at(iroot);
};
@@ -242,7 +244,7 @@ class ForwardProblem {
* initial timepoint
* @return SimulationState
*/
- const SimulationState &getInitialSimulationState() const {
+ SimulationState const& getInitialSimulationState() const {
return initial_state_;
};
@@ -251,7 +253,7 @@ class ForwardProblem {
* final timepoint (or when simulation failed)
* @return SimulationState
*/
- const SimulationState &getFinalSimulationState() const {
+ SimulationState const& getFinalSimulationState() const {
return final_state_;
};
@@ -262,7 +264,7 @@ class ForwardProblem {
Solver *solver;
/** pointer to experimental data instance */
- const ExpData *edata;
+ ExpData const* edata;
private:
@@ -293,8 +295,6 @@ class ForwardProblem {
/**
* @brief Applies the event bolus to the current state
- *
- * @param model pointer to model specification object
*/
void applyEventBolus();
@@ -451,7 +451,7 @@ class FinalStateStorer : public ContextManager {
explicit FinalStateStorer(ForwardProblem *fwd) : fwd_(fwd) {
}
- FinalStateStorer &operator=(const FinalStateStorer &other) = delete;
+ FinalStateStorer& operator=(FinalStateStorer const& other) = delete;
/**
* @brief destructor, stores simulation state
diff --git a/include/amici/misc.h b/include/amici/misc.h
index fe9fb5b8e2..91c26cffa0 100644
--- a/include/amici/misc.h
+++ b/include/amici/misc.h
@@ -45,8 +45,7 @@ gsl::span slice(std::vector &data, int index, unsigned size) {
*/
template
-gsl::span slice(const std::vector &data,
- int index, unsigned size) {
+gsl::span slice(std::vector const& data, int index, unsigned size) {
if ((index + 1) * size > data.size())
throw std::out_of_range("requested slice is out of data range");
if (size > 0)
@@ -78,7 +77,7 @@ void checkBufferSize(gsl::span buffer,
* @param buffer buffer to which values are to be written
*/
template
-void writeSlice(const gsl::span slice, gsl::span buffer) {
+void writeSlice(const gsl::span slice, gsl::span buffer) {
checkBufferSize(buffer, slice.size());
std::copy(slice.begin(), slice.end(), buffer.data());
};
@@ -89,7 +88,7 @@ void writeSlice(const gsl::span slice, gsl::span buffer) {
* @param buffer buffer to which values are to be added
*/
template
-void addSlice(const gsl::span slice, gsl::span buffer) {
+void addSlice(const gsl::span slice, gsl::span buffer) {
checkBufferSize(buffer, slice.size());
std::transform(slice.begin(), slice.end(), buffer.begin(), buffer.begin(),
std::plus());
@@ -100,8 +99,7 @@ void addSlice(const gsl::span slice, gsl::span buffer) {
* @param s computed value
* @param b buffer to which values are to be written
*/
-template
-void writeSlice(const std::vector &s, std::vector &b) {
+template void writeSlice(std::vector const& s, std::vector& b) {
writeSlice(gsl::make_span(s.data(), s.size()),
gsl::make_span(b.data(), b.size()));
};
@@ -111,8 +109,7 @@ void writeSlice(const std::vector &s, std::vector &b) {
* @param s computed value
* @param b buffer to which values are to be written
*/
-template
-void writeSlice(const std::vector &s, gsl::span b) {
+template void writeSlice(std::vector const& s, gsl::span b) {
writeSlice(gsl::make_span(s.data(), s.size()), b);
};
@@ -121,8 +118,7 @@ void writeSlice(const std::vector &s, gsl::span b) {
* @param s computed value
* @param b buffer to which values are to be written
*/
-template
-void addSlice(const std::vector &s, gsl::span b) {
+template void addSlice(std::vector const& s, gsl::span b) {
addSlice(gsl::make_span(s.data(), s.size()), b);
};
@@ -131,21 +127,21 @@ void addSlice(const std::vector &s, gsl::span b) {
* @param s computed value
* @param b buffer to which values are to be written
*/
-void writeSlice(const AmiVector &s, gsl::span b);
-
+void writeSlice(AmiVector const& s, gsl::span b);
/**
- * @brief Remove parameter scaling according to the parameter scaling in pscale
- *
- * All vectors must be of same length.
- *
- * @param bufferScaled scaled parameters
- * @param pscale parameter scaling
- * @param bufferUnscaled unscaled parameters are written to the array
- */
-void unscaleParameters(gsl::span bufferScaled,
- gsl::span pscale,
- gsl::span bufferUnscaled);
+ * @brief Remove parameter scaling according to the parameter scaling in pscale
+ *
+ * All vectors must be of same length.
+ *
+ * @param bufferScaled scaled parameters
+ * @param pscale parameter scaling
+ * @param bufferUnscaled unscaled parameters are written to the array
+ */
+void unscaleParameters(
+ gsl::span bufferScaled,
+ gsl::span pscale, gsl::span bufferUnscaled
+);
/**
* @brief Remove parameter scaling according to `scaling`
@@ -173,16 +169,18 @@ double getScaledParameter(double unscaledParameter, ParameterScaling scaling);
* @param pscale parameter scaling
* @param bufferScaled destination
*/
-void scaleParameters(gsl::span bufferUnscaled,
- gsl::span pscale,
- gsl::span bufferScaled);
+void scaleParameters(
+ gsl::span bufferUnscaled,
+ gsl::span pscale, gsl::span bufferScaled
+);
/**
* @brief Returns the current backtrace as std::string
* @param maxFrames Number of frames to include
+ * @param first_frame Index of first frame to include
* @return Backtrace
*/
-std::string backtraceString(int maxFrames);
+std::string backtraceString(int maxFrames, int const first_frame = 0);
/**
* @brief Convert std::regex_constants::error_type to string
@@ -197,7 +195,7 @@ std::string regexErrorToString(std::regex_constants::error_type err_type);
* @param ap Argument list pointer
* @return Formatted String
*/
-std::string printfToString(const char *fmt, va_list ap);
+std::string printfToString(char const* fmt, va_list ap);
/**
* @brief Generic implementation for a context manager, explicitly deletes copy
diff --git a/include/amici/steadystateproblem.h b/include/amici/steadystateproblem.h
index 030ce7317e..bb1b39e36e 100644
--- a/include/amici/steadystateproblem.h
+++ b/include/amici/steadystateproblem.h
@@ -185,7 +185,6 @@ class SteadystateProblem {
/**
* @brief Handles the computation of quadratures in adjoint mode
- * @param newtonSolver pointer to the newtonSolver solver object
* @param solver pointer to the solver object
* @param model pointer to the model object
*/
diff --git a/python/examples/example_errors.ipynb b/python/examples/example_errors.ipynb
new file mode 100644
index 0000000000..d4f207c819
--- /dev/null
+++ b/python/examples/example_errors.ipynb
@@ -0,0 +1,1001 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "6d8e8890",
+ "metadata": {},
+ "source": [
+ "# Debugging simulation failures\n",
+ "\n",
+ "**Objective:** Demonstrate common simulation failures and give some hints for interpreting, debugging, and fixing them."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "87383e84",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%matplotlib inline\n",
+ "import os\n",
+ "import amici\n",
+ "from amici.petab_import import import_petab_problem\n",
+ "from amici.petab_objective import simulate_petab, RDATAS, EDATAS\n",
+ "from amici.plotting import plot_state_trajectories, plot_jacobian\n",
+ "import petab\n",
+ "import numpy as np\n",
+ "import matplotlib.pyplot as plt\n",
+ "from pathlib import Path\n",
+ "from contextlib import suppress\n",
+ "\n",
+ "try:\n",
+ " import benchmark_models_petab\n",
+ "except ModuleNotFoundError:\n",
+ " # install `benchmark_models_petab` if necessary\n",
+ " %pip install -q -e \"git+https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab.git@master#subdirectory=src/python&egg=benchmark_models_petab\"\n",
+ " try:\n",
+ " import benchmark_models_petab\n",
+ " except ModuleNotFoundError:\n",
+ " print(\"** Please restart the kernel. **\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "46500fb0",
+ "metadata": {},
+ "source": [
+ "## Overview\n",
+ "\n",
+ "In the following, we will simulate models contained in the [PEtab Benchmark Collection](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/) to demonstrate a number of simulation failures to analyze and fix them. We use the PEtab format, as it makes model import and simulation much easier, but everything illustrated here, also applies to plain SBML or PySB import.\n",
+ "\n",
+ "Note that, due to numerical issues, the examples below may not be fully reproducible on every system.\n",
+ "\n",
+ "If any simulation failures occur, they will be printed via Python logging. \n",
+ "\n",
+ "Programmatically, simulation success can be checked via `ReturnDataView.status`. In case of a successful simulation, and only then, this value corresponds to `amici.AMICI_SUCCESS`.\n",
+ "In case of a simulation error, all quantities in `ReturnData`/`ReturnDataView` will be reported up to the time of failure, the rest will be `NaN`. The likelihood and it's gradient will always be `NaN` in case of failure."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0ad882ac",
+ "metadata": {},
+ "source": [
+ "## `AMICI_TOO_MUCH_WORK` - `mxstep steps taken before reaching tout`\n",
+ "\n",
+ "Let's run a simulation:\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ae3cb45e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "petab_problem = benchmark_models_petab.get_problem(\"Fujita_SciSignal2010\")\n",
+ "amici_model = import_petab_problem(petab_problem, verbose=False, force_compile=False)\n",
+ "\n",
+ "np.random.seed(2991)\n",
+ "problem_parameters = dict(\n",
+ " zip(\n",
+ " petab_problem.x_free_ids,\n",
+ " petab_problem.sample_parameter_startpoints(n_starts=1)[0],\n",
+ " )\n",
+ ")\n",
+ "res = simulate_petab(\n",
+ " petab_problem=petab_problem, \n",
+ " amici_model=amici_model,\n",
+ " problem_parameters=problem_parameters,\n",
+ " scaled_parameters=True\n",
+ ")\n",
+ "print(\"Status:\", [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]])\n",
+ "assert [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]] == ['AMICI_SUCCESS', 'AMICI_SUCCESS', 'AMICI_SUCCESS', 'AMICI_TOO_MUCH_WORK', 'AMICI_SUCCESS', 'AMICI_SUCCESS']"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e75a6dcc",
+ "metadata": {},
+ "source": [
+ "**What happened?**\n",
+ "\n",
+ "AMICI failed to integrate the forward problem. The problem occurred for only one simulation condition, `condition_step_03_0`. The issue occurred at $t = 3031.8$, where the CVODES reached the maximum number of steps.\n",
+ "\n",
+ "**How to address?**\n",
+ "\n",
+ "The number of steps the solver has to take is closely related to the chosen error tolerance. More accurate results, more steps. Therefore, this problem can be solved in two ways:\n",
+ "\n",
+ "1. Increasing the maximum number of steps via [amici.Solver.setMaxSteps](https://amici.readthedocs.io/en/latest/generated/amici.amici.Solver.html#amici.amici.Solver.setMaxSteps). Note that this will increase the time required for simulation, and that simulation may still fail eventually. Sometimes it may be preferable to not increase this limit but rather fail fast. Also note that increasing the number of allowed steps increase RAM requirements (even if fewer steps are actually taken), so don't set this to ridiculously large values in order to avoid this error.\n",
+ "\n",
+ "2. Reducing the number of steps CVODES has to take. This is determined by the required error tolerance. There are various solver error tolerances than can be adjusted. The most relevant ones are those controlled via [amici.Solver.setRelativeTolerance()](https://amici.readthedocs.io/en/latest/generated/amici.amici.Solver.html#amici.amici.Solver.setRelativeTolerance) and [amici.Solver.setAbsoluteTolerance()](https://amici.readthedocs.io/en/latest/generated/amici.amici.Solver.html#amici.amici.Solver.setAbsoluteTolerance).\n",
+ "\n",
+ "So, let's fix that:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e9667f55",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# let's increase the allowed number of steps by 10x:\n",
+ "print(\"Increasing allowed number of steps ...\")\n",
+ "amici_solver = amici_model.getSolver()\n",
+ "amici_solver.setMaxSteps(10 * amici_solver.getMaxSteps())\n",
+ "\n",
+ "res = simulate_petab(\n",
+ " petab_problem=petab_problem, \n",
+ " amici_model=amici_model,\n",
+ " problem_parameters=problem_parameters,\n",
+ " scaled_parameters=True,\n",
+ " solver=amici_solver\n",
+ ")\n",
+ "\n",
+ "print(\"Status:\", [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]])\n",
+ "assert all(rdata.status == amici.AMICI_SUCCESS for rdata in res[RDATAS])\n",
+ "print(\"Simulations finished succesfully.\")\n",
+ "print()\n",
+ "\n",
+ "\n",
+ "# let's relax the relative error tolerance by a factor of 50\n",
+ "print(\"Relaxing relative error tolerance ...\")\n",
+ "amici_solver = amici_model.getSolver()\n",
+ "amici_solver.setRelativeTolerance(50 * amici_solver.getRelativeTolerance())\n",
+ "\n",
+ "res = simulate_petab(\n",
+ " petab_problem=petab_problem, \n",
+ " amici_model=amici_model,\n",
+ " problem_parameters=problem_parameters,\n",
+ " scaled_parameters=True,\n",
+ " solver=amici_solver\n",
+ ")\n",
+ "print(\"Status:\", [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]])\n",
+ "assert all(rdata.status == amici.AMICI_SUCCESS for rdata in res[RDATAS])\n",
+ "print(\"Simulations finished succesfully.\")\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "18fd3fa6",
+ "metadata": {},
+ "source": [
+ "## `Internal t = [...] and h = [...] are such that t + h = t on the next step`\n",
+ "\n",
+ "Let's run a simulation:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f78179dc",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "petab_problem = benchmark_models_petab.get_problem(\"Crauste_CellSystems2017\")\n",
+ "amici_model = import_petab_problem(petab_problem, verbose=False)\n",
+ "\n",
+ "np.random.seed(1)\n",
+ "problem_parameters = dict(\n",
+ " zip(\n",
+ " petab_problem.x_free_ids,\n",
+ " petab_problem.sample_parameter_startpoints(n_starts=1)[0],\n",
+ " )\n",
+ ")\n",
+ "res = simulate_petab(\n",
+ " petab_problem=petab_problem, \n",
+ " amici_model=amici_model,\n",
+ " problem_parameters=problem_parameters,\n",
+ " scaled_parameters=True\n",
+ ")\n",
+ "print(\"Status:\", [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]])\n",
+ "assert [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]] == ['AMICI_TOO_MUCH_WORK']"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "53e4b822",
+ "metadata": {},
+ "source": [
+ "**What happened?**\n",
+ "\n",
+ "The forward simulation failed because AMICI the solver exceeded the maximum number of steps. Unlike in the previous case of `mxstep steps taken before reaching tout` (see above), here we got several additional warnings that the current step size $h$ is numerically zero.\n",
+ "\n",
+ "**How to address?**\n",
+ "\n",
+ "The warning `Internal t = [...] and h = [...] are such that t + h = t on the next step` tells us that the solver is not able to move forward. The solver may be able to recover from that, but not always.\n",
+ "\n",
+ "Let's look at the state trajectories to see what's going on. Such a tiny step size is usually related to very fast dynamics. We repeat the simulation with additional timepoints before the point of failure:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6a6794d3",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Create a copy of this simulation condition\n",
+ "edata = amici.ExpData(res[EDATAS][0])\n",
+ "edata.setTimepoints(np.linspace(0, 0.33011, 5000))\n",
+ "amici_solver = amici_model.getSolver()\n",
+ "rdata = amici.runAmiciSimulation(amici_model, amici_solver, edata)\n",
+ "\n",
+ "# Visualize state trajectories\n",
+ "plot_state_trajectories(rdata, model=amici_model)\n",
+ "plt.yscale(\"log\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a7fe6d08",
+ "metadata": {},
+ "source": [
+ "We can see a steep increase for `Pathogen` just before the error occurs. Let's zoom in:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6a645efb",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "plt.plot(rdata.t, rdata.by_id(\"Pathogen\"))\n",
+ "plt.xlabel(\"time\")\n",
+ "plt.ylabel(\"Pathogen\");"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b85cd813",
+ "metadata": {},
+ "source": [
+ "The solver is unable to handle such a steep increase. There is not much we can do. Increasing the tolerances will let the solver proceed a bit further, but this is usually not enough. Most likely there is a problem in the model or in the choice of parameter values."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5afbd242",
+ "metadata": {},
+ "source": [
+ "## `the error test failed repeatedly or with |h| = hmin`\n",
+ "\n",
+ "Let's run a simulation:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "bb8910f2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "petab_problem = benchmark_models_petab.get_problem(\"Fujita_SciSignal2010\")\n",
+ "amici_model = import_petab_problem(petab_problem, verbose=False)\n",
+ "\n",
+ "np.random.seed(4920)\n",
+ "problem_parameters = dict(\n",
+ " zip(\n",
+ " petab_problem.x_free_ids,\n",
+ " petab_problem.sample_parameter_startpoints(n_starts=1)[0],\n",
+ " )\n",
+ ")\n",
+ "res = simulate_petab(\n",
+ " petab_problem=petab_problem, \n",
+ " amici_model=amici_model,\n",
+ " problem_parameters=problem_parameters,\n",
+ " scaled_parameters=True\n",
+ ")\n",
+ "print(\"Status:\", [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]])\n",
+ "\n",
+ "assert [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]] == ['AMICI_SUCCESS', 'AMICI_ERR_FAILURE', 'AMICI_SUCCESS', 'AMICI_SUCCESS', 'AMICI_SUCCESS', 'AMICI_SUCCESS']"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0b3a9904",
+ "metadata": {},
+ "source": [
+ "**What happened?**\n",
+ "\n",
+ "AMICI failed to integrate the forward problem. The problem occurred for only one simulation condition, `condition_step_00_3`. The issue occurred at $t = 429.232$, where the error test failed.\n",
+ "This means, the solver is unable to take a step of non-zero size without violating the choosen error tolerances."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c16ac6c8",
+ "metadata": {},
+ "source": [
+ "**How to address?**\n",
+ "\n",
+ "The step size is computed based on the Jacobian. Inspecting `ReturnData.J` shows us that we have rather large values in the Jacobian:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "467c3d36",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "rdata = res[RDATAS][1]\n",
+ "\n",
+ "# Show Jacobian as heatmap\n",
+ "plot_jacobian(rdata)\n",
+ "\n",
+ "print(f\"largest absolute Jacobian value: {np.max(np.abs(rdata.J)):.3g}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1f6ec0e3",
+ "metadata": {},
+ "source": [
+ "In this case, the default relative error tolerance may be too high and lead too large absolute errors. \n",
+ "\n",
+ "Let's retry simulation using stricter tolerances:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "600ae826",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# set stricter relative error tolerance\n",
+ "amici_solver = amici_model.getSolver()\n",
+ "amici_solver.setRelativeTolerance(amici_solver.getRelativeTolerance() / 10)\n",
+ "\n",
+ "res = simulate_petab(\n",
+ " petab_problem=petab_problem, \n",
+ " amici_model=amici_model,\n",
+ " problem_parameters=problem_parameters,\n",
+ " scaled_parameters=True,\n",
+ " solver=amici_solver\n",
+ ")\n",
+ "print(\"Status:\", [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]])\n",
+ "assert all(rdata.status == amici.AMICI_SUCCESS for rdata in res[RDATAS])\n",
+ "print(\"Simulations finished succesfully.\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "616710b6",
+ "metadata": {},
+ "source": [
+ "## `Cvode routine CVode returned a root after reinitialization`\n",
+ "\n",
+ "Let's run a simulation:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "cec31332",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "petab_problem = benchmark_models_petab.get_problem(\"Weber_BMC2015\")\n",
+ "amici_model = import_petab_problem(petab_problem, verbose=False, force_compile=False)\n",
+ "\n",
+ "np.random.seed(4)\n",
+ "problem_parameters = dict(\n",
+ " zip(\n",
+ " petab_problem.x_free_ids,\n",
+ " petab_problem.sample_parameter_startpoints(n_starts=1)[0],\n",
+ " )\n",
+ ")\n",
+ "res = simulate_petab(\n",
+ " petab_problem=petab_problem, \n",
+ " amici_model=amici_model,\n",
+ " problem_parameters=problem_parameters,\n",
+ " scaled_parameters=True\n",
+ ")\n",
+ "print(\"Status:\", [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]])\n",
+ "assert [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]] == ['AMICI_ERROR', 'AMICI_SUCCESS']"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d4386603",
+ "metadata": {},
+ "source": [
+ "**What happened?**\n",
+ "\n",
+ "The simulation failed because the initial step-size after an event or heaviside function was too small. The error occured during simulation of condition `model1_data1` after successful preequilibration (`model1_data2`)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "02f2dd5d",
+ "metadata": {},
+ "source": [
+ "**How to address?**\n",
+ "\n",
+ "The error message already suggests a fix for this situation, so let's try increasing the relative tolerance:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "3d1552e3",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "amici_solver = amici_model.getSolver()\n",
+ "amici_solver.setRelativeTolerance(200 * amici_solver.getRelativeTolerance())\n",
+ "\n",
+ "np.random.seed(4)\n",
+ "problem_parameters = dict(\n",
+ " zip(\n",
+ " petab_problem.x_free_ids,\n",
+ " petab_problem.sample_parameter_startpoints(n_starts=1)[0],\n",
+ " )\n",
+ ")\n",
+ "res = simulate_petab(\n",
+ " petab_problem=petab_problem, \n",
+ " amici_model=amici_model,\n",
+ " problem_parameters=problem_parameters,\n",
+ " scaled_parameters=True,\n",
+ " solver=amici_solver\n",
+ ")\n",
+ "print(\"Status:\", [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]])\n",
+ "assert all(rdata.status == amici.AMICI_SUCCESS for rdata in res[RDATAS])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "86f4db29",
+ "metadata": {},
+ "source": [
+ "## `AMICI encountered a NaN / Inf value for [...]`\n",
+ "\n",
+ "Let's run a simulation:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d97349ff",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "petab_problem = benchmark_models_petab.get_problem(\"Borghans_BiophysChem1997\")\n",
+ "amici_model = import_petab_problem(petab_problem, verbose=False)\n",
+ "\n",
+ "np.random.seed(18)\n",
+ "problem_parameters = dict(\n",
+ " zip(\n",
+ " petab_problem.x_free_ids,\n",
+ " petab_problem.sample_parameter_startpoints(n_starts=1)[0],\n",
+ " )\n",
+ ")\n",
+ "res = simulate_petab(\n",
+ " petab_problem=petab_problem, \n",
+ " amici_model=amici_model,\n",
+ " problem_parameters=problem_parameters,\n",
+ " scaled_parameters=True\n",
+ ")\n",
+ "print(\"Status:\", [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]])\n",
+ "assert [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]] == ['AMICI_FIRST_RHSFUNC_ERR']"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "63641cff",
+ "metadata": {},
+ "source": [
+ "**What happened?**\n",
+ "\n",
+ "The forward simulation failed because AMICI encountered a `NaN` value when simulating condition `model1_data1`.\n",
+ "Then `NaN`s occurred in $\\dot x$ and $w$ (model expressions, such as reaction fluxes or assignment rules). Furthermore, the failure occurred at the first call, so at $t = t_0$ (here: $t = 0$).\n",
+ "\n",
+ "**How to address?**\n",
+ "\n",
+ "The `NaN` in $\\dot x$ is most likely a consequence of the one in $w$. (A subset of) the dependency tree looks something like:\n",
+ "\n",
+ "[](https://mermaid.live/edit#pako:eNpdkrFuwyAQhl8FIWVL1MwMndKFtd1wBmJIg2IDwucCivLuxQp2zvaA_P1398Od7kFbpzRl9DdIfyM_p8aS8t3FnZHW2QGkheH8Er3wjHgZZK9Bh1kFAYyA6XXldBTpyIixBozsSHGAJSQSWwlccEa4bN3FSFu1KCIjOvmgh8GUF8y1yoGYDkZU-lBQ5SwyI-4y6PAnL52esl-B3a68pDZDDofPhfyKYEVLacSVERdGXFchzYAu59iBYweOHTh2qBAxvLupI3s9eJ41pndqGdOqvYXThv2G7xuOiBf7jHMzNsr41oyvzNgv0z3tdeilUWXzHlOooXDTvW4oK79KX-XYQUMb-yypcgT3nW1LGYRR7-noVVmhk5FlZ3vKrrIbFvVLGXChis9_j9jNUw)\n",
+ "\n",
+ "Always look for the most basic (furthest up) model quantities.\n",
+ "In cases where there non-finite values occur in expressions further down, rerunning the simulation after calling `Model.setAlwaysCheckFinite(True)` may give some further hints on where the issue originates.\n",
+ "\n",
+ "The `NaN` in $w$ occurred for `flux_v7_v_6` (see error log), i.e., when computing the reaction flux for reaction `v7_v_6`. As $w$ only depends on $(t, p, k, x)$ and no non-finite values have been reported for those, the issue has to be in the respective flux equation.\n",
+ "\n",
+ "Let's look at that expression. This can either be done by inspecting the underlying SBML model (e.g., using COPASI), or by checking the generated model code:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "fca95143",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# model name and source code location\n",
+ "model_name = amici_model.module.__package__\n",
+ "model_src_dir = Path(amici_model.module.__file__).parents[1]\n",
+ "\n",
+ "# find the problematic expression in the model source code\n",
+ "!grep flux_v7_v_6 {model_src_dir}/{model_name}_w.cpp"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9f49a00a",
+ "metadata": {},
+ "source": [
+ "What could go wrong? We can obtain `NaN` from any of these symbols symbols being `NaN`, or through division by zero.\n",
+ "\n",
+ "Let's let's check the denominator first: $$(A\\_state^2 + Kp^2)*(Kd^{n\\_par} + Z\\_state^{n\\_par})$$\n",
+ "\n",
+ "\n",
+ "`A_state` and `Z_state` are state variables, `Kd`, `K_p`, and `n_par` are parameters.\n",
+ "\n",
+ "As the error occurred at $t = t_0$, let's ensure the initial state is non-zero and finite:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "0eec6fe8",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "rdata = res[RDATAS][0]\n",
+ "edata = res[EDATAS][0]\n",
+ "# check initial states\n",
+ "x0 = dict(zip(amici_model.getStateIds(), rdata.x0))\n",
+ "print(f\"{x0=}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "050c4c38",
+ "metadata": {},
+ "source": [
+ "The initial states are fine - the first multiplicand is non-zero, as $x_0$ was non-zero. \n",
+ "\n",
+ "So let's check the parameter values occurring in the second multiplicand:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ec724ce9",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# we have to account for the chosen parameter scale\n",
+ "from itertools import starmap\n",
+ "unscaled_parameter = dict(zip(\n",
+ " amici_model.getParameterIds(),\n",
+ " starmap(amici.getUnscaledParameter, zip(edata.parameters, edata.pscale)),\n",
+ "))\n",
+ "print(dict((p, unscaled_parameter[p]) for p in ('Kd', 'Kp', 'n_par')))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "62d82971",
+ "metadata": {},
+ "source": [
+ "Considering that `n_par` occurrs as exponent, it's magnitude looks pretty high.\n",
+ "This term is very likely causing the problem - let's check:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "9f3f8bdb",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(f\"{x0['Z_state']**unscaled_parameter['n_par'] + unscaled_parameter['Kd']**unscaled_parameter['n_par']=}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6616c2ff",
+ "metadata": {},
+ "source": [
+ "Indeed, no way we can fix this for the given model.\n",
+ "This was most likely an unrealistic parameter value, originating from a too high upper parameter bound for `n_par`.\n",
+ "Therefore, if this error occurs during optimization, a first step could be adapting the respective parameter bounds.\n",
+ "In other cases, this may be a result of unfortunate arrangement of model expressions, which can sometimes be solved by passing a suitable simplification function to the model import."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "22cfbbbf",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "## `Steady state sensitivity computation failed due to unsuccessful factorization of RHS Jacobian`\n",
+ "\n",
+ "Let's run a simulation:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b41a5017",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "petab_problem = benchmark_models_petab.get_problem(\"Blasi_CellSystems2016\")\n",
+ "with suppress(KeyError):\n",
+ " del os.environ[\"AMICI_EXPERIMENTAL_SBML_NONCONST_CLS\"]\n",
+ "amici_model = import_petab_problem(\n",
+ " petab_problem, \n",
+ " verbose=False,\n",
+ " force_compile=True,\n",
+ " model_name=\"Blasi_CellSystems2016_1\"\n",
+ ")\n",
+ "\n",
+ "amici_solver = amici_model.getSolver()\n",
+ "amici_solver.setSensitivityMethod(amici.SensitivityMethod.forward)\n",
+ "amici_solver.setSensitivityOrder(amici.SensitivityOrder.first)\n",
+ "\n",
+ "np.random.seed(2020)\n",
+ "problem_parameters = dict(\n",
+ " zip(\n",
+ " petab_problem.x_free_ids,\n",
+ " petab_problem.sample_parameter_startpoints(n_starts=1)[0],\n",
+ " )\n",
+ ")\n",
+ "res = simulate_petab(\n",
+ " petab_problem=petab_problem, \n",
+ " amici_model=amici_model,\n",
+ " problem_parameters=problem_parameters,\n",
+ " scaled_parameters=True,\n",
+ " solver=amici_solver,\n",
+ ")\n",
+ "print(\"Status:\", [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]])\n",
+ "\n",
+ "# hard to reproduce on GHA\n",
+ "if os.getenv('GITHUB_ACTIONS') is None:\n",
+ " assert [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]] == ['AMICI_ERROR']"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "82267e47",
+ "metadata": {},
+ "source": [
+ "**What happened?**\n",
+ "\n",
+ "AMICI failed to compute steadystate sensitivities, because it was not able to factorize the Jacobian.\n",
+ "\n",
+ "**How to address?**\n",
+ "\n",
+ "This is most likely a result of a singular Jacobian. Let's check the condition number:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8cd349b7",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "rdata = res[RDATAS][0]\n",
+ "np.linalg.cond(rdata.J)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "da131d3e",
+ "metadata": {},
+ "source": [
+ "Indeed, the condition number shows that the Jacobian is numerically singular. If this happens consistently, it is usually due to conserved quantities in the model.\n",
+ "\n",
+ "There are two ways we can address that:\n",
+ "\n",
+ "1. Use numerical integration to compute sensitivities, for which a singular Jacobian is not an issue. This is, usually, slower, though.\n",
+ "2. Remove any conserved quantities.\n",
+ "\n",
+ "Let's try both approaches:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f82078e7",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# use numerical integration\n",
+ "amici_model.setSteadyStateSensitivityMode(amici.SteadyStateSensitivityMode.integrationOnly)\n",
+ "\n",
+ "res = simulate_petab(\n",
+ " petab_problem=petab_problem, \n",
+ " amici_model=amici_model,\n",
+ " problem_parameters=problem_parameters,\n",
+ " scaled_parameters=True,\n",
+ " solver=amici_solver,\n",
+ ")\n",
+ "print(\"Status:\", [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]])\n",
+ "assert all(rdata.status == amici.AMICI_SUCCESS for rdata in res[RDATAS])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8d7be541",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Remove conserved quantities - this requires re-importing the model\n",
+ "\n",
+ "# this is enabled by the `AMICI_EXPERIMENTAL_SBML_NONCONST_CLS` environment variable\n",
+ "os.environ[\"AMICI_EXPERIMENTAL_SBML_NONCONST_CLS\"] = \"1\"\n",
+ "amici_model = import_petab_problem(\n",
+ " petab_problem, \n",
+ " verbose=False,\n",
+ " # we need a different model name if we import the model again\n",
+ " # we cannot load a model with the same name as an already loaded model\n",
+ " model_name=\"Blasi_CellSystems2016_2\",\n",
+ " force_compile=True,\n",
+ ")\n",
+ "del os.environ[\"AMICI_EXPERIMENTAL_SBML_NONCONST_CLS\"]\n",
+ "\n",
+ "amici_solver = amici_model.getSolver()\n",
+ "amici_solver.setSensitivityMethod(amici.SensitivityMethod.forward)\n",
+ "amici_solver.setSensitivityOrder(amici.SensitivityOrder.first)\n",
+ "\n",
+ "res = simulate_petab(\n",
+ " petab_problem=petab_problem, \n",
+ " amici_model=amici_model,\n",
+ " problem_parameters=problem_parameters,\n",
+ " scaled_parameters=True,\n",
+ " solver=amici_solver,\n",
+ ")\n",
+ "print(\"Status:\", [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]])\n",
+ "assert all(rdata.status == amici.AMICI_SUCCESS for rdata in res[RDATAS])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4b977c0b",
+ "metadata": {},
+ "source": [
+ "## `Steady state computation failed`\n",
+ "\n",
+ "Let's run a simulation:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "97f797dd",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "petab_problem = benchmark_models_petab.get_problem(\"Brannmark_JBC2010\")\n",
+ "amici_model = import_petab_problem(\n",
+ " petab_problem, \n",
+ " verbose=False,\n",
+ ")\n",
+ "\n",
+ "amici_solver = amici_model.getSolver()\n",
+ "\n",
+ "\n",
+ "np.random.seed(1851)\n",
+ "problem_parameters = dict(\n",
+ " zip(\n",
+ " petab_problem.x_free_ids,\n",
+ " petab_problem.sample_parameter_startpoints(n_starts=1)[0],\n",
+ " )\n",
+ ")\n",
+ "res = simulate_petab(\n",
+ " petab_problem=petab_problem, \n",
+ " amici_model=amici_model,\n",
+ " problem_parameters=problem_parameters,\n",
+ " scaled_parameters=True,\n",
+ " solver=amici_solver,\n",
+ ")\n",
+ " \n",
+ "print(\"Status:\", [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]])\n",
+ "\n",
+ "# hard to reproduce on GHA\n",
+ "if os.getenv('GITHUB_ACTIONS') is None:\n",
+ " assert [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]] == ['AMICI_ERROR', 'AMICI_SUCCESS', 'AMICI_SUCCESS', 'AMICI_SUCCESS', 'AMICI_SUCCESS', 'AMICI_SUCCESS', 'AMICI_SUCCESS', 'AMICI_SUCCESS']"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0713b830",
+ "metadata": {},
+ "source": [
+ "**What happened?**\n",
+ "\n",
+ "All given experimental conditions require pre-equilibration, i.e., finding a steady state. AMICI first tries find a steady state using the Newton solver, if that fails, it tries simulating until steady state, if that also failes, it tries the Newton solver from the end of the simulation. In this case, all three failed. Neither Newton's method nor simulation yielded a steadystate satisfying the required tolerances.\n",
+ "\n",
+ "This can also be seen in `ReturnDataView.preeq_status` (the three statuses corresponds to Newton \\#1, Simulation, Newton \\#2):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ffdc8e82",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "rdata = res[RDATAS][0]\n",
+ "list(map(amici.SteadyStateStatus, rdata.preeq_status.flatten()))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "189dd964",
+ "metadata": {},
+ "source": [
+ "**How to address?**\n",
+ "\n",
+ "There are several ways to address that:\n",
+ "\n",
+ "1. Stricter integration tolerances (preferred if affordable - higher accuracy, but generally slower)\n",
+ "\n",
+ "2. Looser steadystate tolerances (lower accuracy, generally faster)\n",
+ "\n",
+ "3. Increase the number of allowed steps for Newton's method\n",
+ "\n",
+ "Let's try all of them:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "28fada9f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Reduce relative tolerance for integration\n",
+ "amici_solver = amici_model.getSolver()\n",
+ "amici_solver.setRelativeTolerance(1/100 * amici_solver.getRelativeTolerance())\n",
+ "\n",
+ "res = simulate_petab(\n",
+ " petab_problem=petab_problem, \n",
+ " amici_model=amici_model,\n",
+ " problem_parameters=problem_parameters,\n",
+ " scaled_parameters=True,\n",
+ " solver=amici_solver,\n",
+ ")\n",
+ "print(\"status:\", [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]])\n",
+ "\n",
+ "rdata = res[RDATAS][0]\n",
+ "print(f\"preeq_status={list(map(amici.SteadyStateStatus, rdata.preeq_status.flatten()))}\")\n",
+ "print(f\"{rdata.preeq_numsteps=}\")\n",
+ "\n",
+ "# hard to reproduce on GHA\n",
+ "if os.getenv('GITHUB_ACTIONS') is None:\n",
+ " assert all(rdata.status == amici.AMICI_SUCCESS for rdata in res[RDATAS])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a6d34467",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Increase relative steady state tolerance\n",
+ "for log10_relaxation_factor in range(1, 10):\n",
+ " print(f\"Relaxing tolerances by factor {10 ** log10_relaxation_factor}\")\n",
+ " amici_solver = amici_model.getSolver()\n",
+ " amici_solver.setRelativeToleranceSteadyState(amici_solver.getRelativeToleranceSteadyState() * 10 ** log10_relaxation_factor)\n",
+ " \n",
+ " res = simulate_petab(\n",
+ " petab_problem=petab_problem, \n",
+ " amici_model=amici_model,\n",
+ " problem_parameters=problem_parameters,\n",
+ " scaled_parameters=True,\n",
+ " solver=amici_solver,\n",
+ " )\n",
+ " if all(rdata.status == amici.AMICI_SUCCESS for rdata in res[RDATAS]):\n",
+ " print(f\"-> Succeeded with relative steady state tolerance {amici_solver.getRelativeToleranceSteadyState()}\\n\")\n",
+ " break\n",
+ " else:\n",
+ " print(\"-> Failed.\\n\")\n",
+ "\n",
+ "print(\"status:\", [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]])\n",
+ "\n",
+ "rdata = res[RDATAS][0]\n",
+ "print(f\"preeq_status={list(map(amici.SteadyStateStatus, rdata.preeq_status.flatten()))}\")\n",
+ "print(f\"{rdata.preeq_numsteps=}\")\n",
+ "assert all(rdata.status == amici.AMICI_SUCCESS for rdata in res[RDATAS])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6d1b1835",
+ "metadata": {},
+ "source": [
+ "That fixed the error, and took only a quarter of the number steps as the previous run, but at the cost of much lower accuracy."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "df1ee3fc",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Let's try increasing the number of Newton steps\n",
+ "# (this is 0 by default, so the Newton solver wasn't used before, \n",
+ "# as can be seen from the 0 in `rdata.preeq_numsteps[0]`)\n",
+ "amici_solver = amici_model.getSolver()\n",
+ "amici_solver.setNewtonMaxSteps(10**4)\n",
+ "\n",
+ "res = simulate_petab(\n",
+ " petab_problem=petab_problem, \n",
+ " amici_model=amici_model,\n",
+ " problem_parameters=problem_parameters,\n",
+ " scaled_parameters=True,\n",
+ " solver=amici_solver,\n",
+ ")\n",
+ "print(\"status:\", [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]])\n",
+ "\n",
+ "rdata = res[RDATAS][0]\n",
+ "print(f\"preeq_status={list(map(amici.SteadyStateStatus, rdata.preeq_status.flatten()))}\")\n",
+ "print(f\"{rdata.preeq_numsteps=}\")\n",
+ "# hard to reproduce on GHA\n",
+ "if os.getenv('GITHUB_ACTIONS') is None:\n",
+ " assert [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]] == ['AMICI_ERROR', 'AMICI_SUCCESS', 'AMICI_SUCCESS', 'AMICI_SUCCESS', 'AMICI_SUCCESS', 'AMICI_SUCCESS', 'AMICI_SUCCESS', 'AMICI_SUCCESS']"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5c746982",
+ "metadata": {},
+ "source": [
+ "Increasing the maximum number of Newton steps doesn't seem to help here. The Jacobian was numerically singular and its factorization failed. This can be a result of conserved quantities in the model. Section [Steady state sensitivity computation failed due to unsuccessful factorization of RHS Jacobian](#unsuccessful_factorization) shows how to address that."
+ ]
+ }
+ ],
+ "metadata": {
+ "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"
+ },
+ "toc": {
+ "base_numbering": 1,
+ "nav_menu": {},
+ "number_sections": true,
+ "sideBar": true,
+ "skip_h1_title": false,
+ "title_cell": "Table of Contents",
+ "title_sidebar": "Contents",
+ "toc_cell": false,
+ "toc_position": {
+ "height": "calc(100% - 180px)",
+ "left": "10px",
+ "top": "150px",
+ "width": "335.6px"
+ },
+ "toc_section_display": true,
+ "toc_window_display": true
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/python/sdist/amici/__init__.py b/python/sdist/amici/__init__.py
index 94acc73df1..a55bfd5115 100644
--- a/python/sdist/amici/__init__.py
+++ b/python/sdist/amici/__init__.py
@@ -120,14 +120,19 @@ def _imported_from_setup() -> bool:
from .sbml_import import SbmlImporter, assignmentRules2observables
from .ode_export import ODEModel, ODEExporter
- from typing import Protocol
+ from typing import Protocol, runtime_checkable
+ @runtime_checkable
class ModelModule(Protocol):
"""Enable Python static type checking for AMICI-generated model
modules"""
+
def getModel(self) -> amici.Model:
- pass
+ ...
+
+ def get_model(self) -> amici.Model:
+ ...
class add_path:
diff --git a/python/sdist/amici/__init__.template.py b/python/sdist/amici/__init__.template.py
index 9fbab85003..bc83c1d4c9 100644
--- a/python/sdist/amici/__init__.template.py
+++ b/python/sdist/amici/__init__.template.py
@@ -14,6 +14,7 @@
'version currently installed.'
)
-from TPL_MODELNAME._TPL_MODELNAME import *
+from .TPL_MODELNAME import *
+from .TPL_MODELNAME import getModel as get_model
__version__ = 'TPL_PACKAGE_VERSION'
diff --git a/python/sdist/amici/logging.py b/python/sdist/amici/logging.py
index eae753b29e..f115a1b2e7 100644
--- a/python/sdist/amici/logging.py
+++ b/python/sdist/amici/logging.py
@@ -187,16 +187,19 @@ def wrapper_timer(*args, **kwargs):
)
recursion = ''
+ level = logging.INFO
if recursion_level > 1:
recursion = '+' * (recursion_level - 1)
+ level = logging.DEBUG
tstart = time.perf_counter()
rval = func(*args, **kwargs)
tend = time.perf_counter()
spacers = ' ' * max(54 - len(description) - len(logger.name) -
len(recursion), 0)
- logger.info(f'Finished {description}{spacers}'
- f'{recursion} ({(tend - tstart):.2E}s)')
+ logger.log(
+ level, f'Finished {description}{spacers}{recursion} ({(tend - tstart):.2E}s)'
+ )
return rval
return wrapper_timer
return decorator_timer
diff --git a/python/sdist/amici/numpy.py b/python/sdist/amici/numpy.py
index 65df16a557..ce767679f3 100644
--- a/python/sdist/amici/numpy.py
+++ b/python/sdist/amici/numpy.py
@@ -141,6 +141,14 @@ def __deepcopy__(self, memo):
other._cache = copy.deepcopy(self._cache)
return other
+ def __repr__(self):
+ """
+ String representation of the object
+
+ :returns: string representation
+ """
+ return f'<{self.__class__.__name__}({self._swigptr})>'
+
class ReturnDataView(SwigPtrView):
"""
@@ -235,9 +243,13 @@ def __getitem__(self, item: str) -> Union[np.ndarray, ReturnDataPtr,
:returns: self[item]
"""
+ if item == 'status':
+ return int(super().__getitem__(item))
+
if item == 't':
item = 'ts'
- return super(ReturnDataView, self).__getitem__(item)
+
+ return super().__getitem__(item)
def by_id(
self,
diff --git a/python/sdist/amici/ode_export.py b/python/sdist/amici/ode_export.py
index fcae4d883c..bc669374af 100644
--- a/python/sdist/amici/ode_export.py
+++ b/python/sdist/amici/ode_export.py
@@ -1872,7 +1872,7 @@ def _derivative(self, eq: str, var: str, name: str = None) -> None:
cv for cv in ['w', 'tcl']
if var_in_function_signature(eq, cv)
and cv not in self._lock_total_derivative
- and var is not cv
+ and var != cv
and min(self.sym(cv).shape)
and (
(eq, var) not in ignore_chainrule
@@ -3070,7 +3070,7 @@ def _write_model_header_cpp(self) -> None:
self._get_symbol_name_initializer_list('y'),
'OBSERVABLE_TRAFO_INITIALIZER_LIST':
'\n'.join(
- f'ObservableScaling::{trafo}, // y[{idx}]'
+ f'ObservableScaling::{trafo.value}, // y[{idx}]'
for idx, trafo in enumerate(
self.model.get_observable_transformations()
)
diff --git a/python/sdist/amici/ode_model.py b/python/sdist/amici/ode_model.py
index a21526d27d..9e03822ac3 100644
--- a/python/sdist/amici/ode_model.py
+++ b/python/sdist/amici/ode_model.py
@@ -297,12 +297,15 @@ class Observable(ModelQuantity):
_measurement_symbol: Union[sp.Symbol, None] = None
- def __init__(self,
- identifier: sp.Symbol,
- name: str,
- value: sp.Expr,
- measurement_symbol: Optional[sp.Symbol] = None,
- transformation: Optional[ObservableTransformation] = 'lin'):
+ def __init__(
+ self,
+ identifier: sp.Symbol,
+ name: str,
+ value: sp.Expr,
+ measurement_symbol: Optional[sp.Symbol] = None,
+ transformation: Optional[
+ ObservableTransformation] = ObservableTransformation.LIN
+ ):
"""
Create a new Observable instance.
diff --git a/python/sdist/amici/petab_objective.py b/python/sdist/amici/petab_objective.py
index e8557cb242..4f03e66647 100644
--- a/python/sdist/amici/petab_objective.py
+++ b/python/sdist/amici/petab_objective.py
@@ -37,6 +37,7 @@
RES = 'res'
SRES = 'sres'
RDATAS = 'rdatas'
+EDATAS = 'edatas'
@log_execution_time('Simulating PEtab model', logger)
@@ -92,6 +93,7 @@ def simulate_petab(
* cost function value (``LLH``),
* list of :class:`amici.amici.ReturnData` (``RDATAS``),
+ * list of :class:`amici.amici.ExpData` (``EDATAS``),
corresponding to the different simulation conditions.
For ordering of simulation conditions, see
@@ -163,7 +165,8 @@ def simulate_petab(
return {
LLH: llh,
- RDATAS: rdatas
+ RDATAS: rdatas,
+ EDATAS: edatas,
}
diff --git a/python/sdist/amici/plotting.py b/python/sdist/amici/plotting.py
index d2917de9fe..6b3eade225 100644
--- a/python/sdist/amici/plotting.py
+++ b/python/sdist/amici/plotting.py
@@ -3,14 +3,17 @@
--------
Plotting related functions
"""
-from . import ReturnDataView, Model
+from typing import Iterable, Optional
import matplotlib.pyplot as plt
+import pandas as pd
+import seaborn as sns
from matplotlib.axes import Axes
-from typing import Optional, Iterable
+from . import Model, ReturnDataView
-def plotStateTrajectories(
+
+def plot_state_trajectories(
rdata: ReturnDataView,
state_indices: Optional[Iterable[int]] = None,
ax: Optional[Axes] = None,
@@ -50,7 +53,7 @@ def plotStateTrajectories(
ax.set_title('State trajectories')
-def plotObservableTrajectories(
+def plot_observable_trajectories(
rdata: ReturnDataView,
observable_indices: Optional[Iterable[int]] = None,
ax: Optional[Axes] = None,
@@ -88,3 +91,18 @@ def plotObservableTrajectories(
ax.set_ylabel('$y(t)$')
ax.legend()
ax.set_title('Observable trajectories')
+
+
+def plot_jacobian(rdata: ReturnDataView):
+ """Plot Jacobian as heatmap."""
+ df = pd.DataFrame(
+ data=rdata.J,
+ index=rdata._swigptr.state_ids_solver,
+ columns=rdata._swigptr.state_ids_solver,
+ )
+ sns.heatmap(df, center=0.0)
+ plt.title("Jacobian")
+
+# backwards compatibility
+plotStateTrajectories = plot_state_trajectories
+plotObservableTrajectories = plot_observable_trajectories
diff --git a/python/sdist/amici/swig.py b/python/sdist/amici/swig.py
index b2ab7c090c..ed1aeb8534 100644
--- a/python/sdist/amici/swig.py
+++ b/python/sdist/amici/swig.py
@@ -133,6 +133,10 @@ def visit_FunctionDef(self, node):
for arg in node.args.args:
if not arg.annotation:
continue
+ if isinstance(arg.annotation, ast.Name):
+ # there is already proper annotation
+ continue
+
arg.annotation = self._new_annot(arg.annotation.value)
return node
diff --git a/python/sdist/amici/swig_wrappers.py b/python/sdist/amici/swig_wrappers.py
index 4b303536ae..cf998970e6 100644
--- a/python/sdist/amici/swig_wrappers.py
+++ b/python/sdist/amici/swig_wrappers.py
@@ -281,3 +281,5 @@ def _ids_and_names_to_rdata(
f"{entity_type.lower()}_{name_or_id.lower()}",
names_or_ids
)
+ rdata.state_ids_solver = model.getStateIdsSolver()
+ rdata.state_names_solver = model.getStateNamesSolver()
diff --git a/python/tests/test_sbml_import.py b/python/tests/test_sbml_import.py
index 8c632c23b5..f70cb773ab 100644
--- a/python/tests/test_sbml_import.py
+++ b/python/tests/test_sbml_import.py
@@ -180,6 +180,11 @@ def test_logging_works(observable_dependent_error_model, caplog):
assert rdata.status != amici.AMICI_SUCCESS
assert "mxstep steps taken" in caplog.text
+@skip_on_valgrind
+def test_model_module_is_set(observable_dependent_error_model):
+ model_module = observable_dependent_error_model
+ assert isinstance(model_module.getModel().module, amici.ModelModule)
+
@pytest.fixture(scope='session')
def model_steadystate_module():
diff --git a/python/tests/test_swig_interface.py b/python/tests/test_swig_interface.py
index c23eaaad00..e7c52d1982 100644
--- a/python/tests/test_swig_interface.py
+++ b/python/tests/test_swig_interface.py
@@ -403,3 +403,34 @@ def test_model_instance_settings_custom_x0(pysb_example_presimulation_module):
assert model2.getInitialStateSensitivities() == sx0
assert settings == amici.get_model_settings(model2)
+
+def test_solver_repr():
+ for solver in (amici.CVodeSolver(), amici.IDASolver()):
+ solver_ptr = amici.SolverPtr(solver.this)
+ for s in (solver, solver_ptr):
+ assert "maxsteps" in str(s)
+ assert "maxsteps" in repr(s)
+ # avoid double delete!!
+ solver_ptr.release()
+
+
+def test_edata_repr():
+ ny = 1
+ nz = 2
+ ne = 3
+ nt = 4
+ edata = amici.ExpData(ny, nz, ne, range(nt))
+ edata_ptr = amici.ExpDataPtr(edata.this)
+ expected_strs = (
+ f'{nt}x{ny} time-resolved datapoints',
+ f'{ne}x{nz} event-resolved datapoints',
+ f'(0/{ny * nt} measurements',
+ f'(0/{nz * ne} measurements'
+ )
+ for e in [edata, edata_ptr]:
+ for expected_str in expected_strs:
+ assert expected_str in str(e)
+ assert expected_str in repr(e)
+ # avoid double delete!!
+ edata_ptr.release()
+
diff --git a/python/tests/valgrind-python.supp b/python/tests/valgrind-python.supp
index 7815e51fa1..dde0c74cf7 100644
--- a/python/tests/valgrind-python.supp
+++ b/python/tests/valgrind-python.supp
@@ -755,3 +755,9 @@
fun:dlopen@@GLIBC_2.34
fun:_PyImport_FindSharedFuncptr
}
+
+{
+ Python dictkeys_get_index
+ Memcheck:Value8
+ fun:dictkeys_get_index
+}
diff --git a/scripts/run-sphinx.sh b/scripts/run-sphinx.sh
index 933b8dc9cf..04716d4b7b 100755
--- a/scripts/run-sphinx.sh
+++ b/scripts/run-sphinx.sh
@@ -7,8 +7,8 @@ AMICI_PATH=$(cd $SCRIPT_PATH/.. && pwd)
python3 -m venv ${AMICI_PATH}/doc-venv --clear
source ${AMICI_PATH}/doc-venv/bin/activate
python -m pip install --upgrade --no-cache-dir pip
-python -m pip install --exists-action=w --no-cache-dir -r ${AMICI_PATH}/documentation/rtd_requirements.txt
-python -m pip install --exists-action=w --no-cache-dir -r ${AMICI_PATH}/documentation/rtd_requirements2.txt
+(cd ${AMICI_PATH}/ && python -m pip install --exists-action=w --no-cache-dir -r documentation/rtd_requirements.txt)
+(cd ${AMICI_PATH}/ && python -m pip install --exists-action=w --no-cache-dir -r documentation/rtd_requirements2.txt)
${AMICI_PATH}/scripts/run-sphinx-hasenv.sh
diff --git a/src/amici.cpp b/src/amici.cpp
index 338937bca1..55060e083a 100644
--- a/src/amici.cpp
+++ b/src/amici.cpp
@@ -51,6 +51,7 @@ std::map simulation_status_to_str_map = {
{AMICI_TOO_MUCH_ACC, "AMICI_TOO_MUCH_ACC"},
{AMICI_ERR_FAILURE, "AMICI_ERR_FAILURE"},
{AMICI_CONV_FAILURE, "AMICI_CONV_FAILURE"},
+ {AMICI_FIRST_RHSFUNC_ERR, "AMICI_FIRST_RHSFUNC_ERR"},
{AMICI_RHSFUNC_FAIL, "AMICI_RHSFUNC_FAIL"},
{AMICI_ILL_INPUT, "AMICI_ILL_INPUT"},
{AMICI_ERROR, "AMICI_ERROR"},
diff --git a/src/exception.cpp b/src/exception.cpp
index 2d1f72ecdc..871c240c9b 100644
--- a/src/exception.cpp
+++ b/src/exception.cpp
@@ -8,43 +8,42 @@
namespace amici {
-AmiException::AmiException()
-{
- storeBacktrace(12);
+AmiException::AmiException(int const first_frame) {
+ storeBacktrace(12, first_frame);
}
-AmiException::AmiException(const char *fmt, ...)
- : AmiException()
-{
+AmiException::AmiException(char const* fmt, ...)
+ : AmiException(4) {
va_list ap;
va_start(ap, fmt);
storeMessage(fmt, ap);
va_end(ap);
}
-const char *AmiException::what() const noexcept {
- return msg_.data();
-}
+char const* AmiException::what() const noexcept { return msg_.data(); }
-const char *AmiException::getBacktrace() const {
- return trace_.data();
-}
+char const* AmiException::getBacktrace() const { return trace_.data(); }
-void AmiException::storeBacktrace(const int nMaxFrames) {
- snprintf(trace_.data(), trace_.size(), "%s",
- backtraceString(nMaxFrames).c_str());
+void AmiException::storeBacktrace(int const nMaxFrames, int const first_frame) {
+ snprintf(
+ trace_.data(), trace_.size(), "%s",
+ backtraceString(nMaxFrames, first_frame).c_str()
+ );
}
-void AmiException::storeMessage(const char *fmt, va_list argptr)
-{
+void AmiException::storeMessage(char const* fmt, va_list argptr) {
vsnprintf(msg_.data(), msg_.size(), fmt, argptr);
}
-CvodeException::CvodeException(const int error_code, const char *function) :
- AmiException("Cvode routine %s failed with error code %i",function,error_code){}
+CvodeException::CvodeException(int const error_code, char const* function)
+ : AmiException(
+ "Cvode routine %s failed with error code %i", function, error_code
+ ) {}
-IDAException::IDAException(const int error_code, const char *function) :
- AmiException("IDA routine %s failed with error code %i",function,error_code){}
+IDAException::IDAException(int const error_code, char const* function)
+ : AmiException(
+ "IDA routine %s failed with error code %i", function, error_code
+ ) {}
IntegrationFailure::IntegrationFailure(int code, realtype t) :
AmiException("AMICI failed to integrate the forward problem"),
@@ -54,13 +53,14 @@ IntegrationFailureB::IntegrationFailureB(int code, realtype t) :
AmiException("AMICI failed to integrate the backward problem"),
error_code(code), time(t) {}
-NewtonFailure::NewtonFailure(int code, const char *function) :
- AmiException("NewtonSolver routine %s failed with error code %i",function,code) {
+NewtonFailure::NewtonFailure(int code, char const* function)
+ : AmiException(
+ "NewtonSolver routine %s failed with error code %i", function, code
+ ) {
error_code = code;
}
-SetupFailure::SetupFailure(const char *fmt, ...)
-{
+SetupFailure::SetupFailure(char const* fmt, ...) {
va_list ap;
va_start(ap, fmt);
storeMessage(fmt, ap);
diff --git a/src/misc.cpp b/src/misc.cpp
index 9c360064ef..bce3fdfddb 100644
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -20,7 +20,7 @@
namespace amici {
-void writeSlice(const AmiVector &s, gsl::span b) {
+void writeSlice(AmiVector const& s, gsl::span b) {
writeSlice(s.getVector(), b);
};
@@ -38,10 +38,10 @@ double getUnscaledParameter(double scaledParameter, ParameterScaling scaling)
throw AmiException("Invalid value for ParameterScaling.");
}
-void unscaleParameters(gsl::span bufferScaled,
- gsl::span pscale,
- gsl::span bufferUnscaled)
-{
+void unscaleParameters(
+ gsl::span bufferScaled,
+ gsl::span pscale, gsl::span bufferUnscaled
+) {
Expects(bufferScaled.size() == pscale.size());
Expects(bufferScaled.size() == bufferUnscaled.size());
@@ -51,7 +51,6 @@ void unscaleParameters(gsl::span bufferScaled,
}
}
-
double getScaledParameter(double unscaledParameter, ParameterScaling scaling)
{
switch (scaling) {
@@ -66,11 +65,10 @@ double getScaledParameter(double unscaledParameter, ParameterScaling scaling)
throw AmiException("Invalid value for ParameterScaling.");
}
-
-void scaleParameters(gsl::span bufferUnscaled,
- gsl::span pscale,
- gsl::span bufferScaled)
-{
+void scaleParameters(
+ gsl::span bufferUnscaled,
+ gsl::span pscale, gsl::span bufferScaled
+) {
Expects(bufferScaled.size() == pscale.size());
Expects(bufferScaled.size() == bufferUnscaled.size());
@@ -78,23 +76,21 @@ void scaleParameters(gsl::span bufferUnscaled,
ip < bufferUnscaled.size(); ++ip) {
bufferScaled[ip] = getScaledParameter(bufferUnscaled[ip], pscale[ip]);
}
-
}
-std::string backtraceString(const int maxFrames)
-{
+std::string backtraceString(int const maxFrames, int const first_frame) {
std::ostringstream trace_buf;
#ifdef PLATFORM_WINDOWS
trace_buf << "stacktrace not available on windows platforms\n";
#else
- void *callstack[maxFrames];
+ int const last_frame = first_frame + maxFrames;
+ void* callstack[last_frame];
char buf[1024];
- int nFrames = backtrace(callstack, maxFrames);
+ int nFrames = backtrace(callstack, last_frame);
char **symbols = backtrace_symbols(callstack, nFrames);
- // start at 2 to omit AmiException and storeBacktrace
- for (int i = 2; i < nFrames; i++) {
+ for (int i = first_frame; i < nFrames; i++) {
// call
Dl_info info;
if (dladdr(callstack[i], &info) && info.dli_sname) {
@@ -120,8 +116,8 @@ std::string backtraceString(const int maxFrames)
}
free(symbols);
- if (nFrames == maxFrames)
- trace_buf << "[truncated]\n";
+ if (nFrames == last_frame)
+ trace_buf << "[possibly truncated]\n";
#endif
return trace_buf.str();
}
@@ -160,7 +156,7 @@ std::string regexErrorToString(std::regex_constants::error_type err_type)
}
}
-std::string printfToString(const char *fmt, va_list ap) {
+std::string printfToString(char const* fmt, va_list ap) {
// Get size of string
va_list ap_count;
va_copy(ap_count, ap);
diff --git a/src/model.cpp b/src/model.cpp
index df71ae3b8f..db36040167 100644
--- a/src/model.cpp
+++ b/src/model.cpp
@@ -305,6 +305,8 @@ void Model::initializeStates(AmiVector &x) {
fx_solver(x0_solver.data(), x0data_.data());
std::copy(x0_solver.cbegin(), x0_solver.cend(), x.data());
}
+
+ checkFinite(x.getVector(), ModelQuantity::x0);
}
void Model::initializeStateSensitivities(AmiVectorArray &sx,
@@ -1663,10 +1665,7 @@ void Model::fx0(AmiVector &x) {
state_.unscaledParameters.data(),
state_.fixedParameters.data());
- if (always_check_finite_) {
- checkFinite(derived_state_.x_rdata_, ModelQuantity::x0_rdata);
- checkFinite(x.getVector(), ModelQuantity::x0);
- }
+ checkFinite(derived_state_.x_rdata_, ModelQuantity::x0_rdata);
}
void Model::fx0_fixedParameters(AmiVector &x) {
diff --git a/src/returndata_matlab.cpp b/src/returndata_matlab.cpp
index 297c6d68cf..9599be2673 100644
--- a/src/returndata_matlab.cpp
+++ b/src/returndata_matlab.cpp
@@ -216,7 +216,7 @@ void writeMatlabField1(
double *array = initAndAttachArray(matlabStruct, fieldName, dim);
auto data_ptr = fieldData.data();
- for(int i = 0; i < dim0; i++)
+ for (mwSize i = 0; i < dim0; i++)
array[i] = static_cast(data_ptr[i]);
}
diff --git a/swig/edata.i b/swig/edata.i
index ec6fe7d551..310869768c 100644
--- a/swig/edata.i
+++ b/swig/edata.i
@@ -8,5 +8,81 @@ using namespace amici;
%ignore ConditionContext;
+// ExpData.__repr__
+%pythoncode %{
+def _edata_repr(self: "ExpData"):
+ n_data_y = sum(
+ self.isSetObservedData(it, iy)
+ for it in range(self.nt()) for
+ iy in range(self.nytrue())
+ )
+ n_sigma_y = sum(
+ self.isSetObservedDataStdDev(it, iy)
+ for it in range(self.nt())
+ for iy in range(self.nytrue())
+ )
+ n_data_z = sum(
+ self.isSetObservedEvents(ie, iz)
+ for ie in range(self.nmaxevent())
+ for iz in range(self.nztrue())
+ )
+ n_sigma_z = sum(
+ self.isSetObservedEventsStdDev(ie, iz)
+ for ie in range(self.nmaxevent())
+ for iz in range(self.nztrue())
+ )
+
+ custom_simulation_settings = []
+ if self.pscale:
+ custom_simulation_settings.append(f"parameter scales")
+ if self.fixedParameters:
+ custom_simulation_settings.append(f"constants")
+ if self.fixedParametersPreequilibration:
+ custom_simulation_settings.append(f"pre-equilibration condition")
+ if self.t_presim:
+ tmp = f"pre-simulation condition (t={self.t_presim})"
+ if self.fixedParametersPresimulation:
+ tmp += " with custom constants"
+ custom_simulation_settings.append(tmp)
+ if self.reinitializeFixedParameterInitialStates and self.reinitialization_state_idxs_sim:
+ custom_simulation_settings.append(f"{len(self.reinitialization_state_idxs_sim)} reinitialized states (simulation)")
+ if self.reinitializeFixedParameterInitialStates and self.reinitialization_state_idxs_presim:
+ custom_simulation_settings.append(f"{len(self.reinitialization_state_idxs_presim)} reinitialized states (presimulation)")
+ if self.parameters:
+ custom_simulation_settings.append(f"parameters")
+ if self.x0:
+ custom_simulation_settings.append(f"initial states")
+ if self.sx0:
+ custom_simulation_settings.append(f"initial state sensitivities")
+
+ if custom_simulation_settings:
+ custom_simulation_settings = " with custom " + ", ".join(custom_simulation_settings)
+ else:
+ custom_simulation_settings = " without custom settings"
+
+ return "\n".join([
+ self.this.__repr__()[:-1],
+ f" condition {id} starting at t={self.tstart_}" + custom_simulation_settings,
+ f" {self.nt()}x{self.nytrue()} time-resolved datapoints",
+ f" ({n_data_y}/{self.nt()*self.nytrue()} measurements & {n_sigma_y}/{self.nt()*self.nytrue()} sigmas set)",
+ f" {self.nmaxevent()}x{self.nztrue()} event-resolved datapoints",
+ f" ({n_data_z}/{self.nmaxevent()*self.nztrue()} measurements & {n_sigma_z}/{self.nmaxevent()*self.nztrue()} sigmas set)",
+ ">"
+ ])
+%}
+%extend amici::ExpData {
+%pythoncode %{
+def __repr__(self):
+ return _edata_repr(self)
+%}
+};
+%extend std::unique_ptr {
+%pythoncode %{
+def __repr__(self):
+ return _edata_repr(self)
+%}
+};
+
+
// Process symbols in header
%include "amici/edata.h"
diff --git a/swig/modelname.template.i b/swig/modelname.template.i
index 3425d78a17..33804a1800 100644
--- a/swig/modelname.template.i
+++ b/swig/modelname.template.i
@@ -10,5 +10,12 @@ using namespace amici;
%}
+// Make model module accessible from the model
+%feature("pythonappend") amici::generic_model::getModel %{
+ import sys
+ val.module = sys.modules['.'.join(__name__.split('.')[:-1])]
+%}
+
+
// Process symbols in header
%include "wrapfunctions.h"
diff --git a/swig/solver.i b/swig/solver.i
index 853edae9bd..319e654530 100644
--- a/swig/solver.i
+++ b/swig/solver.i
@@ -40,6 +40,67 @@ using namespace amici;
%ignore getRootInfo;
%ignore updateAndReinitStatesAndSensitivities;
+// Solver.__repr__
+%pythoncode %{
+def _solver_repr(self: "Solver"):
+ return "\n".join([
+ self.this.__repr__()[:-1],
+ " reporting_mode: "
+ f"{RDataReporting(self.getReturnDataReportingMode())!r}",
+ f" sens_meth: {SensitivityMethod(self.getSensitivityMethod())!r}",
+ f" sens_order: {SensitivityOrder(self.getSensitivityOrder())!r}",
+ f" sens_meth_preeq: {SensitivityMethod(self.getSensitivityMethodPreequilibration())!r}",
+ f" maxsteps: {self.getMaxSteps()}",
+ f" maxtime: {self.getMaxTime()}s",
+ f" abs_tol: {self.getAbsoluteTolerance()}",
+ f" rel_tol: {self.getRelativeTolerance()}",
+ f" abs_tol_b: {self.getAbsoluteToleranceB()}",
+ f" rel_tol_b: {self.getRelativeToleranceB()}",
+ f" abs_tol_fsa: {self.getAbsoluteToleranceFSA()}",
+ f" rel_tol_fsa: {self.getRelativeToleranceFSA()}",
+ f" abs_tol_quad: {self.getAbsoluteToleranceQuadratures()}",
+ f" rel_tol_quad: {self.getRelativeToleranceQuadratures()}",
+ f" abs_tol_ss: {self.getAbsoluteToleranceSteadyState()}",
+ f" rel_tol_ss: {self.getRelativeToleranceSteadyState()}",
+ f" abs_tol_sss: {self.getAbsoluteToleranceSteadyStateSensi()}",
+ f" rel_tol_sss: {self.getRelativeToleranceSteadyStateSensi()}",
+ f" int_sens_meth: {InternalSensitivityMethod(self.getInternalSensitivityMethod())!r}",
+ f" int_type: {InterpolationType(self.getInterpolationType())!r}",
+ f" linsol: {LinearSolver(self.getLinearSolver())!r}",
+ f" lmm: {LinearMultistepMethod(self.getLinearMultistepMethod())!r}",
+ f" newton_damp_mode: {NewtonDampingFactorMode(self.getNewtonDampingFactorMode())!r}",
+ f" newton_damp_lb: {self.getNewtonDampingFactorLowerBound()}",
+ f" newton_maxsteps: {self.getNewtonMaxSteps()}",
+ f" newton_ss_check: {self.getNewtonStepSteadyStateCheck()}",
+ f" sens_ss_check: {self.getSensiSteadyStateCheck()}",
+ f" interpolation_type: {InterpolationType(self.getInterpolationType())!r}",
+ f" ism: {InternalSensitivityMethod(self.getInternalSensitivityMethod())!r}",
+ f" nlsol_iter: {NonlinearSolverIteration(self.getNonlinearSolverIteration())!r}",
+ f" stability_limit: {self.getStabilityLimitFlag()}",
+ f" state_ordering: {self.getStateOrdering()}",
+ ">"
+ ])
+%}
+%extend amici::CVodeSolver {
+%pythoncode %{
+def __repr__(self):
+ return _solver_repr(self)
+%}
+};
+%extend amici::IDASolver {
+%pythoncode %{
+def __repr__(self):
+ return _solver_repr(self)
+%}
+};
+
+%extend std::unique_ptr {
+%pythoncode %{
+def __repr__(self):
+ return _solver_repr(self)
+%}
+};
+
%newobject amici::Solver::clone;
// Process symbols in header
diff --git a/tests/benchmark-models/benchmark_models.yaml b/tests/benchmark-models/benchmark_models.yaml
index d9ce6d0dd9..42d9f4a71d 100644
--- a/tests/benchmark-models/benchmark_models.yaml
+++ b/tests/benchmark-models/benchmark_models.yaml
@@ -1,15 +1,15 @@
Bachmann_MSB2011:
llh: 418.40573341425295
t_sim: 0.05
- t_fwd: 3.0
- t_adj: 4.5
+ t_fwd: 3.5
+ t_adj: 5.0
note: benchmark collection reference value matches up to sign when applying log10-correction +sum(log(meas*log(10)) / 2
Beer_MolBioSystems2014:
llh: 58622.9145631413
t_sim: 0.05
t_fwd: 0.5
- t_adj: 8.0
+ t_adj: 8.5
note: benchmark collection reference parameters do not match petab, but reference llh has been confirmed for parameters reported there up to sign
Boehm_JProteomeRes2014:
@@ -61,7 +61,7 @@ Fiedler_BMC2016:
llh: 58.58390161681
t_sim: 0.005
t_fwd: 0.05
- t_adj: 0.1
+ t_adj: 0.15
Fujita_SciSignal2010:
llh: 53.08377736998929
@@ -80,7 +80,7 @@ Lucarelli_CellSystems2018:
llh: -1681.6059879426584
t_sim: 0.05
t_fwd: 2.5
- t_adj: 2.0
+ t_adj: 2.5
Schwen_PONE2014:
llh: -943.9992988598723
diff --git a/tests/performance/test.py b/tests/performance/test.py
index 9dde939f5f..03a2309c77 100755
--- a/tests/performance/test.py
+++ b/tests/performance/test.py
@@ -4,6 +4,7 @@
import shutil
import subprocess
import sys
+import logging
from pathlib import Path
import amici
@@ -47,6 +48,8 @@ def run_import(model_name, model_dir: Path):
pp = petab.Problem.from_yaml(git_dir / 'FroehlichKes2018' / 'PEtab'
/ 'FroehlichKes2018.yaml')
petab.lint_problem(pp)
+ amici.ode_export.logger.setLevel(logging.DEBUG)
+ amici.sbml_import.logger.setLevel(logging.DEBUG)
import_model(model_name=model_name,
model_output_dir=model_dir,
sbml_model=pp.sbml_model,
diff --git a/version.txt b/version.txt
index a551051694..04a373efe6 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-0.15.0
+0.16.0