# sbmlsim > SBML simulation made easy: simulation experiments, parameter scans, parameter fitting, sensitivity analysis and SED-ML for SBML models The complete documentation from https://matthiaskoenig.github.io/sbmlsim, one section per page. --- ![](images/favicon/sbmlsim-100x100-300dpi.png) # sbmlsim: SBML simulation made easy [![GitHub Actions CI/CD Status](https://github.com/matthiaskoenig/sbmlsim/actions/workflows/ci-cd.yml/badge.svg)](https://github.com/matthiaskoenig/sbmlsim/actions/workflows/ci-cd.yml) [![Documentation](https://img.shields.io/badge/docs-sbmlsim-3f51b5.svg)](https://matthiaskoenig.github.io/sbmlsim) [![Version](https://img.shields.io/pypi/v/sbmlsim.svg)](https://pypi.org/project/sbmlsim/) [![Python Versions](https://img.shields.io/pypi/pyversions/sbmlsim.svg)](https://pypi.org/project/sbmlsim/) [![MIT License](https://img.shields.io/pypi/l/sbmlsim.svg)](https://opensource.org/licenses/MIT) [![DOI](https://zenodo.org/badge/55952847.svg)](https://zenodo.org/badge/latestdoi/55952847) `sbmlsim` is a collection of python utilities for the simulation of models in the [Systems Biology Markup Language](https://sbml.org) (SBML), built on [libroadrunner](https://libroadrunner.org). The source code is available from [https://github.com/matthiaskoenig/sbmlsim](https://github.com/matthiaskoenig/sbmlsim). ## Background SBML is the exchange format for computational models in systems biology ([Keating *et al.* 2020](references.md#standards)) and libroadrunner is a fast simulator for it ([Welsh *et al.* 2023](references.md#simulation)). Simulating a model is a few lines with roadrunner; a simulation *experiment* is more: the model comes with changes of parameters and initial conditions, timecourses are concatenated into dosing protocols, parameters are scanned over ranges, the results are compared to experimental data in the units of the model, plotted and reported, and all of that has to be reproducible. `sbmlsim` is the layer above the simulator which describes these experiments. A `Timecourse` is a period of a simulation with its changes, a `TimecourseSim` concatenates them, a `ScanSim` runs a simulation over the dimensions of parameter changes, and a `SimulationExperiment` collects models, datasets, simulations, tasks, data and figures into one python object which is executed and reported by an `ExperimentRunner`. Results are `XResult` objects, labeled N-dimensional arrays with units, so the mean over a scan dimension or the conversion to the units of a dataset is one call. Around this core the package collects the tasks which come with simulation experiments: fitting parameters to data, analysing the sensitivity of a model to its parameters, and executing experiments described in SED-ML from COMBINE archives. ## Features - **[Models](models.md)** — SBML models are loaded into roadrunner with their units, parameter changes and selections; species can be clamped and model sources can be files, URNs or URLs. - **[Timecourse simulations](simulation.md)** — `Timecourse` and `TimecourseSim`, concatenated periods with changes of parameters and initial conditions, for dosing protocols and perturbations. - **[Parameter scans](scans.md)** — `ScanSim` runs a simulation over the dimensions of parameter changes, the result is an N-dimensional `XResult`. - **[Units](units.md)** — the units of the model are read from the SBML and all changes and results carry [pint](https://pint.readthedocs.io) quantities, so values are converted instead of assumed. - **[Simulation experiments](experiments.md)** — `SimulationExperiment` and `ExperimentRunner`, the reproducible description of an experiment with models, datasets, simulations, tasks, data and figures. - **[Data](data.md)** — `Data` references simulation results and experimental datasets, with functions computed from them. - **[Plots and reports](plotting.md)** — figures described independent of the backend and rendered with matplotlib, HTML and markdown reports of experiments. - **[Parameter fitting](fitting.md)** — `FitParameter`, `FitMapping` and `OptimizationProblem` with local and global optimizers, analysis of the results and PEtab archives. - **[Sensitivity analysis](sensitivity.md)** — local sensitivities by finite differences and the global Morris, Sobol and FAST methods of [SALib](https://salib.readthedocs.io), with classification and plots. - **[SED-ML and COMBINE archives](sedml.md)** — execution of simulation experiments in the Simulation Experiment Description Markup Language, from files and COMBINE archives. The standards and methods behind the package are cited in [References](references.md). ## Quickstart A model is simulated with a `TimecourseSim`, the result is an `XResult`: ```python from sbmlsim.resources import REPRESSILATOR_SBML from sbmlsim.simulation import Timecourse, TimecourseSim from sbmlsim.simulator import SimulatorSerial simulator = SimulatorSerial(model=REPRESSILATOR_SBML) simulation = TimecourseSim( [ Timecourse(start=0, end=100, steps=100), Timecourse(start=0, end=100, steps=100, changes={"X": 10}), ] ) xres = simulator.run_timecourse(simulation) print(xres["X"]) ``` Continue with [Installation](installation.md) and the [timecourse simulation guide](simulation.md). ## How to cite [![DOI](https://zenodo.org/badge/55952847.svg)](https://zenodo.org/badge/latestdoi/55952847) If you use `sbmlsim` please cite the archived software on [Zenodo](https://zenodo.org/badge/latestdoi/55952847): > König, M. (2026). *sbmlsim: SBML simulation made easy* [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.597149 ## License - Source Code: [MIT](https://opensource.org/license/MIT) - Documentation: [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/) ## Funding Matthias König is supported by the German Research Foundation (DFG) within the Research Unit Programme FOR 5151 "QuaLiPerF (Quantifying Liver Perfusion-Function Relationship in Complex Resection — A Systems Medicine Approach)" by grant number 436883643 and by grant number 465194077 (Priority Programme SPP 2311, Subproject SimLivA). Matthias König was supported by the Federal Ministry of Education and Research (BMBF, Germany) within the research network Systems Medicine of the Liver (**LiSyM**, grant number 031L0054). Matthias König has received funding from the EOSCsecretariat.eu which has received funding from the European Union's Horizon Programme call H2020-INFRAEOSC-05-2018-2019, grant Agreement number 831644. --- # Installation `sbmlsim` requires python >= 3.13 and is available from [pypi](https://pypi.python.org/pypi/sbmlsim). It is tested on Linux, macOS and Windows. The simulations run on [libroadrunner](https://libroadrunner.org), which ships binary wheels for all three platforms, so no compiler is needed. ## With uv [uv](https://docs.astral.sh/uv/) is the recommended way to install the package. In a project it is added as a dependency, which resolves and locks it together with the rest of the environment: ```bash uv add sbmlsim ``` Into an existing virtual environment it is installed through the pip interface of uv: ```bash uv venv --python 3.14 uv pip install sbmlsim ``` ## With pip ```bash pip install sbmlsim ``` ## Development version The current state of the `develop` branch is installed directly from GitHub: ```bash uv add "sbmlsim @ git+https://github.com/matthiaskoenig/sbmlsim.git@develop" ``` or, with pip, ```bash pip install git+https://github.com/matthiaskoenig/sbmlsim.git@develop ``` To work on the repository itself, with the test and documentation tooling, see [Development](development.md). ## Dependencies `sbmlsim` builds on the packages of the COMBINE ecosystem and the scientific python stack. They are installed with it: | package | used for | | --- | --- | | [libroadrunner](https://libroadrunner.org) | simulation of the SBML models | | [sbmlutils](https://github.com/matthiaskoenig/sbmlutils), [python-libsbml](https://sbml.org/software/libsbml/) | reading, validating and changing SBML models | | [pymetadata](https://github.com/matthiaskoenig/pymetadata), [python-libsedml](https://github.com/fbergmann/libSEDML), [python-libnuml](https://github.com/NuML/NuML) | COMBINE archives, SED-ML and NuML | | [numpy](https://numpy.org), [pandas](https://pandas.pydata.org), [xarray](https://xarray.dev), [scipy](https://scipy.org), [sympy](https://www.sympy.org) | numerics, data and results | | [pint](https://pint.readthedocs.io) | units and unit conversions | | [petab](https://petab.readthedocs.io), [SALib](https://salib.readthedocs.io) | parameter fitting problems and global sensitivity analysis | | [matplotlib](https://matplotlib.org), [seaborn](https://seaborn.pydata.org), [jinja2](https://jinja.palletsprojects.com) | plots and reports | ## Logging `sbmlsim` does not configure logging. It logs to loggers below the `sbmlsim` logger and leaves handlers, levels and formatting to the application, so the messages of the package stay under your control: ```python import logging logging.basicConfig(level=logging.INFO) logging.getLogger("sbmlsim").setLevel(logging.WARNING) ``` For scripts and interactive work the rich output of the package can be turned on explicitly: ```python from sbmlsim import log log.enable_rich_logging() ``` --- # Models `sbmlsim` simulates models in the [Systems Biology Markup Language](https://sbml.org) (SBML) with [libroadrunner](https://libroadrunner.org). This guide shows how a model is loaded, what the package reads from it and how the model is changed before a simulation. ## Loading a model The simulator loads a model from a path, a URL or an SBML string: ```python from sbmlsim.resources import REPRESSILATOR_SBML from sbmlsim.simulator import SimulatorSerial simulator = SimulatorSerial(model=REPRESSILATOR_SBML) print(simulator.model) ``` `sbmlsim.resources` provides the three models used throughout the documentation and the tests: `REPRESSILATOR_SBML`, the repressilator of Elowitz and Leibler, `DEMO_SBML`, a small demo model with compartments, and `MIDAZOLAM_SBML`, a whole body pharmacokinetics model of midazolam. Behind the simulator is a `RoadrunnerSBMLModel`, which owns the roadrunner instance `r`, the units of the model and the selections, i.e., the variables recorded in a simulation: ```python from sbmlsim.model import RoadrunnerSBMLModel model = RoadrunnerSBMLModel(source=REPRESSILATOR_SBML) print(model.r) # the roadrunner.RoadRunner instance print(model.selections) ``` The model can be created explicitly and passed to the simulator, which is useful when several simulators or experiments share a model. ## Units of a model The units of every parameter, species and compartment are read from the SBML and stored as a `UnitsInformation`, a mapping from identifier to unit string. All changes and results of a simulation carry these units, see [Units](units.md): ```python uinfo = model.uinfo print(uinfo["X"]) # unit of the species X print(uinfo["time"]) ``` ## Changes and selections A `RoadrunnerSBMLModel` accepts `changes`, which are applied to the model whenever it is reset, and `selections`, the variables recorded in a simulation. Changes are quantities with units or plain floats in the units of the model: ```python model = RoadrunnerSBMLModel( source=REPRESSILATOR_SBML, changes={"X": 5.0, "Y": 10.0}, selections=["time", "X", "Y", "Z"], ) print(model.changes) print(model.selections) ``` The roadrunner integrator is configured with `settings`, e.g., `settings={"absolute_tolerance": 1e-10}`; the defaults are set by `RoadrunnerSBMLModel.set_default_settings`. ## Abstract models A `SimulationExperiment` (see [Simulation experiments](experiments.md)) describes its models as an `AbstractModel`: the source and the changes without loading the model. The `ExperimentRunner` resolves the abstract models into roadrunner models when the experiment is run: ```python from sbmlsim.model import AbstractModel abstract_model = AbstractModel( source=REPRESSILATOR_SBML, changes={"X": 5.0}, ) print(abstract_model) model = RoadrunnerSBMLModel.from_abstract_model(abstract_model) print(model.r) ``` The source of a model is resolved by `sbmlsim.model.model_resources`: a path relative to the `base_path` of the experiment, an absolute path, a URL, or a `urn:miriam:biomodels.db:` URN which downloads the model from BioModels. ## Clamping species `ModelChange` implements structural changes of the model, currently clamping a species to a fixed value or formula. Clamping is a boundary condition set during a simulation and is part of a `Timecourse`, see the `model_manipulations` of [Timecourse simulations](simulation.md#clamping-species): ```python from sbmlsim.model import ModelChange r = model.r ModelChange.clamp_species(r, "X", "10.0") # clamp X to 10.0 ModelChange.clamp_species(r, "X", False) # release the clamp ``` ## Inspecting a model The parameters and species of the model with their current values and units are available as data frames: ```python print(RoadrunnerSBMLModel.parameter_df(model.r).head()) print(RoadrunnerSBMLModel.species_df(model.r).head()) ``` --- # Timecourse simulations A timecourse simulation integrates the model over a period of time. In `sbmlsim` a period is a `Timecourse` with its changes, and a `TimecourseSim` concatenates timecourses into one simulation. This is how dosing protocols, perturbations and pre-simulations are described. ## A single timecourse `Timecourse(start, end, steps)` integrates from `start` to `end` in `steps` intervals, i.e., `steps + 1` time points. The simulator runs the `TimecourseSim` and returns an `XResult`: ```python from sbmlsim.resources import REPRESSILATOR_SBML from sbmlsim.simulation import Timecourse, TimecourseSim from sbmlsim.simulator import SimulatorSerial simulator = SimulatorSerial(model=REPRESSILATOR_SBML) tcsim = TimecourseSim(Timecourse(start=0, end=100, steps=100)) xres = simulator.run_timecourse(tcsim) print(xres) ``` The result is a labeled N-dimensional array (an `xarray.Dataset` wrapped in an `XResult`) with a `_time` dimension. The variables are accessed with the selection ids of roadrunner: `X` for the amount of a species and `[X]` for its concentration: ```python print(xres["time"].values[:5]) print(xres["[X]"].values[:5]) ``` ## Changes A timecourse applies `changes` before it starts: values of parameters, initial amounts (`X`) or initial concentrations (`[X]`) of species. Changes are plain floats in the units of the model or quantities with units, which are converted to the model units, see [Units](units.md): ```python tcsim = TimecourseSim( Timecourse(start=0, end=100, steps=100, changes={"X": 10, "Y": 200}) ) xres = simulator.run_timecourse(tcsim) print(xres["X"].values[0], xres["Y"].values[0]) ``` ## Concatenated timecourses Several timecourses are simulated one after the other. Every timecourse continues from the end state of the previous one and applies its changes; the time of the result is continuous: ```python tcsim = TimecourseSim( [ Timecourse(start=0, end=100, steps=100), Timecourse(start=0, end=100, steps=100, changes={"X": 10, "Y": 20}), Timecourse(start=0, end=100, steps=100, changes={"X": 0.5}), ] ) xres = simulator.run_timecourse(tcsim) print(xres["time"].values[[0, 100, 101, 200, 201, -1]]) ``` This is the pattern for a dosing protocol: every dose is a timecourse whose change sets the dose parameter. A `Timecourse` with `discard=True` is simulated but removed from the result, which is how a pre-simulation to a steady state is described. By default a `TimecourseSim` resets the model to its initial state before the first timecourse (`reset=True`); `time_offset` shifts the time of the complete result. ## Clamping species Structural changes of the model, e.g., clamping a species to a fixed value, are `model_manipulations` of a timecourse. Here `X` is clamped during the second period and released in the third: ```python from sbmlsim.model import ModelChange tcsim = TimecourseSim( [ Timecourse(start=0, end=100, steps=100), Timecourse( start=0, end=100, steps=100, model_manipulations={ModelChange.CLAMP_SPECIES: {"X": True}}, ), Timecourse( start=0, end=100, steps=100, model_manipulations={ModelChange.CLAMP_SPECIES: {"X": False}}, ), ] ) xres = simulator.run_timecourse(tcsim) print(xres["[X]"].values[100:105]) ``` ## Selections and integrator settings The variables recorded in a simulation are the selections of the model. By default all species (amounts and concentrations), parameters, reactions and compartments are recorded; a smaller selection speeds up the simulation: ```python simulator.set_timecourse_selections(["time", "[X]", "[Y]", "[Z]"]) xres = simulator.run_timecourse(TimecourseSim(Timecourse(start=0, end=10, steps=10))) print(list(xres.xds.data_vars)) ``` The integrator settings of roadrunner are passed to the simulator or set afterwards: ```python simulator = SimulatorSerial( model=REPRESSILATOR_SBML, absolute_tolerance=1e-10, relative_tolerance=1e-10 ) simulator.set_integrator_settings(variable_step_size=False) ``` ## Results An `XResult` is converted to pandas for further processing and stored as netCDF or TSV: ```python from pathlib import Path df = xres.to_dataframe() print(df.head()) xres.to_netcdf(Path("repressilator.nc")) xres.to_tsv(Path("repressilator.tsv")) ``` `XResult.dim_mean`, `dim_std`, `dim_min` and `dim_max` reduce the result over all dimensions except time and return quantities with the units of the variable, see [Parameter scans](scans.md). ## Serialization A `TimecourseSim` is serialized to JSON and read back, which is how a simulation experiment stores its simulations: ```python json_str = tcsim.to_json() tcsim2 = TimecourseSim.from_json(json_str) print(tcsim2) ``` --- # Parameter scans A parameter scan runs a simulation for every combination of parameter values. In `sbmlsim` a `ScanSim` combines a `TimecourseSim` with one or more `Dimension` objects, each describing the changes along one axis of the scan. The result is an `XResult` with one dimension per scan dimension and the time. ## A one dimensional scan A `Dimension` is a set of changes with vectors of values. The values of all changes of a dimension are applied together, element by element; the index of the dimension is the position in these vectors: ```python import numpy as np from sbmlsim.resources import REPRESSILATOR_SBML from sbmlsim.simulation import Dimension, ScanSim, Timecourse, TimecourseSim from sbmlsim.simulator import SimulatorSerial simulator = SimulatorSerial(model=REPRESSILATOR_SBML) scan = ScanSim( simulation=TimecourseSim(Timecourse(start=0, end=100, steps=100)), dimensions=[ Dimension("dim_n", changes={"n": np.linspace(2, 4, num=5)}), ], ) xres = simulator.run_scan(scan) print(xres.xds.dims) print(xres["PX"].shape) ``` The values of a dimension are quantities with units or floats in the units of the model. Values from a distribution are a scan as well: ```python scan = ScanSim( simulation=TimecourseSim(Timecourse(start=0, end=100, steps=100)), dimensions=[ Dimension( "dim_n", changes={"n": np.random.normal(loc=3.0, scale=0.2, size=20)} ), ], ) xres = simulator.run_scan(scan) print(xres["PX"].sizes) ``` ## Multi-dimensional scans Several dimensions are combined: every combination of the indices is simulated. The result has a dimension for every `Dimension`: ```python scan = ScanSim( simulation=TimecourseSim(Timecourse(start=0, end=100, steps=100)), dimensions=[ Dimension("dim_n", changes={"n": np.linspace(2, 4, num=3)}), Dimension("dim_X", changes={"X": np.array([1.0, 10.0, 100.0, 1000.0])}), ], ) xres = simulator.run_scan(scan) print(xres["PX"].sizes) ``` The indices of the scan are available as `scan.indices()` and the individual simulations as `scan.to_simulations()`, which returns the indices and one `TimecourseSim` per combination. ## Working with scan results The result is an `xarray.Dataset`, so the usual selections and reductions apply. `XResult.dim_mean`, `dim_std`, `dim_min` and `dim_max` reduce over all scan dimensions and return quantities with units: ```python da = xres["PX"] print(da.isel(dim_n=0, dim_X=1).values[:3]) # single timecourse print(da.mean(dim="dim_X").sizes) # mean over one dimension mean = xres.dim_mean("PX") # mean over all scan dimensions, a quantity print(mean.units, mean.magnitude[:3]) ``` `XResult.to_mean_dataframe` reduces every variable to its mean over the scan dimensions and returns a data frame with one row per time point. ## Sensitivity scans `ModelSensitivity` creates scans of all parameters of a model, either by relative differences or by sampling from distributions, see `sbmlsim.simulation.sensitivity`: ```python from sbmlsim.simulation.sensitivity import ModelSensitivity model = simulator.model_loaded tcsim = TimecourseSim(Timecourse(start=0, end=100, steps=100)) diff_scan = ModelSensitivity.difference_sensitivity_scan( model=model, simulation=tcsim, difference=0.1 ) xres = simulator.run_scan(diff_scan) print(xres["PX"].sizes) distrib_scan = ModelSensitivity.distribution_sensitivity_scan( model=model, simulation=tcsim, cv=0.05, size=10 ) xres = simulator.run_scan(distrib_scan) print(xres["PX"].sizes) ``` The difference scan varies every constant parameter up and down by the relative `difference` (two simulations per parameter, plus the reference); the distribution scan samples `size` values of every parameter from a normal distribution with the coefficient of variation `cv`. The global sensitivity methods of `sbmlsim.sensitivity` build on scans like these, see [Sensitivity analysis](sensitivity.md). --- # Units Every SBML model declares units for its parameters, species and compartments. `sbmlsim` reads them and uses [pint](https://pint.readthedocs.io) quantities for changes and results, so a dose given in milligram is converted to the substance unit of the model instead of being assumed to match. ## Units of a model The units are read into a `UnitsInformation`, a mapping from identifier to unit string with the unit registry of the model: ```python from sbmlsim.resources import DEMO_SBML from sbmlsim.units import UnitsInformation uinfo = UnitsInformation.from_sbml(DEMO_SBML) print(uinfo["Vmax_bA"]) print(uinfo["e__A"]) # amount of the species print(uinfo["[e__A]"]) # concentration of the species ``` Every `RoadrunnerSBMLModel` and every `SimulatorSerial` carry the units of their model as `uinfo`, and `Q_` is the quantity constructor of the registry: ```python from sbmlsim.simulator import SimulatorSerial simulator = SimulatorSerial(model=DEMO_SBML) Q_ = simulator.Q_ dose = Q_(10, "mmole") print(dose, dose.to("mole")) ``` ## Changes with units Changes of a `Timecourse` or a `Dimension` are quantities; they are converted to the units of the model before the simulation. A float without a unit is taken in the units of the model: ```python import numpy as np from sbmlsim.simulation import Dimension, ScanSim, Timecourse, TimecourseSim scan = ScanSim( simulation=TimecourseSim( Timecourse( start=0, end=10, steps=100, changes={ "[e__A]": Q_(10, "mM"), "[e__B]": Q_(1, "mmole/litre"), "[e__C]": Q_(1, "mole/m**3"), "c__A": Q_(1e-5, "mole"), "c__B": Q_(10, "µmole"), "Vmax_bA": Q_(300.0, "mole/min"), }, ) ), dimensions=[ Dimension("dim1", changes={"[e__A]": Q_(np.linspace(5, 15, num=5), "mM")}), ], ) xres = simulator.run_scan(scan) print(xres["[e__A]"].values[0]) ``` A change with a unit which cannot be converted to the model unit raises a `DimensionalityError`, which is the point: a dose in `mg` for a parameter in `mmole` is a mistake the units catch. `UnitsInformation.normalize_changes` performs the conversion; the `normalize` methods of `Timecourse`, `TimecourseSim` and `ScanSim` call it before a simulation. ## Results with units The result of a simulation knows the units of its variables, so reductions return quantities: ```python print(xres.uinfo["[e__A]"]) mean = xres.dim_mean("[e__A]") print(mean.units) print(mean.to("mole/litre").magnitude[:3]) ``` Data in a `Data` object or a `DataSet` are converted into requested units the same way, see [Data](data.md); the axes of a plot declare their unit and the curves are converted to it, see [Plots and reports](plotting.md). ## The unit registry All quantities of a model share one `UnitRegistry`. A `SimulationExperiment` creates a single registry which its models, datasets and results share, so quantities from different sources can be combined. The registry is extended with the units SBML uses, e.g., `mmole` for millimole, in `UnitsInformation._default_ureg`. --- # Simulation experiments A `SimulationExperiment` is the reproducible description of an experiment: the models, the datasets, the simulations, the tasks which apply a simulation to a model, the data derived from the results, and the figures and reports. The experiment is a python class; the `ExperimentRunner` executes it and writes results, figures and a JSON serialization. ## Defining an experiment An experiment subclasses `SimulationExperiment` and overrides the methods for its parts. Every method returns a dictionary keyed by identifier, and the parts reference each other by these identifiers: ```python from pathlib import Path from sbmlsim.data import Data from sbmlsim.experiment import SimulationExperiment from sbmlsim.model import AbstractModel from sbmlsim.plot import Axis, Figure from sbmlsim.resources import REPRESSILATOR_SBML from sbmlsim.simulation import AbstractSim, Timecourse, TimecourseSim from sbmlsim.task import Task class RepressilatorExperiment(SimulationExperiment): """Repressilator with a perturbation of X.""" def models(self) -> dict[str, AbstractModel | Path]: return {"model": REPRESSILATOR_SBML} def simulations(self) -> dict[str, AbstractSim]: return { "tc": TimecourseSim( [ Timecourse(start=0, end=100, steps=100), Timecourse(start=0, end=100, steps=100, changes={"X": 10}), ] ) } def tasks(self) -> dict[str, Task]: return {"task_tc": Task(model="model", simulation="tc")} def data(self) -> dict[str, Data]: data = [Data(sid, task="task_tc") for sid in ["time", "[X]", "[Y]", "[Z]"]] return {d.sid: d for d in data} def figures(self) -> dict[str, Figure]: fig = Figure(experiment=self, sid="fig1", name="Repressilator", num_rows=1) plots = fig.create_plots( xaxis=Axis("time", unit="second"), yaxis=Axis("concentration", unit="dimensionless"), legend=True, ) for sid in ["[X]", "[Y]", "[Z]"]: plots[0].curve( x=Data("time", task="task_tc"), y=Data(sid, task="task_tc"), label=sid ) return {"fig1": fig} ``` - **models** are paths, URLs or `AbstractModel` objects with changes, see [Models](models.md). They are resolved relative to the `base_path` of the experiment. - **simulations** are `TimecourseSim` or `ScanSim` objects, see [Timecourse simulations](simulation.md) and [Parameter scans](scans.md). - **tasks** apply a simulation to a model; the results of the experiment are keyed by task. - **data** are `Data` objects referencing a task or a dataset, see [Data](data.md). - **figures** are `Figure` objects with plots and curves, see [Plots and reports](plotting.md). - **datasets** (not used here) are `DataSet` objects with experimental data, see [Data](data.md). ## Running an experiment The `ExperimentRunner` creates the experiments, loads their models into a simulator and runs them. The results, the figures (`svg` by default) and the JSON serialization of every experiment are written below `output_path`: ```python from sbmlsim.experiment import ExperimentRunner from sbmlsim.simulator import SimulatorSerial runner = ExperimentRunner( [RepressilatorExperiment], simulator=SimulatorSerial(), base_path=Path.cwd(), data_path=Path.cwd(), ) results = runner.run_experiments(output_path=Path.cwd() / "results") print(results[0].experiment) print(sorted(p.name for p in (Path.cwd() / "results").rglob("*") if p.is_file())) ``` `base_path` is the directory the model sources are resolved against, `data_path` the directory of the datasets. The `results` of an experiment are the `XResult` of every task: ```python experiment = results[0].experiment xres = experiment.results["task_tc"] print(xres["[X]"].values[-3:]) ``` `run_experiments(reduced_selections=True)` records only the variables the data of the experiment refer to, which speeds up large experiments; `reduced_selections=False` records everything. ## Reports `ExperimentReport` renders the results of one or several experiments into an HTML (or markdown) report with the figures, the models and the simulations of every experiment: ```python from sbmlsim.report.experiment_report import ExperimentReport report = ExperimentReport(results) report_path = report.create_report(output_path=Path.cwd() / "results") print(report_path) ``` ## Serialization Every experiment is serialized to JSON when it is run, `.json` below the output path, with the models, simulations, tasks, data and figures. The serialization is the basis of the [SED-ML export](sedml.md) of an experiment: ```python print(experiment.to_json()[:300]) ``` ## Where to look The [examples](https://github.com/matthiaskoenig/sbmlsim/tree/develop/examples) contain complete experiments: `examples/initial_assignment` for a model with changes and two simulations, `examples/curve_types` for the curve types of the plots, `examples/glucose` for an experiment with datasets and a dose response scan, and `examples/midazolam` for pharmacokinetics experiments with data from several studies and the parameter fitting problems built on them. --- # Data Simulation experiments compare simulations with experimental data. `sbmlsim` represents experimental data as `DataSet` objects, data frames which know the units of their columns, and references both simulation results and datasets through `Data` objects, which plots, functions and fit mappings consume. ## Datasets A `DataSet` is a `pandas.DataFrame` with units. It is created from a data frame whose columns declare their units, either with a `*_unit` column per column or with one `unit` column which applies to `mean`, `value`, `median` and their `sd`/`se` columns: ```python import pandas as pd from sbmlsim.data import DataSet from sbmlsim.units import UnitsInformation df = pd.DataFrame( { "time": [0.0, 10.0, 20.0, 30.0], "time_unit": ["min"] * 4, "mean": [0.0, 2.1, 1.6, 1.1], "mean_sd": [0.0, 0.3, 0.2, 0.2], "mean_unit": ["mg/l"] * 4, } ) ureg = UnitsInformation._default_ureg() dset = DataSet.from_df(df, ureg=ureg) print(dset.uinfo["time"], dset.uinfo["mean"]) print(dset) ``` The units of the dataset are a `UnitsInformation` like the units of a model, see [Units](units.md). A column is read as a quantity and converted: ```python q = dset.get_quantity("mean") print(q.to("mg/dl")) ``` In an experiment the datasets are returned by `datasets()`, keyed by identifier, and read from the `data_path` of the experiment. `load_pkdb_dataframe` reads the TSV files of a [PK-DB](https://pk-db.com) study, see `examples/glucose/experiments/dose_response.py`. ## Data references A `Data` object is a promise for data: it names a variable of a task, a column of a dataset, or a function of other data, and is resolved when the experiment has run. A plot curve or a fit mapping is described with `Data` objects before any simulation exists: ```python from sbmlsim.data import Data x = Data("time", task="task_tc") y = Data("[X]", task="task_tc") y_data = Data("mean", dataset="dset1") print(x.sid, x.dtype, y.selection) print(y_data.sid, y_data.dtype) ``` A species in brackets is a concentration, without brackets an amount; `Data.selection` is the roadrunner selection recorded for it. In an experiment the `data()` method returns the `Data` objects, and every variable used in a figure or a fit must be declared there, since the selections of the simulation are reduced to them. ## Functions of data Data can be a function of other data, written as a formula with the referenced data as variables. The function is evaluated on the resolved data with their units: ```python f = Data( "[X]_ratio", function="x / y", variables={"x": Data("[X]", task="task_tc"), "y": Data("[Y]", task="task_tc")}, ) print(f.sid, f.dtype, f.function) ``` ## Resolving data `Data.get_data(experiment)` returns the quantity for the data in a run experiment, i.e., the values of the task result or the dataset column with their units, optionally converted to other units: ```python from pathlib import Path from sbmlsim.experiment import ExperimentRunner, SimulationExperiment from sbmlsim.model import AbstractModel from sbmlsim.resources import REPRESSILATOR_SBML from sbmlsim.simulation import AbstractSim, Timecourse, TimecourseSim from sbmlsim.simulator import SimulatorSerial from sbmlsim.task import Task class DataExperiment(SimulationExperiment): def models(self) -> dict[str, AbstractModel | Path]: return {"model": REPRESSILATOR_SBML} def simulations(self) -> dict[str, AbstractSim]: return {"tc": TimecourseSim(Timecourse(start=0, end=100, steps=100))} def tasks(self) -> dict[str, Task]: return {"task_tc": Task(model="model", simulation="tc")} def data(self) -> dict[str, Data]: data = [Data(sid, task="task_tc") for sid in ["time", "[X]"]] return {d.sid: d for d in data} runner = ExperimentRunner( [DataExperiment], simulator=SimulatorSerial(), base_path=Path.cwd(), data_path=Path.cwd(), ) results = runner.run_experiments(output_path=Path.cwd() / "results") experiment = results[0].experiment time = Data("time", task="task_tc").get_data(experiment) print(time.units, time.magnitude[:3]) x = Data("[X]", task="task_tc").get_data(experiment, to_units="dimensionless") print(x.units, x.magnitude[:3]) ``` ## Data generators A `DataGenerator` (see `sbmlsim.combine.datagenerator`) post-processes results, e.g., `DataGeneratorIndexingFunction` reduces a scan result to a single time point, which turns a scan over doses into a dose response, see `examples/datagenerator.py`. --- # Plots and reports Figures of a simulation experiment are described independent of the plotting backend: a `Figure` holds `Plot` panels, a plot has axes and `Curve` objects, and a curve references `Data` with a `Style`. The description is serialized with the experiment and exported to SED-ML; matplotlib renders it. ## Figures and plots A `Figure` belongs to an experiment and has a grid of `num_rows` x `num_cols` panels. `create_plots` creates one `Plot` per panel with the given axes: ```python from sbmlsim.plot import Axis, Figure fig = Figure(experiment=None, sid="fig1", name="Repressilator", num_rows=1, num_cols=2) plots = fig.create_plots( xaxis=Axis("time", unit="second"), yaxis=Axis("concentration", unit="dimensionless"), legend=True, ) plots[0].set_title("timecourse") plots[1].set_title("phase plane") plots[1].set_xaxis("[X]", unit="dimensionless") print(fig, len(plots)) ``` An `Axis` has a label and a unit, which together form the axis label, a `scale` (`linear` or `log`), `min`, `max`, `grid` and visibility flags. The data plotted on an axis are converted to its unit, see [Units](units.md). ## Curves A curve plots `Data` against `Data`, with optional error data, see [Data](data.md). `Plot.curve` adds a curve with matplotlib style keywords, `Plot.add_data` is the shortcut which creates the `Data` objects from a task or dataset: ```python from sbmlsim.data import Data plots[0].curve( x=Data("time", task="task_tc"), y=Data("[X]", task="task_tc"), label="X", color="tab:blue", linewidth=2.0, ) plots[0].add_data(task="task_tc", xid="time", yid="[Y]", label="Y", color="tab:red") plots[1].add_data(task="task_tc", xid="[X]", yid="[Y]", label="Y ~ X", color="black") print([c.name for c in plots[0].curves]) ``` Experimental data are added from a dataset with their errors and the count of the measurements: ```python plots[0].add_data( dataset="dset1", xid="time", yid="mean", yid_sd="mean_sd", label="data", color="black", ) ``` `count` names the column with the number of measurements behind a mean, which is shown in the legend and used as weight in a fit. The `CurveType` of a curve is `POINTS` (lines and markers), `BAR`, `BARSTACKED`, `HORIZONTALBAR` or `HORIZONTALBARSTACKED`; `ShadedArea` fills the area between two data curves. `examples/curve_types` shows all of them. ## Styles A `Style` bundles the `Line` (type, color, thickness), the `Marker` (type, size, fill, line color) and the `Fill` of a curve. Matplotlib keywords such as `color`, `linestyle`, `linewidth`, `marker` and `alpha` are translated into a style, so a curve is styled either way: ```python from sbmlsim.plot.plotting import ColorType, Line, LineType, Marker, MarkerType, Style style = Style( line=Line(color=ColorType("tab:green"), type=LineType.DASH, thickness=1.5), marker=Marker(type=MarkerType.SQUARE, size=4, fill=ColorType("white")), ) plots[0].curve( x=Data("time", task="task_tc"), y=Data("[Z]", task="task_tc"), style=style ) print(style) ``` Colors are `ColorType` objects, created from matplotlib color names or hex strings, which are serialized to the `#RRGGBBAA` colors of SED-ML. ## Rendering The `ExperimentRunner` renders the figures of every experiment with matplotlib and writes them to the output path in the `figure_formats` (`svg` by default). `MatplotlibFigureSerializer.to_figure` renders a single figure from a run experiment; `Figure.fig_dpi`, `Figure.axes_labelsize` and the other class attributes of `Figure` are the global matplotlib settings of the rendering. ## Reports `ExperimentReport` collects `ExperimentResult` objects (or a stored `ReportResults`) and renders an HTML report with an index page and one page per experiment, listing the models, simulations, tasks, datasets, data and figures with the rendered images. The report is written next to the results, so the relative paths of the images resolve: ```python from pathlib import Path from sbmlsim.report.experiment_report import ExperimentReport # results = runner.run_experiments(output_path=Path.cwd() / "results") # ExperimentReport(results).create_report(output_path=Path.cwd() / "results") ``` `ReportResults.to_json` and `from_json` store the report data, so reports of experiments run at different times are combined. The report templates are jinja2 templates in `sbmlsim/resources/templates/`; `create_report(report_type=ExperimentReport.ReportType.MARKDOWN)` renders markdown instead of HTML. --- # Parameter fitting Parameter fitting adjusts model parameters so that the simulations of experiments match the experimental data. In `sbmlsim` a fit is an `OptimizationProblem` built from `FitExperiment` objects, which name the simulation experiments and their fit mappings, and `FitParameter` objects with the bounds of the parameters. The problem is run with local or global optimizers of scipy and analysed with `OptimizationAnalysis`. ## Fit mappings A fit mapping pairs a reference, the experimental data, with an observable, the simulated variable. It is defined in the `fit_mappings()` of a `SimulationExperiment` with `FitData` objects, which are `Data` references (see [Data](data.md)) with their errors and counts: ```python from sbmlsim.fit import FitData, FitMapping # inside SimulationExperiment.fit_mappings() # reference: the dataset column and its error, observable: the task variable mapping_code = """ def fit_mappings(self) -> dict[str, FitMapping]: return { "fm_mid_iv": FitMapping( self, reference=FitData( self, dataset="Fig1_midazolam_iv", xid="time", yid="mean", yid_sd="mean_sd" ), observable=FitData(self, task="task_mid_iv", xid="time", yid="[Cve_mid]"), metadata=None, ), } """ print(mapping_code) ``` The units of the reference and the observable are compared and the reference is converted to the units of the model. A `MappingMetaData` on a mapping carries application specific information such as the tissue or the dosing and an `outlier` flag which excludes the mapping from the fit; `sbmlsim.fit.helpers` collects this metadata into a table. ## Fit parameters and experiments `FitParameter` names a parameter of the model with its start value, bounds and unit, `FitExperiment` names an experiment class and the mappings of it which enter the fit, with optional weights: ```python from sbmlsim.fit import FitExperiment, FitParameter from examples.midazolam.experiments.kupferschmidt1995 import Kupferschmidt1995 fit_experiments = [ FitExperiment(experiment=Kupferschmidt1995, mappings=["fm_mid_iv", "fm_mid1oh_iv"]), ] fit_parameters = [ FitParameter( pid="LI__MIDIM_Vmax", start_value=0.1, lower_bound=1e-5, upper_bound=1e3, unit="mmole_per_min", ), FitParameter( pid="KI__MID1OHEX_Km", start_value=100, lower_bound=1e-5, upper_bound=1e-1, unit="mM", ), ] print(fit_experiments[0]) print(FitParameter.parameters_to_df(fit_parameters)) ``` `FitExperiment(use_mapping_weights=True)` weights the mappings by the weights of the `FitMapping` objects, e.g., the counts of the data, instead of the weights given here. ## The optimization problem The `OptimizationProblem` collects the fit experiments and parameters with the `base_path` and `data_path` of the experiments: ```python from examples.midazolam import MIDAZOLAM_PATH from sbmlsim.fit.optimization import OptimizationProblem op = OptimizationProblem( opid="mid_iv", fit_experiments=fit_experiments, fit_parameters=fit_parameters, base_path=MIDAZOLAM_PATH, data_path=MIDAZOLAM_PATH / "data", ) print(op) ``` The problem is picklable, so it is distributed to worker processes; `initialize` then creates the runner, loads the models, resolves the data and calculates the weights. The options of the initialization define how the residuals are computed: - `ResidualType`: `ABSOLUTE` residuals or `NORMALIZED` residuals, i.e., relative to the data, - `LossFunctionType`: `LINEAR`, `SOFT_L1`, `CAUCHY` or `ARCTAN` as in `scipy.optimize.least_squares`, - `WeightingCurvesType`: weighting of the curves by their `MAPPING` weight and by the number of `POINTS`, - `WeightingPointsType`: `NO_WEIGHTING` or `ERROR_WEIGHTING` of the points by their errors. ## Running the optimization `run_optimization` samples `size` start points within the bounds (see `sbmlsim.fit.sampling`), runs the optimizer from every start point, in parallel on `n_cores`, and returns an `OptimizationResult`: ```py from sbmlsim.fit.options import ( OptimizationAlgorithmType, ResidualType, WeightingCurvesType, WeightingPointsType, ) from sbmlsim.fit.runner import run_optimization opt_result = run_optimization( problem=op, size=10, n_cores=4, seed=1234, algorithm=OptimizationAlgorithmType.LEAST_SQUARE, residual=ResidualType.NORMALIZED, weighting_curves=[WeightingCurvesType.POINTS], weighting_points=WeightingPointsType.ERROR_WEIGHTING, ) ``` `OptimizationAlgorithmType.LEAST_SQUARE` is the local least squares optimizer, `DIFFERENTIAL_EVOLUTION` the global one. The `OptimizationResult` holds the fits of all start points with their costs, the optimal parameters `xopt`, and the trajectories of the optimizer; it is stored as JSON and TSV with `to_json` and `to_tsv`, and results of several runs are combined with `OptimizationResult.combine`. ## Analysing the fit `OptimizationAnalysis` writes the report of a fit: the parameter table with the bounds, the correlation of the parameters over the fits, waterfall and trajectory plots of the optimizations, and, when the problem is passed, the fitted curves against the data with the residuals for every mapping: ```py from sbmlsim.fit.analysis import OptimizationAnalysis analysis = OptimizationAnalysis( opt_result=opt_result, output_name="mid_iv", output_dir=Path("results"), op=op, residual=ResidualType.NORMALIZED, weighting_curves=[WeightingCurvesType.POINTS], weighting_points=WeightingPointsType.ERROR_WEIGHTING, ) analysis.run() ``` The complete fitting problems of the midazolam model are in `examples/midazolam/fitting_problems.py` and run by `examples/midazolam/fitting_example.py`. ## PEtab `sbmlsim.fit.petab_omex` packages a [PEtab](https://petab.readthedocs.io) parameter estimation problem, i.e., the model, the condition, observable, measurement and parameter tables and the PEtab YAML, as a COMBINE archive with `create_petab_omex`, so that the problem is exchanged with other tools. `examples/petab/` shows PEtab problems solved with pypesto and AMICI, which are not dependencies of sbmlsim. --- # Sensitivity analysis Sensitivity analysis quantifies how the outputs of a model depend on its parameters. `sbmlsim.sensitivity` implements local sensitivities by finite differences and the global methods of [SALib](https://salib.readthedocs.io): sampling based uncertainty analysis, the Morris screening method, Sobol indices and the Fourier amplitude sensitivity test (FAST), see [References](references.md#sensitivity-analysis). All methods share the same description of the problem, i.e., the simulation which computes the outputs, the parameters with their bounds, and the groups of model conditions under which the analysis is run. ## The sensitivity simulation A `SensitivitySimulation` defines what is simulated and which scalar outputs are computed from the timecourse. It is a subclass with a `simulate` method which receives the roadrunner instance and the parameter changes of one sample and returns the outputs. The samples are simulated in worker processes, so the class has to live in an importable module, here `examples/sensitivity/sensitivity_example.py`: ```py import numpy as np import roadrunner from sbmlsim.sensitivity import SensitivityOutput, SensitivitySimulation class ChainSimulation(SensitivitySimulation): """Simulation of the simple chain model with its outputs.""" def simulate( self, r: roadrunner.RoadRunner, changes: dict[str, float] ) -> dict[str, float]: self.apply_changes(r, {**self.changes_simulation, **changes}, reset_all=True) s = r.simulate(start=0, end=1000, steps=1000) t = s["time"] y: dict[str, float] = {} for key in ["S1", "S2", "S3"]: v = s[f"[{key}]"] y[f"[{key}]_auc"] = np.trapezoid(y=v, x=t) y["[S2]_max"] = np.max(s["[S2]"]) return y ``` The outputs are declared as `SensitivityOutput` objects, the changes of the simulation (e.g., a dose) as `changes_simulation`: ```python from examples.sensitivity.sensitivity_example import ( ExampleSensitivitySimulation, model_path, ) from sbmlsim.sensitivity import SensitivityOutput simulation = ExampleSensitivitySimulation( model_path=model_path, selections=["time", "[S1]", "[S2]", "[S3]"], changes_simulation={}, outputs=[ SensitivityOutput(uid="[S1]_auc", name="[S1] AUC", unit=None), SensitivityOutput(uid="[S2]_auc", name="[S2] AUC", unit=None), SensitivityOutput(uid="[S3]_auc", name="[S3] AUC", unit=None), SensitivityOutput(uid="[S2]_max", name="[S2] maximum", unit=None), SensitivityOutput(uid="[S2]_tmax", name="[S2] time maximum", unit=None), ], ) ``` ## Parameters and groups `SensitivityParameter.parameters_from_sbml` reads the constant parameters of the model with their values and units; the bounds of the analysis are set relative to the values or from data: ```python from sbmlsim.sensitivity import AnalysisGroup, SensitivityParameter parameters = SensitivityParameter.parameters_from_sbml( sbml_path=model_path, exclude_ids=None, exclude_na=True, exclude_zero=True ) for p in parameters: p.lower_bound = p.value * 0.85 p.upper_bound = p.value * 1.15 print(SensitivityParameter.parameters_to_df(parameters)) ``` An `AnalysisGroup` is a condition of the model under which the sensitivities are computed, e.g., a low and a high initial concentration. Every group is analysed separately and the results are compared across groups: ```python groups = [ AnalysisGroup(uid="lowS1", name="Low S1", changes={"[S1]": 0.1}, color="tab:red"), AnalysisGroup(uid="highS1", name="High S1", changes={"[S1]": 10}, color="tab:blue"), ] ``` ## Running an analysis Every analysis is created with the simulation, the parameters, the groups and a `results_path`; `execute` creates the samples, simulates them (in parallel on `n_cores`, cached with `cache_results=True`) and computes the sensitivities, `plot` writes the figures into the results path: ```python from pathlib import Path from sbmlsim.sensitivity import LocalSensitivityAnalysis sa_local = LocalSensitivityAnalysis( sensitivity_simulation=simulation, parameters=parameters, groups=groups, results_path=Path.cwd() / "sensitivity" / "local", difference=0.01, n_cores=1, seed=1234, ) sa_local.execute() print(sa_local.sensitivity_df(group_id="lowS1", key="normalized")) ``` The local analysis varies every parameter by the relative `difference` around its value and reports the raw and the normalized sensitivities, i.e., the relative change of the output per relative change of the parameter. The global methods sample the parameter space within the bounds: ```py from sbmlsim.sensitivity import ( FASTSensitivityAnalysis, MorrisSensitivityAnalysis, SamplingSensitivityAnalysis, SobolSensitivityAnalysis, ) kwargs = dict( sensitivity_simulation=simulation, parameters=parameters, groups=groups, n_cores=4 ) sa_sampling = SamplingSensitivityAnalysis( results_path=Path("sampling"), N=1000, **kwargs ) sa_sobol = SobolSensitivityAnalysis(results_path=Path("sobol"), N=4096, **kwargs) sa_fast = FASTSensitivityAnalysis(results_path=Path("fast"), N=1000, **kwargs) sa_morris = MorrisSensitivityAnalysis( results_path=Path("morris"), N=100, num_levels=4, optimal_trajectories=25, **kwargs ) for sa in [sa_sampling, sa_sobol, sa_fast, sa_morris]: sa.execute() sa.plot() ``` - **Sampling** draws `N` parameter sets and reports the distribution of every output (mean, median, standard deviation, coefficient of variation, quantiles), i.e., an uncertainty analysis. - **Sobol** computes the first order (`S1`) and total (`ST`) variance based indices with the Saltelli sampling scheme; `N` samples per parameter. - **FAST** computes first order and total indices with the extended Fourier amplitude sensitivity test. - **Morris** computes the elementary effects `mu`, `mu_star` and `sigma` of the screening method, with `num_levels` grid levels and `optimal_trajectories` trajectories. The sensitivities are stored as `xarray.DataArray` objects per group and key (`sa.sensitivity[group_id][key]`), returned as data frames with `sensitivity_df`, and written as tables and figures into the results path. `sbmlsim.sensitivity.classification` groups parameters by their sensitivities across outputs. The complete example with all methods is `examples/sensitivity/sensitivity_example.py`. --- # SED-ML and COMBINE archives The [Simulation Experiment Description Markup Language](https://sed-ml.org) (SED-ML) describes simulation experiments in a tool independent way: the models with their changes, the simulations and tasks, the data generators computed from the results and the plots and reports. A [COMBINE archive](https://co.mbine.org/standards/omex) (OMEX) packages a SED-ML document with its models and data in one file. `sbmlsim.combine.sedml` reads SED-ML Level 1 Version 4 into a `SimulationExperiment` and executes it, and serializes an experiment to SED-ML, see [References](references.md#standards). ## Executing SED-ML `execute_sedml` reads a SED-ML file or a COMBINE archive, builds the simulation experiment and runs it, writing the results and figures into the `output_path`. The `working_dir` is the directory the models are resolved against and, for an archive, the directory it is extracted to: ```python from pathlib import Path from sbmlsim.combine.sedml.runner import execute_sedml # from the root of the repository, the paths have to be absolute sedml_path = Path("examples/sedml/l1v4/algorithm_parameters.sedml").resolve() output_path = Path("results").resolve() / "algorithm_parameters" execute_sedml(path=sedml_path, working_dir=sedml_path.parent, output_path=output_path) print(sorted(p.name for p in output_path.rglob("*") if p.is_file())[:5]) ``` The example SED-ML files of Level 1 Version 4 with the corresponding reference figures are in `examples/sedml/l1v4/`, the SED-ML test cases used by the tests in `tests/data/sedml/` and `tests/data/combine/`. ## The SED-ML parser `SEDMLReader` reads the document from a file, a string or an archive, `SEDMLParser` translates it into an experiment class: ```python from sbmlsim.combine.sedml.io import SEDMLReader from sbmlsim.combine.sedml.parser import SEDMLParser reader = SEDMLReader(source=sedml_path, working_dir=sedml_path.parent) parser = SEDMLParser( sed_doc=reader.sed_doc, exec_dir=reader.exec_dir, working_dir=sedml_path.parent, name="algorithm_parameters", ) print(parser.models.keys()) print(parser.simulations.keys()) print(parser.tasks.keys()) print(parser.figures.keys()) ``` The parser maps the SED-ML objects onto the objects of `sbmlsim`: | SED-ML | sbmlsim | | --- | --- | | `model` with `changeAttribute` changes | `AbstractModel` with changes; the XPath targets are resolved to the ids of the model | | `uniformTimeCourse`, `oneStep` | `TimecourseSim` | | `repeatedTask` with `ranges` | `ScanSim` with `Dimension` objects, see `sbmlsim.simulation.range` | | `algorithm` and `algorithmParameter` | `Algorithm` and `AlgorithmParameter` with their KISAO terms, see `sbmlsim.simulation.kisaos` | | `dataGenerator` with `variable` and `math` | `Data`, functions of data are evaluated with `sbmlsim.combine.mathml` | | `dataDescription` with NuML, CSV or TSV data | `DataSet`, see `sbmlsim.combine.sedml.data` | | `plot2D`, `curve`, `shadedArea`, `style`, `axis` | `Figure`, `Plot`, `Curve`, `ShadedArea`, `Style`, `Axis`, see [Plots and reports](plotting.md) | | `report` | the report of the experiment, i.e., a table of data generators | Steady state simulations, `plot3D` and the parameter estimation tasks are not supported. ## Serializing an experiment to SED-ML `SEDMLSerializer` writes a `SimulationExperiment` as a SED-ML document with the models and, when an `omex_path` is given, as a COMBINE archive. The experiment is run first to resolve the models and their selections: ```py from sbmlsim.combine.sedml.parser import SEDMLSerializer SEDMLSerializer( exp_class=RepressilatorExperiment, working_dir=Path("results") / "omex", sedml_filename="repressilator.sedml", omex_path=Path("results") / "repressilator.omex", ) ``` `examples/covid/simulate.py` runs the COVID-19 experiments, serializes them to archives and executes the archives again, `examples/midazolam/simulate.py` does the same for the midazolam experiments. ## COMBINE archives Archives are read and written with [pymetadata](https://github.com/matthiaskoenig/pymetadata), which resolves the manifest and the formats of the entries. The master SED-ML file of an archive is executed by `execute_sedml`, or the first SED-ML file if none is flagged as master. The models of the [BioModels](https://www.ebi.ac.uk/biomodels/) database are downloaded as archives with `sbmlutils.biomodels.download_biomodel_omex`, see `examples/covid/omex/download_covid_models.py`. --- # References `sbmlsim` builds on the standards of the [COMBINE](https://co.mbine.org) community and on the simulation and analysis libraries of the scientific python ecosystem. These are the publications behind them; cite them when you describe a simulation experiment, and cite `sbmlsim` itself as described in [Home](index.md#how-to-cite). ## Simulation **libRoadRunner.** The SBML simulation engine all simulations run on. > Welsh C, Xu J, Smith L, König M, Choi K, Sauro HM. > **libRoadRunner 2.0: a high performance SBML simulation and analysis library.** > *Bioinformatics.* 2023;39(1):btac770. > [doi:10.1093/bioinformatics/btac770](https://doi.org/10.1093/bioinformatics/btac770) > Somogyi ET, Bouteiller JM, Glazier JA, König M, Medley JK, Swat MH, Sauro HM. > **libRoadRunner: a high performance SBML simulation and analysis library.** > *Bioinformatics.* 2015;31(20):3315-3321. > [doi:10.1093/bioinformatics/btv363](https://doi.org/10.1093/bioinformatics/btv363) ## Standards **SBML Level 3.** The format of the models. > Keating SM, Waltemath D, König M, Zhang F, Dräger A, Chaouiya C, Bergmann FT, Finney A, Gillespie CS, Helikar T, Hoops S, Malik-Sheriff RS, Moodie SL, Moraru II, Myers CJ, Naldi A, Olivier BG, Sahle S, Schaff JC, Smith LP, Swat MJ, Thieffry D, Watanabe L, Wilkinson DJ, Blinov ML, Begley K, Faeder JR, Gómez HF, Hamm TM, Inagaki Y, Liebermeister W, Lister AL, Lucio D, Mjolsness E, Proctor CJ, Raman K, Rodriguez N, Shaffer CA, Shapiro BE, Stelling J, Swainston N, Tanimura N, Wagner J, Meier-Schellersheim M, Sauro HM, Palsson B, Bolouri H, Kitano H, Funahashi A, Hermjakob H, Doyle JC, Hucka M; SBML Level 3 Community members. > **SBML Level 3: an extensible format for the exchange and reuse of biological models.** > *Molecular Systems Biology.* 2020;16(8):e9110. > [doi:10.15252/msb.20199110](https://doi.org/10.15252/msb.20199110) **SED-ML.** The description of simulation experiments which `sbmlsim.combine.sedml` reads and executes, see [SED-ML and COMBINE archives](sedml.md). > Smith LP, Bergmann FT, Garny A, Helikar T, Karr J, Nickerson D, Sauro H, Waltemath D, König M. > **The simulation experiment description markup language (SED-ML): language specification for level 1 version 4.** > *Journal of Integrative Bioinformatics.* 2021;18(3):20210021. > [doi:10.1515/jib-2021-0021](https://doi.org/10.1515/jib-2021-0021) > Waltemath D, Adams R, Bergmann FT, Hucka M, Kolpakov F, Miller AK, Moraru II, Nickerson D, Sahle S, Snoep JL, Le Novère N. > **Reproducible computational biology experiments with SED-ML — the Simulation Experiment Description Markup Language.** > *BMC Systems Biology.* 2011;5:198. > [doi:10.1186/1752-0509-5-198](https://doi.org/10.1186/1752-0509-5-198) **COMBINE archive.** The container for models, simulation experiments and data. > Bergmann FT, Adams R, Moodie S, Cooper J, Glont M, Golebiewski M, Hucka M, Laibe C, Miller AK, Nickerson DP, Olivier BG, Rodriguez N, Sauro HM, Scharm M, Soiland-Reyes S, Waltemath D, Yvon F, Le Novère N. > **COMBINE archive and OMEX format: one file to share all information to reproduce a modeling project.** > *BMC Bioinformatics.* 2014;15:369. > [doi:10.1186/s12859-014-0369-z](https://doi.org/10.1186/s12859-014-0369-z) **KISAO.** The ontology of simulation algorithms and their parameters, see `sbmlsim.simulation.kisaos`. > Courtot M, Juty N, Knüpfer C, Waltemath D, Zhukova A, Dräger A, Dumontier M, Finney A, Golebiewski M, Hastings J, Hoops S, Keating S, Kell DB, Kerrien S, Lawson J, Lister A, Lu J, Machne R, Mendes P, Pocock M, Rodriguez N, Villeger A, Wilkinson DJ, Wimalaratne S, Laibe C, Hucka M, Le Novère N. > **Controlled vocabularies and semantics in systems biology.** > *Molecular Systems Biology.* 2011;7:543. > [doi:10.1038/msb.2011.77](https://doi.org/10.1038/msb.2011.77) **PEtab.** The specification of parameter estimation problems which `sbmlsim.fit.petab_omex` packages, see [Parameter fitting](fitting.md). > Schmiester L, Schälte Y, Bergmann FT, Camba T, Dudkin E, Egert J, Fröhlich F, Fuhrmann L, Hauber AL, Kemmer S, Lakrisenko P, Loos C, Merkt S, Müller W, Pathirana D, Raimúndez E, Refisch L, Rosenblatt M, Stapor PL, Städter P, Wang D, Wieland FG, Banga JR, Timmer J, Villaverde AF, Sahle S, Kreutz C, Hasenauer J, Weindl D. > **PEtab — Interoperable specification of parameter estimation problems in systems biology.** > *PLoS Computational Biology.* 2021;17(1):e1008646. > [doi:10.1371/journal.pcbi.1008646](https://doi.org/10.1371/journal.pcbi.1008646) ## Sensitivity analysis The global methods of `sbmlsim.sensitivity` are the implementations of [SALib](https://salib.readthedocs.io), see [Sensitivity analysis](sensitivity.md). > Herman J, Usher W. > **SALib: An open-source Python library for Sensitivity Analysis.** > *Journal of Open Source Software.* 2017;2(9):97. > [doi:10.21105/joss.00097](https://doi.org/10.21105/joss.00097) > Iwanaga T, Usher W, Herman J. > **Toward SALib 2.0: Advancing the accessibility and interpretability of global sensitivity analyses.** > *Socio-Environmental Systems Modelling.* 2022;4:18155. > [doi:10.18174/sesmo.18155](https://doi.org/10.18174/sesmo.18155) **Sobol indices.** Variance based first order and total effect indices. > Sobol' IM. > **Global sensitivity indices for nonlinear mathematical models and their Monte Carlo estimates.** > *Mathematics and Computers in Simulation.* 2001;55(1-3):271-280. > [doi:10.1016/S0378-4754(00)00270-6](https://doi.org/10.1016/S0378-4754(00)00270-6) > Saltelli A, Annoni P, Azzini I, Campolongo F, Ratto M, Tarantola S. > **Variance based sensitivity analysis of model output. Design and estimator for the total sensitivity index.** > *Computer Physics Communications.* 2010;181(2):259-270. > [doi:10.1016/j.cpc.2009.09.018](https://doi.org/10.1016/j.cpc.2009.09.018) **Morris method.** Elementary effects screening. > Morris MD. > **Factorial sampling plans for preliminary computational experiments.** > *Technometrics.* 1991;33(2):161-174. > [doi:10.1080/00401706.1991.10484804](https://doi.org/10.1080/00401706.1991.10484804) > Campolongo F, Cariboni J, Saltelli A. > **An effective screening design for sensitivity analysis of large models.** > *Environmental Modelling & Software.* 2007;22(10):1509-1518. > [doi:10.1016/j.envsoft.2006.10.004](https://doi.org/10.1016/j.envsoft.2006.10.004) **FAST.** The Fourier amplitude sensitivity test and its extended form. > Cukier RI, Fortuin CM, Shuler KE, Petschek AG, Schaibly JH. > **Study of the sensitivity of coupled reaction systems to uncertainties in rate coefficients. I Theory.** > *The Journal of Chemical Physics.* 1973;59(8):3873-3878. > [doi:10.1063/1.1680571](https://doi.org/10.1063/1.1680571) > Saltelli A, Tarantola S, Chan KPS. > **A quantitative model-independent method for global sensitivity analysis of model output.** > *Technometrics.* 1999;41(1):39-56. > [doi:10.1080/00401706.1999.10485594](https://doi.org/10.1080/00401706.1999.10485594) ## Data structures **xarray.** Simulation results are stored as labeled N-dimensional arrays, see `sbmlsim.result.xresult`. > Hoyer S, Hamman J. > **xarray: N-D labeled arrays and datasets in Python.** > *Journal of Open Research Software.* 2017;5(1):10. > [doi:10.5334/jors.148](https://doi.org/10.5334/jors.148) --- # API reference The API reference is generated from the docstrings of the package. ## sbmlsim The top level modules: data, units and the shared output. | module | description | | --- | --- | | [data](data.md) | `Data` objects referencing simulation results and datasets, the input of plots and calculations | | [units](units.md) | unit registry of a model and unit conversions with pint | | [serialization](serialization.md) | JSON serialization of experiments | | [utils](utils.md) | timing and other helpers | | [console](console.md) | shared rich console | | [log](log.md) | logging of the package | ## sbmlsim.model Models and model changes, see [Models](../models.md). | module | description | | --- | --- | | [model.model](model.model.md) | `AbstractModel`, the model of a simulation experiment with its changes and selections | | [model.model_roadrunner](model.model_roadrunner.md) | `RoadrunnerSBMLModel`, the roadrunner instance of an SBML model with its units and parameter changes | | [model.model_change](model.model_change.md) | `ModelChange`, clamping species and other structural changes | | [model.model_resources](model.model_resources.md) | resolving model sources, i.e., files, URNs and URLs | ## sbmlsim.simulation Definition of simulations, see [Timecourse simulations](../simulation.md) and [Parameter scans](../scans.md). | module | description | | --- | --- | | [simulation.timecourse](simulation.timecourse.md) | `Timecourse` and `TimecourseSim`, concatenated timecourses with changes | | [simulation.scan](simulation.scan.md) | `ScanSim`, a simulation over the dimensions of parameter changes | | [simulation.sensitivity](simulation.sensitivity.md) | `ModelSensitivity`, sensitivity scans of parameters and initial conditions | | [simulation.range](simulation.range.md) | ranges of values for scans | | [simulation.change](simulation.change.md) | changes applied to a model before a simulation | | [simulation.algorithm](simulation.algorithm.md) | `Algorithm` and `AlgorithmParameter`, the KISAO description of an integrator | | [simulation.kisaos](simulation.kisaos.md) | the KISAO terms of the supported algorithms and parameters | | [simulation.calculation](simulation.calculation.md) | calculations on simulation results | | [simulation.base](simulation.base.md) | base classes shared by the simulation objects and SED-ML | | [simulation.simulation](simulation.simulation.md) | `AbstractSim`, the base of all simulations | ## sbmlsim.simulator, sbmlsim.task Execution of simulations. | module | description | | --- | --- | | [simulator.simulation_serial](simulator.simulation_serial.md) | `SimulatorSerial`, running timecourses and scans on a roadrunner model | | [task.task](task.task.md) | `Task`, a simulation applied to a model | ## sbmlsim.experiment, sbmlsim.result Simulation experiments and their results, see [Simulation experiments](../experiments.md). | module | description | | --- | --- | | [experiment.experiment](experiment.experiment.md) | `SimulationExperiment`, models, datasets, simulations, tasks, data and figures of an experiment | | [experiment.runner](experiment.runner.md) | `ExperimentRunner`, executing experiments and writing their results | | [result.xresult](result.xresult.md) | `XResult`, simulation results as an xarray dataset with units | | [result.datagenerator](result.datagenerator.md) | data generators processing results | | [result.report](result.report.md) | reports of results | ## sbmlsim.plot, sbmlsim.report Figures and reports, see [Plots and reports](../plotting.md). | module | description | | --- | --- | | [plot.plotting](plot.plotting.md) | `Figure`, `Plot`, `Axis`, `Curve` and their styles, the plot description independent of the backend | | [plot.serialization_matplotlib](plot.serialization_matplotlib.md) | rendering of the figures with matplotlib | | [report.experiment_report](report.experiment_report.md) | HTML and markdown reports of simulation experiments | ## sbmlsim.fit Parameter fitting, see [Parameter fitting](../fitting.md). | module | description | | --- | --- | | [fit.objects](fit.objects.md) | `FitParameter`, `FitMapping`, `FitData` and `FitExperiment`, the objects of a fit problem | | [fit.optimization](fit.optimization.md) | `OptimizationProblem`, the residuals and cost of a fit problem | | [fit.options](fit.options.md) | options of the optimization, i.e., algorithms, residuals, weighting and loss functions | | [fit.result](fit.result.md) | `OptimizationResult`, the result of an optimization with its analysis | | [fit.runner](fit.runner.md) | running optimizations serially or in parallel | | [fit.analysis](fit.analysis.md) | plots and tables of optimization results | | [fit.sampling](fit.sampling.md) | sampling of initial parameter values | | [fit.rmse](fit.rmse.md) | statistics of fits | | [fit.helpers](fit.helpers.md) | helpers for fitting | | [fit.petab_omex](fit.petab_omex.md) | COMBINE archives of PEtab problems | ## sbmlsim.sensitivity Local and global sensitivity analysis, see [Sensitivity analysis](../sensitivity.md). | module | description | | --- | --- | | [sensitivity.analysis](sensitivity.analysis.md) | the common analysis of a model, i.e., outputs, observables and the simulation of parameter samples | | [sensitivity.parameters](sensitivity.parameters.md) | selection, bounds and distributions of the analysed parameters | | [sensitivity.sensitivity_local](sensitivity.sensitivity_local.md) | local sensitivities by finite differences | | [sensitivity.sensitivity_sampling](sensitivity.sensitivity_sampling.md) | sampling based sensitivity and uncertainty analysis | | [sensitivity.sensitivity_morris](sensitivity.sensitivity_morris.md) | Morris elementary effects screening | | [sensitivity.sensitivity_sobol](sensitivity.sensitivity_sobol.md) | variance based Sobol indices | | [sensitivity.sensitivity_fast](sensitivity.sensitivity_fast.md) | Fourier amplitude sensitivity test (FAST) | | [sensitivity.classification](sensitivity.classification.md) | classification of sensitivities and uncertainties | | [sensitivity.plots](sensitivity.plots.md) | plots of the sensitivity results | ## sbmlsim.combine SED-ML, NuML and COMBINE archives, see [SED-ML and COMBINE archives](../sedml.md). | module | description | | --- | --- | | [combine.sedml.parser](combine.sedml.parser.md) | `SEDMLParser`, a SED-ML document into a simulation experiment | | [combine.sedml.runner](combine.sedml.runner.md) | executing SED-ML files and COMBINE archives | | [combine.sedml.task](combine.sedml.task.md) | tasks and repeated tasks of SED-ML | | [combine.sedml.data](combine.sedml.data.md) | data descriptions, i.e., NuML, CSV and TSV data | | [combine.sedml.numl](combine.sedml.numl.md) | parser for NuML data | | [combine.sedml.report](combine.sedml.report.md) | SED-ML reports | | [combine.sedml.io](combine.sedml.io.md) | reading and writing SED-ML documents | | [combine.datagenerator](combine.datagenerator.md) | data generators of SED-ML | | [combine.mathml](combine.mathml.md) | evaluation of MathML expressions | ## sbmlsim.interpolation, sbmlsim.comparison | module | description | | --- | --- | | [interpolation.interpolation](interpolation.interpolation.md) | interpolation of datasets as SBML models | | [comparison.diff](comparison.diff.md) | numerical comparison of simulation results from different simulators | --- # sbmlsim.data Module handling data (experiment and simulation). ## class `Data(index: 'str', symbol: 'Symbols | None' = None, task: 'str | None' = None, dataset: 'str | None' = None, function: 'str | None' = None, variables: 'dict[str, Data] | None' = None, parameters: 'dict[str, float] | None' = None, sid: 'str | None' = None)` Data. Main data generator class which uses data either from experimental data, simulations or via function calculations. All transformation of data and a tree of data operations. This is just a promise for data which will be fullfilled with data from tasks. ### `Data.Symbols(*values)` Symbols. ### `Data.Types(*values)` Data types. ### `Data.get_data(self, experiment, to_units: 'str | None' = None) -> 'Quantity'` Return actual data from the data object. The data is resolved from the available datasets and the injected Experiment. :param to_units: units to convert to :return: ### `Data.is_dataset(self) -> 'bool'` Check if dataset. ### `Data.is_function(self)` Check if function. ### `Data.is_task(self) -> 'bool'` Check if task. ### `Data.to_dict(self)` Convert to dictionary. ## class `DataSeries(data=None, index=None, dtype: 'Dtype | None' = None, name=None, copy: 'bool | None' = None) -> 'None'` DataSet - a pd.Series with additional unit information. ## class `DataSet(data=None, index: 'Axes | None' = None, columns: 'Axes | None' = None, dtype: 'Dtype | None' = None, copy: 'bool | None' = None) -> 'None'` DataSet, a pd.DataFrame with additional unit information. ### `DataSet.get_quantity(self, key: 'str')` Return quantity for given key. Requires using the numpy data instead of the series. ### `DataSet.unit_conversion(self, key, factor: 'Quantity') -> 'None'` Convert the units of the given key in the dataset via `key * factor`. Changes values in place in the DataSet. The quantity in the dataset is multiplied with the conversion factor. In addition to the key, also the respective error measures are converted with the same factor, i.e. - {key} - {key}_sd - {key}_se - {key}_min - {key}_max FIXME: in addition base keys should be updated in the table, i.e. if key in [mean, median, min, max, sd, se, cv] then the other keys should be updated; use default set of keys for automatic conversion :param key: column key in dataset (this column is unit converted) :param factor: multiplicative Quantity factor for conversion :return: None ## function `load_pkdb_dataframe(sid, data_path: 'Path | list[Path]', sep='\t', comment='#', **kwargs) -> 'pd.DataFrame'` Load TSV data from PKDB figure or table id. This is a simple helper functions to directly loading the TSV data. It is recommended to use `pkdb_analysis` methods instead. This function will be removed. E.g. for 'Amchin1999_Tab1' the file data_path / 'Amchin1999' / '.Amchin1999.tsv' is loaded. :param sid: figure or table id :param data_path: base path of data or iterable of data_paths :param sep: separator :param comment: comment characters :param kwargs: additional kwargs for csv parsing :return: pandas DataFrame ## function `load_pkdb_dataframes_by_substance(sid, data_path, **kwargs) -> 'dict[str, pd.DataFrame]'` Load dataframes from given PKDB figure/table id split on substance. The DataFrame is split on the 'substance' key. This is a simple helper functions to directly loading the TSV data. It is recommended to use `pkdb_analysis` methods instead. This function will be removed. :param sid: :param data_path: :param kwargs: :return: dict[substance, pd.DataFrame] --- # sbmlsim.units Manage units and units conversions. Used for model and data unit conversions. ## class `Units()` Units class. Container for unit related functionality. Allows to read the unit information from SBML models and provides helpers for the unit conversion. ## class `UnitsInformation(udict: 'UdictType', ureg: 'UnitRegistry', *args, **kwargs)` Storage of units information. Used for models or datasets. ### `UnitsInformation.from_sbml(sbml: 'str | Path', ureg: 'UnitRegistry | None' = None) -> 'UnitsInformation'` Get pint UnitsInformation for model. ### `UnitsInformation.from_sbml_doc(doc: 'libsbml.SBMLDocument', ureg: 'UnitRegistry | None' = None) -> 'UnitsInformation'` Get pint UnitsInformation for model in document. ### `UnitsInformation.model_uid_dict(model: 'libsbml.Model', ureg: 'UnitRegistry') -> 'dict[str, str]'` Populate the model uid dict for lookup. ### `UnitsInformation.normalize_changes(changes: 'dict[str, Quantity | float]', uinfo: 'UnitsInformation') -> 'dict[str, Quantity | float]'` Normalize all changes to units in given units dictionary. This is a major helper function allowing to convert changes to the requested units. --- # sbmlsim.serialization Helpers for JSON serialization of experiments. ## class `ObjectJSONEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None)` Class for encoding in JSON. ### `ObjectJSONEncoder.default(self, o)` JSON encoder. ### `ObjectJSONEncoder.to_json(self, path: pathlib.Path | None = None) -> str | pathlib.Path` Convert definition to JSON for exchange. :param path: path for file, if None JSON str is returned :return: ## function `from_json(json_info: str | pathlib.Path) -> dict[typing.Any, typing.Any]` Load data from JSON. ## function `to_json(object, path: pathlib.Path | None = None) -> str | pathlib.Path` Serialize to JSON. --- # sbmlsim.utils Utility functions. ## function `deprecated(function)` Get decorator for deprecation. This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted when the function is used. ## function `function_name() -> str` Get current function name. ## function `md5_for_path(path)` Calculate MD5 of file content. ## function `timeit(function)` Time function via timing decorator. --- # sbmlsim.console Shared rich console. The console is used for the output of scripts and examples; library code logs instead of printing, see `sbmlsim.log`. ```python from sbmlsim.console import console console.print(result) console.rule("Section", style="white") ``` Importing this module has no side effects on the interpreter. To get rich representations in an interactive session, install them explicitly with `rich.pretty.install()`. --- # sbmlsim.log Logging of the package. `sbmlsim` follows the convention for libraries: it only gets loggers and logs to them, it does not configure logging. Handlers, levels and formatting are left to the application, which keeps the messages of the package under the control of whoever uses it. Modules get their logger from the standard library with ```python import logging logger = logging.getLogger(__name__) ``` All loggers are therefore below the `sbmlsim` logger, so an application configures them in one place: ```python import logging logging.getLogger("sbmlsim").setLevel(logging.WARNING) ``` For scripts and interactive work the rich formatting of the package can be enabled explicitly, which is what the examples do: ```python from sbmlsim import log log.enable_rich_logging() ``` ## function `enable_rich_logging(level: int = 20, console: rich.console.Console | None = None) -> logging.Logger` Log the messages of the package on a rich console. This configures logging and is meant for scripts, examples and interactive work. Applications should configure logging themselves instead of calling this. Calling it repeatedly replaces the handler instead of adding a second one. Args: level: level from which messages are logged console: console to log on, the console of the package by default Returns: The `sbmlsim` logger. --- # sbmlsim.model.model Models. Functions for model loading, model manipulation and settings on the integrator. Model can be in different formats, main supported format being SBML. Other formats could be supported like CellML or NeuroML. ## class `AbstractModel(source: str | pathlib.Path, sid: str | None = None, name: str | None = None, language: str | None = None, language_type: sbmlsim.model.model.AbstractModel.LanguageType | None = None, base_path: pathlib.Path | None = None, changes: dict | None = None, selections: list[str] | None = None)` Abstract base class to store a model in sbmlsim. Depending on the model language different subclasses are implemented. ### `AbstractModel.LanguageType(*values)` Language types. ### `AbstractModel.SourceType(*values)` Source types. ### `AbstractModel.normalize(self, uinfo: sbmlsim.units.UnitsInformation)` Normalize values to model units for all changes. ### `AbstractModel.to_dict(self)` Convert to dictionary. --- # sbmlsim.model.model_roadrunner RoadRunner model. ## class `RoadrunnerSBMLModel(source: str | pathlib.Path, base_path: pathlib.Path | None = None, changes: dict | None = None, sid: str | None = None, name: str | None = None, selections: list[str] | None = None, ureg: pint.registry.UnitRegistry | None = None, settings: dict | None = None)` Roadrunner model wrapper. ### `RoadrunnerSBMLModel.from_abstract_model(abstract_model: sbmlsim.model.model.AbstractModel, selections: list[str] | None = None, ureg: pint.registry.UnitRegistry | None = None, settings: dict | None = None)` Create from AbstractModel. ### `RoadrunnerSBMLModel.get_state_path(sbml_path: pathlib.Path) -> pathlib.Path | None` Get path of the state file. The state file is a binary file which allows fast model loading. ### `RoadrunnerSBMLModel.parameter_df(r: roadrunner.roadrunner.RoadRunner) -> pandas.DataFrame` Create GlobalParameter DataFrame. :return: pandas DataFrame ### `RoadrunnerSBMLModel.parse_units(self, ureg: pint.registry.UnitRegistry) -> sbmlsim.units.UnitsInformation` Parse units from SBML model. ### `RoadrunnerSBMLModel.set_default_settings(r: roadrunner.roadrunner.RoadRunner, **kwargs)` Set default settings of integrator. ### `RoadrunnerSBMLModel.set_integrator_settings(r: roadrunner.roadrunner.RoadRunner, **kwargs) -> roadrunner.roadrunner.Integrator` Set integrator settings. Keys are: variable_step_size [boolean] stiff [boolean] absolute_tolerance [float] relative_tolerance [float] ### `RoadrunnerSBMLModel.species_df(r: roadrunner.roadrunner.RoadRunner) -> pandas.DataFrame` Create FloatingSpecies DataFrame. :return: pandas DataFrame --- # sbmlsim.model.model_change Model changes. Model changes are structural changes to the model structure. Changes of values and initial conditions are encoded via the changes instead. ## class `ModelChange()` ModelChange. Structural change to a model. ### `ModelChange.clamp_species(r: roadrunner.roadrunner.RoadRunner, species_id, formula=True, speed=10000.0)` Clamp/free species to certain value or formula. This is only an approximative clamp, i.e. not working instantenious. Depending on the model kinetics different speed settings are required. FIXME: `time` cannot be used in formula due to https://github.com/sys-bio/roadrunner/issues/601 FIXME: concentrations and amounts are not handled (uses native species setting i.e., amount or concentration definition. --- # sbmlsim.model.model_resources Model resources. Interacting with model resources to retrieve models. This currently includes BioModels, but can easily be extended to other models. ## class `Source(source: str, path: pathlib.Path | None = None, content: str | None = None) -> None` Class for keeping track of the resolved sources. ### `Source.is_content(self) -> bool` Check if the source is Content. ### `Source.is_path(self) -> bool` Check if the source is a Path. ### `Source.to_dict(self) -> dict[str, str | None]` Convert to dict. Used for serialization. ## function `is_http(source: str) -> bool` Check if http source. ## function `is_urn(source: str) -> bool` Check if urn source. ## function `model_from_biomodels(mid: str) -> str` Get SBML string from given BioModels identifier. :param mid: biomodels id :return: SBML string ## function `model_from_url(url: str) -> str` Get model string from given URL. Handles redirects of the download page. :param url: :return: ## function `model_from_urn(urn: str) -> str` Get model string from given URN. ## function `parse_biomodels_mid(text: str) -> str` Parse biomodel id from string. --- # sbmlsim.simulation.timecourse Definition of timecourses and timecourse simulations. ## class `Timecourse(start: float, end: float, steps: int, changes: dict[str, pint.facets.plain.quantity.PlainQuantity | float] | None = None, model_changes: dict[str, Any] | None = None, model_manipulations: dict[str, Any] | None = None, discard: bool = False)` Simulation definition. Definition of all information necessary to run a single timecourse simulation. A single simulation consists of multiple changes which are applied, all simulations are performed and collected. Changesets and selections are deep copied for persistence. ### `Timecourse.add_change(self, sid: str, value: pint.facets.plain.quantity.PlainQuantity | float) -> None` Add change. ### `Timecourse.add_model_change(self, sid: str, change: Any) -> None` Add model change. ### `Timecourse.add_model_changes(self, model_changes: dict[str, typing.Any]) -> None` Add model changes. ### `Timecourse.normalize(self, uinfo: sbmlsim.units.UnitsInformation) -> None` Normalize values to model units for all changes. ### `Timecourse.remove_change(self, sid: str) -> None` Remove change for given id. ### `Timecourse.remove_model_change(self, sid: str) -> None` Remove model change for id. ### `Timecourse.strip_units(self) -> None` Strip units from changes for parallel simulation. All changes must be normalized before stripping !. ### `Timecourse.to_dict(self) -> dict[str, typing.Any]` Convert to dictionary. ## class `TimecourseSim(timecourses: collections.abc.Sequence[sbmlsim.simulation.timecourse.Timecourse | dict[str, Any] | None] | sbmlsim.simulation.timecourse.Timecourse, selections: list[str] | None = None, reset: bool = True, time_offset: float = 0.0)` Timecourse simulation consisting of multiple concatenated timecourses. In case of a single timecourse, only the single timecourse is executed. ### `TimecourseSim.add_model_changes(self, model_changes: dict[str, typing.Any]) -> None` Add model changes to given simulation. ### `TimecourseSim.dimensions(self) -> list[sbmlsim.simulation.range.Dimension]` Get dimensions. ### `TimecourseSim.from_json(json_info: str | pathlib.Path) -> 'TimecourseSim'` Load from JSON. ### `TimecourseSim.normalize(self, uinfo: sbmlsim.units.UnitsInformation) -> None` Normalize timecourse simulation. ### `TimecourseSim.strip_units(self) -> None` Strip units from simulation. ### `TimecourseSim.to_dict(self) -> dict[str, typing.Any]` Convert to dictionary. ### `TimecourseSim.to_json(self, path: pathlib.Path | None = None) -> str | None` Convert definition to JSON. :param path: path for file, if None the JSON str is returned --- # sbmlsim.simulation.scan Scan simulation. Allows scans over other simulations. ## class `ScanSim(simulation: sbmlsim.simulation.simulation.AbstractSim, dimensions: list[sbmlsim.simulation.range.Dimension] | None = None, mapping: dict[str, int] | None = None)` A scan simulation over another AbstractSim. FIXME: probably not necessary to make this a simulation. ### `ScanSim.add_model_changes(self, model_changes: dict[str, typing.Any]) -> None` Add model changes to first timecourse. ### `ScanSim.get_dimension(self, key: str) -> sbmlsim.simulation.range.Dimension` Get dimension by key. ### `ScanSim.indices(self) -> list[tuple[typing.Any, ...]]` Get indices of all combinations. ### `ScanSim.normalize(self, uinfo: sbmlsim.units.UnitsInformation) -> None` Normalize units in scan. Requires normalization of timecourse simulation as well as all dimensions in the scan. ### `ScanSim.to_simulations(self) -> tuple[list[tuple[typing.Any, ...]], list[sbmlsim.simulation.timecourse.TimecourseSim]]` Flatten the scan to individual simulations. Here the changes are appended. Scan should be normalized before calling this function. Necessary to track the results. --- # sbmlsim.simulation.sensitivity Helpers for calculating model sensitivities and uncertainties. Allows to get sets of changes from given model instance. ## class `DistributionType(*values)` Type of supported distributions. # FIXME: support lognormal ## class `ModelSensitivity()` Helpers for calculating model sensitivity. ### `ModelSensitivity.apply_change_to_dict(ref_dict, change: float = 0.1)` Apply relative change to reference dictionary. :param ref_dict: {key: value} dictionary to change :param change: relative change to apply. :return: ### `ModelSensitivity.create_difference_dimension(model: sbmlsim.model.model_roadrunner.RoadrunnerSBMLModel, changes: dict | None = None, difference: float = 0.1, stype: sbmlsim.simulation.sensitivity.SensitivityType = , exclude_filter=None, exclude_zero: bool = True, zero_eps: float = 1e-08) -> sbmlsim.simulation.range.Dimension` Create list of dimensions for sampling parameter values. Only parameters relevant for "GU_", "LI_" and "KI_" models are sampled. cv: coeffient of variation (sigma/mean) -> sigma = cv*mean ### `ModelSensitivity.create_sampling_dimension(model: sbmlsim.model.model_roadrunner.RoadrunnerSBMLModel, changes: dict | None = None, cv: float = 0.1, size: int = 10, distribution: sbmlsim.simulation.sensitivity.DistributionType = , stype: sbmlsim.simulation.sensitivity.SensitivityType = , exclude_filter=None, exclude_zero: bool = True, zero_eps: float = 1e-08) -> sbmlsim.simulation.range.Dimension` Create list of dimensions for sampling parameter values. Only parameters relevant for "GU_", "LI_" and "KI_" models are sampled. cv: coeffient of variation (sigma/mean) -> sigma = cv*mean ### `ModelSensitivity.difference_sensitivity_scan(model: sbmlsim.model.model_roadrunner.RoadrunnerSBMLModel, simulation: sbmlsim.simulation.timecourse.TimecourseSim, difference: float = 0.1, stype: sbmlsim.simulation.sensitivity.SensitivityType = , exclude_filter=None, exclude_zero: bool = True, zero_eps: float = 1e-08) -> sbmlsim.simulation.scan.ScanSim` Create a parameter sensitivity scan for given TimecourseSimulation. :param model: model for execution (needed to select parameters) :param simulation: timecourse simulation to scan :param difference: change in parameters, i.e. every parameter (which is not excluded) is changed to '(1.0 - difference) * value' and '(1.0 + difference) * value' :param stype: which sensitivity (parameters or species) :param exclude_filter: filter function which defines which parameters should be excluded from scan :param exclude_zero: parameters with a value of abs(value), stype: sbmlsim.simulation.sensitivity.SensitivityType = , exclude_filter=None, exclude_zero: bool = True, zero_eps: float = 1e-08) -> sbmlsim.simulation.scan.ScanSim` Get sensitivity scan based on distributions for values. ### `ModelSensitivity.reference_dict(model: sbmlsim.model.model_roadrunner.RoadrunnerSBMLModel, changes: dict | None = None, stype: sbmlsim.simulation.sensitivity.SensitivityType = , exclude_filter=None, exclude_zero: bool = True, zero_eps: float = 1e-08) -> dict` Get key:value dict for sensitivity analysis. Values are based on the reference state of the model with the applied changes. Values in current model state are used. :param model: :param exclude_filter: filter function to exclude parameters, excludes parameter id if the filter function is True :param exclude_zero: exclude parameters which are zero :return: ## class `SensitivityType(*values)` Type of sensitivity. --- # sbmlsim.simulation.range Module handling ranges. ## class `DataRange(sid: str, source_ref: str, name: str | None = None)` DataRange class. The DataRange constructs a range by reference to external data. The sourceRef must point to a DataDescription with a single dimension, whose values are used as the values of the range. ## class `Dimension(dimension: str, index: numpy.ndarray | None = None, changes: dict[str, Any] | None = None)` Define dimension for a scan. The dimension defines how the dimension is called, the index is the corresponding index of the dimension. ### `Dimension.indices_from_dimensions(dimensions: list['Dimension']) -> list[tuple[typing.Any, ...]]` Get indices of all combinations of dimensions. ## class `FunctionalRange(sid: str, variables: list[sbmlsim.simulation.calculation.Variable], parameters: list[sbmlsim.simulation.calculation.Parameter], math: str, range: str, name: str | None = None)` FunctionalRange class. The FunctionalRange constructs a range through calculations that determine the next value based on the value(s) of other range(s) or model variables. In this it is similar to the ComputeChange element, and shares some of the same child elements (but is not a subclass of ComputeChange). ## class `Range(sid: str, name: str | None = None)` Range class. The Range class is the base class for the different types of ranges, i.e. UniformRange, VectorRange, FunctionalRange, and DataRange. ## class `UniformRange(sid: str, start: float, end: float, steps: int, range_type: sbmlsim.simulation.range.UniformRangeType = , name: str | None = None)` UniformRange class. The UniformRange on the preceding page) allows the definition of a Range with uniformly spaced values. The range_type determines whether to draw the values logarithmically (with a base of 10) or linearly. ## class `UniformRangeType(*values)` UniformRangeType. Attribute type that can take the values linear or log. Determines whether to draw the values logarithmically (with a base of 10) or linearly. ## class `VectorRange(sid: str, values: list | tuple | numpy.ndarray, name: str | None = None)` VectorRange class. The VectorRange describes an ordered collection of real values, listing them explicitly within child value elements. --- # sbmlsim.simulation.change Module handling changes. ## class `Change(target: sbmlsim.simulation.base.Target, sid: str | None = None, name: str | None = None)` Change class. A model might need to undergo pre-processing before simulation. Those pre-processing steps are specified in the listOfChanges via the Change class on Model. Changes can be of the following types: - Changes based on mathematical calculations (ComputeChange) - Changes on attributes of the model (ChangeAttribute) - For XML-encoded models, changes on any XML snippet of the model’s XML representation (AddXML, ChangeXML, RemoveXML) ## class `ComputeChange(sid: str, target: sbmlsim.simulation.base.Target, variables: list[sbmlsim.simulation.calculation.Variable], parameters: list[sbmlsim.simulation.calculation.Parameter], math: str, name: str | None = None)` ComputeChange class. The ComputeChange class permits to change the numerical value of any single element or attribute of a Model addressable by a target, based on a calculation. --- # sbmlsim.simulation.algorithm Handling of algorithms and algorithm parameters. ## class `Algorithm(kisao: str | pymetadata.ontologies.kisao.KISAO, parameters: list[sbmlsim.simulation.algorithm.AlgorithmParameter] | None = None, sid: str | None = None, name: str | None = None)` Algorithm class. ## class `AlgorithmParameter(kisao: str | pymetadata.ontologies.kisao.KISAO, value: str | float, sid: str | None = None, name: str | None = None)` AlgorithmParameter. The AlgorithmParameter class allows to parameterize a particular simulation algorithm. The set of possible parameters for a particular instance is determined by the algorithm that is referenced by the kisaoID of the enclosing algorithm element. --- # sbmlsim.simulation.kisaos Working with the KISAO ontology. ## function `algorithm_parameter_to_parameter_key(par)` Resolve the mapping between parameter keys and roadrunner integrator keys. ## function `integrator_from_kisao(kisao: str)` Get RoadRunner integrator name for algorithm KisaoID. :param kisao: KisaoID :type kisao: str :return: RoadRunner integrator name. :rtype: str ## function `is_supported_algorithm_for_simulation_type(kisao, sim_type)` Check Algorithm Kisao Id is supported for simulation. :return: is supported :rtype: bool --- # sbmlsim.simulation.calculation Module for performing all the Calculations. ## class `AppliedDimension(target: str | None = None, dimension_target: str | None = None, sid: str | None = None, name: str | None = None)` AppliedDimension class. A AppliedDimension object is used when the term of the Variable is a function that reduces the dimen- sionality of the data. Dimension reducing functions can be applied in two contexts: First to reduce data from RepeatedTasks and nested RepeatedTasks which requires the taskReference of the variable to be set and to be a reference to a RepeatedTask. All AppliedDimensions must have the target set and reference either one of the possibly nested RepeatedTask Sids or the Task within the RepeatedTask. Second to reduce data from a multi-dimensional DataSource in a DataGenerator which requires the target of the variable to be set to reference the respective DataSource. The AppliedDimensions must have the dimensionTarget set to a NuMLIdRef referencing a dimension of the data." "If the listOfAppliedDimensions contains 2 or more AppliedDimensions the reducing function is applied on an element-by-element basis." ## class `Calculation(sid: str, variables: list[sbmlsim.simulation.calculation.Variable], parameters: list[sbmlsim.simulation.calculation.Parameter], math: str, name: str | None = None)` Calculation class. Used by ComputeChange, DataGenerator and FunctionalRange. ### `Calculation.values(self) -> None` Access to values. ## class `ComputeChange(sid: str, variables: list[sbmlsim.simulation.calculation.Variable], parameters: list[sbmlsim.simulation.calculation.Parameter], math: str, name: str | None = None)` ComputeChange class. ### `ComputeChange.values(self) -> None` Access to values. ## class `DataGenerator(sid: str, variables: list[sbmlsim.simulation.calculation.Variable], parameters: list[sbmlsim.simulation.calculation.Parameter], math: str, name: str | None = None)` DataGenerator class. ### `DataGenerator.values(self) -> None` Access to values. ## class `DependentVariable(sid: str, model_reference: str | None, task_reference: str | None, target: sbmlsim.simulation.base.Target | None = None, symbol: sbmlsim.simulation.base.Symbol | None = None, target2: sbmlsim.simulation.base.Target | None = None, symbol2: sbmlsim.simulation.base.Symbol | None = None, unit: str | None = None, name: str | None = None, term: str | None = None, applied_dimensions: list[sbmlsim.simulation.calculation.AppliedDimension] | None = None)` DependentVariable class. A dependent variable is necessary when the desired variable is a composite of two other variables, such as ‘the rate of change of S1 with respect to time’. ## class `FunctionalRange(sid: str, variables: list[sbmlsim.simulation.calculation.Variable], parameters: list[sbmlsim.simulation.calculation.Parameter], math: str, name: str | None = None)` FunctionalRange class. ### `FunctionalRange.values(self) -> None` Access to values. ## class `Parameter(sid: str, value: float, unit: str | None = None, name: str | None = None)` Parameter class. The Parameter class (Figure 2.4) is used to create named pars with a constant value. A Parameter can be used wherever a mathematical expression to compute a value is defined, e.g., in ComputeChange, FunctionalRange or DataGenerator. The Parameter definitions are local to the particular class defining them. ## class `Variable(sid: str, model_reference: str | None, task_reference: str | None, target: sbmlsim.simulation.base.Target | None = None, symbol: sbmlsim.simulation.base.Symbol | None = None, unit: str | None = None, name: str | None = None, term: str | None = None, applied_dimensions: list[sbmlsim.simulation.calculation.AppliedDimension] | None = None)` Variable class. A Variable is a reference to an already existing entity, either explicitly created in the SED-ML Document, or to an implicitly defined symbol. --- # sbmlsim.simulation.base BaseObjects for SED-ML and simulation. ## class `BaseObject(sid: str | None, name: str | None)` Base class for SED-ML bases. FIXME: support annotations and notes ## class `BaseObjectSIdRequired(sid: str, name: str | None)` Base class for SED-ML bases with required sid. ## class `Symbol(symbol: str)` Symbol class. The symbol attribute of type string is used to refer either to a predefined, implicit variable or to a predefined implicit function to be performed on the target. In both cases, the symbol should be a kisaoID (and follow the format of that attribute) that represents that variable’s concept. The notion of implicit variables is explained in Section 3.2.5. For backwards compatibility, the old string “urn:sedml:symbol:time” is also allowed, though interpreters should interpret “KISAO:0000832” as meaning the same thing. ## class `Target(target: str)` Target class. An instance of Variable can refer to a model constituent inside a particular model through the address stored in the target attribute, such as an XPath expression. Note that while it is possible to write XPath expressions that select multiple nodes within a referenced model, when used within a target attribute, a single element or attribute must be selected by the expression. The target attribute may also be used in three situations to reference another SED-ML element with mathematical meaning, by containing a fragment identifier consisting of a hash character (#) followed by the SId of the element (i.e. “#id001”). --- # sbmlsim.simulation.simulation Abstract base simulation. ## class `AbstractSim()` AbstractSim. Base class of simulations. ### `AbstractSim.add_model_changes(self, model_changes: dict[str, typing.Any]) -> None` Add model changes to model. ### `AbstractSim.normalize(self, uinfo: sbmlsim.units.UnitsInformation) -> None` Normalize simulation. ### `AbstractSim.to_dict(self) -> dict[str, typing.Any]` Convert to dictionary. ## class `Analysis(sid: str, algorithm: sbmlsim.simulation.algorithm.Algorithm, name: str | None = None)` Analysis class. The Analysis represents any sort of analysis or simulation of a Model, entirely defined by its child Algorithm. ## class `OneStep(sid: str, step: float, algorithm: sbmlsim.simulation.algorithm.Algorithm, name: str | None = None)` OneStep class. The OneStep class calculates one further output step for the model from its current state. ## class `Simulation(sid: str, algorithm: sbmlsim.simulation.algorithm.Algorithm, name: str | None = None)` Simulation class. A simulation is the execution of some defined algorithm(s). Simulations are described differently depending on the type of simulation experiment to be performed. Simulation is an abstract class and serves as parent class for the different types of simulations. ## class `SteadyState(sid: str, algorithm: sbmlsim.simulation.algorithm.Algorithm, name: str | None = None)` SteadyState class. The SteadyState represents a steady state computation (as for example implemented by NLEQ or Kinsolve). ## class `UniformTimeCourse(sid: str, algorithm: sbmlsim.simulation.algorithm.Algorithm, start: float, end: float, steps: int, initial_time: float, name: str | None = None)` UniformTimeCourse class. The UniformTimeCourse class calculates a time course output with equidistant time points. --- # sbmlsim.simulator.simulation_serial Serial simulator. ## class `SimulatorSerial(model: str | pathlib.Path | sbmlsim.model.model_roadrunner.RoadrunnerSBMLModel | sbmlsim.model.model.AbstractModel | None = None, **kwargs)` Serial simulator using a single core. A single simulator can run many different models. See the parallel simulator to run simulations on multiple cores. ### `SimulatorSerial.run_scan(self, scan: sbmlsim.simulation.scan.ScanSim) -> sbmlsim.result.xresult.XResult` Run a scan simulation. ### `SimulatorSerial.run_timecourse(self, simulation: sbmlsim.simulation.timecourse.TimecourseSim) -> sbmlsim.result.xresult.XResult` Run single timecourse. ### `SimulatorSerial.set_integrator_settings(self, **kwargs)` Set settings in the integrator. ### `SimulatorSerial.set_model(self, model: str | pathlib.Path | sbmlsim.model.model_roadrunner.RoadrunnerSBMLModel | sbmlsim.model.model.AbstractModel | None) -> None` Set model for simulator and updates the integrator settings. ### `SimulatorSerial.set_timecourse_selections(self, selections)` Set timecourse selection in model. --- # sbmlsim.task.task Tasks. ## class `Task(model: str, simulation: str, sid: str | None = None, name: str | None = None)` Tasks combine models with simulations. This allows to execute the same simulation with different model variants. ### `Task.to_dict(self) -> dict[str, str]` Convert to dictionary. Returns: Dictionary with model and simulation keys. --- # sbmlsim.experiment.experiment SimulationExperiments and helpers. ## class `ExperimentResult(experiment: sbmlsim.experiment.experiment.SimulationExperiment, output_path: pathlib.Path | None) -> None` Result of a simulation experiment. ### `ExperimentResult.to_dict(self) -> dict` Conversion to dictionary. Used in serialization and required for reports. ## class `SimulationExperiment(sid: str | None = None, base_path: pathlib.Path | None = None, data_path: pathlib.Path | collections.abc.Iterable[pathlib.Path] | None = None, ureg: pint.registry.UnitRegistry | None = None, **kwargs)` Generic simulation experiment. Consists of models, datasets, simulations, tasks, results, processing, figures ### `SimulationExperiment.add_data(self, d: sbmlsim.data.Data) -> None` Add data to the tracked data. ### `SimulationExperiment.add_selections_data(self, selections: collections.abc.Iterable[str], task_ids: collections.abc.Iterable[str] | None = None) -> None` Add selections to given tasks. The data for the selections will be part of the results. Selections are necessary to access data from simulations. Here these selections are added to the tasks. If no tasks are given, the selections are added to all tasks. :param reset: drop and reset all selections. ### `SimulationExperiment.create_mpl_figures(self) -> dict[str, matplotlib.figure.Figure | sbmlsim.plot.plotting.Figure]` Create matplotlib figures. ### `SimulationExperiment.data(self) -> dict[str, sbmlsim.data.Data]` Define DataGenerators including functions. This determines the selection in the model. All data which is accessed in a simulation result must be defined in a data generator. The data generators are important for defining the selections of a simulation experiment. ### `SimulationExperiment.datasets(self) -> dict[str, sbmlsim.data.DataSet]` Define dataset definitions (experimental data). The child classes fill out the information. ### `SimulationExperiment.evaluate_fit_mappings(self)` Evaluate fit mappings. ### `SimulationExperiment.figures(self) -> dict[str, sbmlsim.plot.plotting.Figure]` Figure definition. Selections accessed in figures and analyses must be registered beforehand via datagenerators. Most figures do not require access to concrete data, but only abstract data concepts. ### `SimulationExperiment.figures_mpl(self) -> dict[str, matplotlib.figure.Figure]` Matplotlib figure definition. Selections accessed in figures and analyses must be registered beforehand via datagenerators. Most figures do not require access to concrete data, but only abstract data concepts. ### `SimulationExperiment.fit_mappings(self) -> dict[str, sbmlsim.fit.objects.FitMapping]` Define fit mappings. Mapping reference data on observables. Used for the optimization of parameters. The child classes fill out the information. ### `SimulationExperiment.initialize(self) -> None` Initialize SimulationExperiment. Initialization must be separated from object construction due to the parallel execution of the problem later on. Certain objects cannot be serialized and must be initialized. :return: ### `SimulationExperiment.models(self) -> dict[str, sbmlsim.model.model.AbstractModel | pathlib.Path]` Define model definitions. The child classes fill out the information. ### `SimulationExperiment.reports(self) -> dict[str, dict[str, str]]` Define reports. Reports are defined by a hashmap label:Data. Reports can be serialized in multiple manners. ### `SimulationExperiment.run(self, simulator, output_path: pathlib.Path | None = None, show_figures: bool = True, save_results: bool = False, figure_formats: list[str] | None = None, reduced_selections: bool = True) -> 'ExperimentResult'` Execute given experiment and store results. ### `SimulationExperiment.save_datasets(self, results_path: pathlib.Path) -> None` Save datasets. ### `SimulationExperiment.save_mpl_figures(self, results_path: pathlib.Path, mpl_figures: dict[str, matplotlib.figure.Figure], figure_formats: list[str] | None = None) -> dict[str, list[pathlib.Path]]` Save matplotlib figures. ### `SimulationExperiment.save_results(self, results_path: pathlib.Path) -> None` Save results (mean timecourse). :param results_path: :return: ### `SimulationExperiment.show_mpl_figures(self, mpl_figures: dict[str, matplotlib.figure.Figure]) -> None` Show matplotlib figures. ### `SimulationExperiment.simulations(self) -> dict[str, sbmlsim.simulation.simulation.AbstractSim]` Define simulation definitions. The child classes fill out the information. ### `SimulationExperiment.tasks(self) -> dict[str, sbmlsim.task.task.Task]` Define task definitions. The child classes fill out the information. ### `SimulationExperiment.to_dict(self)` Convert to dictionary. This is the basis for the JSON serialization. ### `SimulationExperiment.to_json(self, path: pathlib.Path | None = None, indent: int = 2)` Convert experiment to JSON for exchange. :param path: path for file, if None JSON str is returned :return: --- # sbmlsim.experiment.runner Runner for SimulationExperiments. The ExperimentRunner is used to execute simulation experiments. This includes - loading of datasets - loading of models - running tasks (simulation on models) - creating outputs ## class `ExperimentRunner(experiment_classes: type[sbmlsim.experiment.experiment.SimulationExperiment] | collections.abc.Iterable[type[sbmlsim.experiment.experiment.SimulationExperiment]], base_path: pathlib.Path | None, data_path: pathlib.Path | None, simulator: sbmlsim.simulator.simulation_serial.SimulatorSerial | None = None, ureg: pint.registry.UnitRegistry | None = None, **kwargs)` Class for running simulation experiments. ### `ExperimentRunner.initialize(self, experiment_classes: list[type[sbmlsim.experiment.experiment.SimulationExperiment]] | tuple[type[sbmlsim.experiment.experiment.SimulationExperiment]] | set[type[sbmlsim.experiment.experiment.SimulationExperiment]], **kwargs)` Initialize ExperimentRunner. Initialization is required in addition to construction to allow serialization of information for parallelization. ### `ExperimentRunner.run_experiments(self, output_path: pathlib.Path, show_figures: bool = False, save_results: bool = False, figure_formats: list[str] | None = None, reduced_selections: bool = True) -> list[sbmlsim.experiment.experiment.ExperimentResult]` Run the experiments. ### `ExperimentRunner.set_simulator(self, simulator: sbmlsim.simulator.simulation_serial.SimulatorSerial | None) -> None` Set simulator on the runner and experiments. ## function `run_experiments(experiments: type[sbmlsim.experiment.experiment.SimulationExperiment] | list[type[sbmlsim.experiment.experiment.SimulationExperiment]], output_path: pathlib.Path, base_path: pathlib.Path | None = None, data_path: pathlib.Path | None = None) -> None` Run simulation experiments and write their report to the output path. --- # sbmlsim.result.xresult Module for encoding simulation results and processed data. ## class `XResult(xdataset: xarray.core.dataset.Dataset, uinfo: sbmlsim.units.UnitsInformation | None = None)` Result of simulations. A wrapper around xr.Dataset which adds unit support via dictionary lookups. ### `XResult.dim_max(self, key: str) -> pint.facets.plain.quantity.PlainQuantity` Get maximum over all added dimensions. Args: key: Variable key. Returns: Maximum values with units. ### `XResult.dim_mean(self, key: str) -> pint.facets.plain.quantity.PlainQuantity` Get mean over all added dimensions. Args: key: Variable key. Returns: Mean values with units. Raises: KeyError: If the key does not exist in the result. ### `XResult.dim_min(self, key: str) -> pint.facets.plain.quantity.PlainQuantity` Get minimum over all added dimensions. Args: key: Variable key. Returns: Minimum values with units. ### `XResult.dim_std(self, key: str) -> pint.facets.plain.quantity.PlainQuantity` Get standard deviation over all added dimensions. Args: key: Variable key. Returns: Standard deviation values with units. ### `XResult.from_netcdf(path: str | pathlib.Path) -> 'XResult'` Read from netCDF. Args: path: Path to the netCDF file. Returns: XResult without units information. ### `XResult.is_timecourse(self) -> bool` Check if timecourse. Returns: True if the result is a single timecourse. ### `XResult.to_dataframe(self) -> pandas.DataFrame` Convert to DataFrame. Returns: DataFrame with flattened data. ### `XResult.to_mean_dataframe(self) -> pandas.DataFrame` Convert to DataFrame with mean data. Returns: DataFrame with the mean over all dimensions. ### `XResult.to_netcdf(self, path_nc: str | pathlib.Path) -> None` Store results as netcdf. Args: path_nc: Path to the netCDF file. ### `XResult.to_tsv(self, path_tsv: str | pathlib.Path) -> None` Write data to tsv. Args: path_tsv: Path to the TSV file. --- # sbmlsim.result.datagenerator DataGenerator. ## class `DataGenerator(f: sbmlsim.result.datagenerator.DataGeneratorFunction, xresults: dict[str, sbmlsim.result.xresult.XResult], dsets: dict[str, sbmlsim.data.DataSet] | None = None)` DataGenerator. DataGenerators allow to postprocess existing data. This can be a variety of operations. - Slicing: reduce the dimension of a given XResult, by slicing a subset on a given dimension - Cumulative processing: mean, sd, ... - Complex processing, such as pharmacokinetics calculation. ### `DataGenerator.process(self) -> dict[str, sbmlsim.result.xresult.XResult]` Process the data generator. Returns: Processed results. ## class `DataGeneratorFunction()` DataGeneratorFunction. ## class `DataGeneratorIndexingFunction(index: int, dimension: str = '_time')` DataGeneratorIndexingFunction. --- # sbmlsim.result.report Reports. ## class `Report(sid: str, name: str | None = None, datasets: dict[str, str] | None = None)` Reports of simulation experiments. Collections of data generators. ### `Report.add_dataset(self, label: str, data_id: str) -> None` Add dataset for given label. Args: label: Label of the dataset in the report. data_id: Identifier of the data. --- # sbmlsim.plot.plotting Classes for storing plotting information. The general workflow of generating plotting information is the following. 1. Within simulation experiments abstract plotting information is stored. i.e., how from the data plots can be generated. Working with multidimensional data ! Additional settings are required which allow to define how things are plotted. E.g. over which dimensions should an error be calculated and which dimensions should be plotted individually. ## class `AbstractCurve(sid: 'str | None', name: 'str | None', x: 'Data | None' = None, order: 'int | None' = None, style: 'Style | None' = None, yaxis_position: 'YAxisPosition | None' = None)` Base class of Curves and ShadedAreas. ## class `Axis(label: 'str | None' = None, unit: 'str | None' = None, name: 'str | None' = None, scale: 'AxisScale | str' = , min: 'float | None' = None, max: 'float | None' = None, reverse: 'bool' = False, grid: 'bool' = False, label_visible: 'bool' = True, ticks_visible: 'bool' = True, style: 'Style | None' = None)` Axis object. ### `Axis.to_dict(self) -> 'dict[str, Any]'` Convert to dictionary. Returns: Dictionary of the axis attributes. ## class `AxisScale(*values)` Scale of the axis. ## class `BasePlotObject(sid: 'str | None', name: 'str | None')` Base class for plotting objects. ## class `ColorType(color: 'str')` ColorType class. Encoding color information used in plots. ### `ColorType.parse_color(color: 'str | None', alpha: 'float' = 1.0) -> 'ColorType | None'` Parse given color and add alpha information. Args: color: Color as matplotlib color string or hex color. alpha: Alpha value in [0, 1]. Returns: ColorType or None if no color is given. Raises: ValueError: If the hex color has an incorrect format. ### `ColorType.to_dict(self) -> 'str'` Convert for serialization. Returns: Color string. ## class `Curve(x: 'Data', y: 'Data', sid: 'str | None' = None, name: 'str | None' = None, xerr: 'Data | None' = None, yerr: 'Data | None' = None, order: 'int | None' = None, type: 'CurveType' = , style: 'Style | None' = None, yaxis_position: 'YAxisPosition | None' = None, **kwargs: 'Any')` Curve object. ### `Curve.to_dict(self) -> 'dict[str, Any]'` Convert Curve to dictionary. Returns: Dictionary of the curve attributes. ## class `CurveType(*values)` CurveType options. ## class `Figure(experiment: 'SimulationExperiment | None', sid: 'str', name: 'str | None' = None, subplots: 'list[SubPlot] | None' = None, height: 'float | None' = None, width: 'float | None' = None, num_rows: 'int' = 1, num_cols: 'int' = 1)` A figure consists of multiple subplots. A reference to the experiment is required, so the plot can resolve the datasets and the simulations. ### `Figure.add_plots(self, plots: 'list[Plot]', copy_plots: 'bool' = False) -> 'None'` Add plots to figure. For every plot a subplot is generated. Args: plots: Plots to add. copy_plots: Flag to copy the plots before adding. Raises: ValueError: If more plots than panels are provided. ### `Figure.add_subplot(self, plot: 'Plot', row: 'int', col: 'int', row_span: 'int' = 1, col_span: 'int' = 1) -> 'Plot'` Add plot as subplot to figure. Be careful that individual subplots do not overlap when adding multiple subplots. Args: plot: Plot to add as subplot. row: row position for plot in [1, num_rows] col: col position for plot in [1, num_cols] row_span: span of figure with row + row_span <= num_rows col_span: span of figure with col + col_span <= num_cols Returns: The added plot. Raises: ValueError: If the position is outside of the figure. ### `Figure.create_plots(self, xaxis: 'Axis | None' = None, yaxis: 'Axis | None' = None, legend: 'bool' = True) -> 'list[Plot]'` Create plots in the figure. Settings are applied to all generated plots. E.g. if an xaxis is provided all plots have a copy of this xaxis. Args: xaxis: xaxis copied to all plots yaxis: yaxis copied to all plots legend: flag to show legends Returns: Created plots. ### `Figure.from_plots(sid: 'str', plots: 'list[Plot]', experiment: 'SimulationExperiment') -> 'Figure'` Create figure object from list of plots. Args: sid: identifier of the figure plots: plots stacked in a single column experiment: simulation experiment of the figure Returns: Figure with the plots. ### `Figure.get_plots(self) -> 'list[Plot]'` Get plots in this figure. Returns: Plots of all subplots. ### `Figure.num_panels(self) -> 'int'` Get number of panel spots for plots. Plots can span multiple of these panels. Returns: Number of panels. ### `Figure.num_subplots(self) -> 'int'` Get number of subplots. Returns: Number of subplots. ### `Figure.set_title(self, title: 'str | None') -> 'None'` Set title. Args: title: Title of the figure. ### `Figure.to_dict(self) -> 'dict[str, Any]'` Convert to dictionary. Returns: Dictionary of the figure attributes. ## class `Fill(color: 'ColorType | None' = None, second_color: 'ColorType | None' = None) -> None` Style of a fill. ### `Fill.to_dict(self) -> 'dict[str, Any]'` Convert to dictionary for serialization. Returns: Dictionary of the fill attributes. ## class `Line(type: 'LineType' = , color: 'ColorType | None' = None, thickness: 'float | None' = 2.0) -> None` Style of a line. ### `Line.to_dict(self) -> 'dict[str, Any]'` Convert to dictionary for serialization. Returns: Dictionary of the line attributes. ## class `LineType(*values)` LineType options. ## class `Marker(size: 'float | None' = 6.0, type: 'MarkerType' = , fill: 'ColorType | None' = None, line_color: 'ColorType | None' = None, line_thickness: 'float | None' = 1.0) -> None` Style of a marker. ### `Marker.to_dict(self) -> 'dict[str, Any]'` Convert to dictionary for serialization. Returns: Dictionary of the marker attributes. ## class `MarkerType(*values)` MarkerType options. ## class `Plot(sid: 'str', name: 'str | None' = None, xaxis: 'Axis | None' = None, yaxis: 'Axis | None' = None, yaxis_right: 'Axis | None' = None, curves: 'list[Curve] | None' = None, areas: 'list[ShadedArea] | None' = None, legend: 'bool' = True, facecolor: 'ColorType | None' = None, title_visible: 'bool' = True, height: 'float | None' = None, width: 'float | None' = None)` Plot panel. A plot is the basic element of a plot. This corresponds to a single panel or axes combination in a plot. Multiple plots create a figure. ### `Plot.add_area(self, area: 'ShadedArea') -> 'None'` Add ShadedArea via the helper function. All additions must go via this function to ensure data registration. Args: area: ShadedArea to add. ### `Plot.add_curve(self, curve: 'Curve') -> 'None'` Add Curve via the helper function. All additions must go via this function to ensure data registration. Args: curve: Curve to add. ### `Plot.add_data(self, xid: 'str', yid: 'str', xid_sd: 'str | None' = None, xid_se: 'str | None' = None, yid_sd: 'str | None' = None, yid_se: 'str | None' = None, count: 'int | str | None' = None, dataset: 'str | None' = None, task: 'str | None' = None, label: 'str | None' = '__yid__', type: 'CurveType' = , style: 'Style | None' = None, yaxis_position: 'YAxisPosition | None' = None, **kwargs: 'Any') -> 'None'` Add a data curve to the plot. Styling of curve is based on the provided style and matplotlib kwargs. Args: xid: index of x data yid: index of y data xid_sd: index of x SD data xid_se: index of x SE data yid_sd: index of y SD data yid_se: index of y SE data count: count for curve (number of subjects) dataset: dataset id task: task id label: label for curve (label=None for no label) type: type of curve (default points) style: style for curve yaxis_position: position of yaxis for this curve **kwargs: matplotlib styling kwargs Raises: ValueError: If the combination of arguments is not supported. ### `Plot.curve(self, x: 'Data', y: 'Data', xerr: 'Data | None' = None, yerr: 'Data | None' = None, type: 'CurveType' = , style: 'Style | None' = None, yaxis_position: 'YAxisPosition | None' = None, **kwargs: 'Any') -> 'None'` Create curve and add to plot. Args: x: x data y: y data xerr: x error data yerr: y error data type: type of curve (default points) style: style for curve yaxis_position: position of yaxis for this curve **kwargs: matplotlib styling kwargs ### `Plot.set_title(self, title: 'str') -> 'None'` Set title. Args: title: Title of the plot. ### `Plot.set_xaxis(self, label: 'str | Axis | None', unit: 'str | None' = None, **kwargs: 'Any') -> 'None'` Set axis with all axes attributes. All argument of Axis are supported. Args: label: label of Axis or Axis object unit: unit of the Axis (added to label) **kwargs: additional Axis arguments ### `Plot.set_yaxis(self, label: 'str | Axis | None', unit: 'str | None' = None, **kwargs: 'Any') -> 'None'` Set axis with all axes attributes. All argument of Axis are supported. Args: label: label of Axis or Axis object unit: unit of the Axis (added to label) **kwargs: additional Axis arguments, e.g. `label_visible` ### `Plot.set_yaxis_right(self, label: 'str | Axis | None', unit: 'str | None' = None, **kwargs: 'Any') -> 'None'` Set axis with all axes attributes. All argument of Axis are supported. Args: label: label of Axis or Axis object unit: unit of the Axis (added to label) **kwargs: additional Axis arguments, e.g. `label_visible` ### `Plot.to_dict(self) -> 'dict[str, Any]'` Convert to dictionary. Returns: Dictionary of the plot attributes. ## class `ShadedArea(x: 'Data', yfrom: 'Data', yto: 'Data', order: 'int | None' = None, style: 'Style | None' = None, yaxis_position: 'YAxisPosition | None' = None, **kwargs: 'Any')` ShadedArea class. ### `ShadedArea.to_dict(self) -> 'dict[str, Any]'` Convert to dictionary. Returns: Dictionary of the area attributes. ## class `Style(sid: 'str | None' = None, name: 'str | None' = None, base_style: 'Style | None' = None, line: 'Line | None' = None, marker: 'Marker | None' = None, fill: 'Fill | None' = None)` Style class. Storing styling informatin about line, marker and fill. Styles can be derived from other styles based on the the base_style attribute. ### `Style.from_mpl_kwargs(**kwargs: 'Any') -> 'Style'` Create style from matplotlib arguments. Args: **kwargs: Matplotlib styling arguments, e.g. `color`, `alpha`, `linestyle`, `linewidth`, `marker`, `markersize`, `markerfacecolor`, `markeredgecolor`, `markeredgewidth`. Returns: Style corresponding to the matplotlib arguments. ### `Style.resolve_style(self) -> 'Style'` Resolve all basestyle information. Resolves the actual style information. Returns: Style with all information of the base styles applied. ### `Style.to_mpl_area_kwargs(self) -> 'dict[str, Any]'` Define keyword dictionary for a shaded area. Returns: Keyword arguments for matplotlib fill_between. ### `Style.to_mpl_bar_kwargs(self) -> 'dict[str, Any]'` Convert to matplotlib bar curve keyword arguments. Returns: Keyword arguments for matplotlib bar plots. ### `Style.to_mpl_curve_kwargs(self) -> 'dict[str, Any]'` Convert to matplotlib curve keyword arguments. Returns: Keyword arguments for matplotlib curves. ### `Style.to_mpl_points_kwargs(self) -> 'dict[str, Any]'` Convert to matplotlib point curve keyword arguments. Returns: Keyword arguments for matplotlib errorbar plots. ## class `SubPlot(plot: 'Plot', row: 'int | None' = None, col: 'int | None' = None, row_span: 'int' = 1, col_span: 'int' = 1, sid: 'str | None' = None, name: 'str | None' = None)` A SubPlot holds a plot in a Figure. The SubPlot defines the layout used by the plot, i.e., the position and number of panels the plot is spanning. ## class `YAxisPosition(*values)` Position of yaxis. --- # sbmlsim.plot.serialization_matplotlib Serialization of Figure object to matplotlib. ## class `MatplotlibFigureSerializer()` Serializer for figures to matplotlib. ## function `interp(x, xp, fp)` Interpolation for speedup of plots. :param x: :param xp: :param fp: :return: --- # sbmlsim.report.experiment_report Create report of simulation experiments. ## class `ExperimentReport(results: sbmlsim.report.experiment_report.ReportResults | list[sbmlsim.experiment.experiment.ExperimentResult], metadata: dict[str, Any] | None = None, template_path: pathlib.Path = PosixPath('/home/runner/work/sbmlsim/sbmlsim/src/sbmlsim/resources/templates'))` Report for an experiment. ### `ExperimentReport.ReportType(*values)` Type of report. ### `ExperimentReport.create_report(self, output_path: pathlib.Path, filename: str | None = None, report_type: sbmlsim.report.experiment_report.ExperimentReport.ReportType = , f_filter_context: collections.abc.Callable[[dict[str, Any]], None] | None = None, **kwargs: Any) -> pathlib.Path` Create report of SimulationExperiments. Processes ExperimentResults to generate overall report. All relative paths only can be resolved in the report if the paths are below the report or at the same level in the file hierarchy. Args: output_path: Directory for the report. filename: Name of the index file (without suffix). report_type: Type of the report. f_filter_context: Function filtering the context (latex reports). **kwargs: Additional arguments, e.g. `latex_path_prefix`. Returns: Path to the created index file. Raises: ValueError: If the report type is not supported. ## class `ReportResults()` Results for a ExperimentReport. ### `ReportResults.add_experiment_result(self, exp_result: sbmlsim.experiment.experiment.ExperimentResult) -> None` Retrieve information for report from the ExperimentResult. Args: exp_result: Result of the simulation experiment. Raises: ValueError: If a model has no resolvable path. ### `ReportResults.from_json(json_path: pathlib.Path) -> 'ReportResults'` Read from JSON. Args: json_path: Path to the JSON file. Returns: ReportResults read from the file. ### `ReportResults.to_json(self, json_path: pathlib.Path) -> None` Write to JSON. Args: json_path: Path to the JSON file. --- # sbmlsim.fit.objects Definition of Objects used in FitProblems and optimization. ## class `FitData(experiment: 'Any', xid: 'str', yid: 'str', xid_sd: 'str | None' = None, xid_se: 'str | None' = None, yid_sd: 'str | None' = None, yid_se: 'str | None' = None, count: 'int | str | None' = None, dataset: 'str | None' = None, task: 'str | None' = None, function: 'str | None' = None)` Data used in a fit. This is either data from a dataset, a simulation results from a task or functional data, i.e. calculated from other data. ### `FitData.get_data(self) -> 'FitDataInitialized'` Return actual data. Numerical values are resolved using the executed simulation experiment. ### `FitData.is_dataset(self) -> 'bool'` Check if FitData comes from a dataset. ### `FitData.is_function(self) -> 'bool'` Check if FitData comes from a function. ### `FitData.is_task(self) -> 'bool'` Check if FitData comes from a task (simulation). ## class `FitDataInitialized()` Initialized FitData with actual data content. Data is create from simulation experiment. ## class `FitExperiment(experiment: 'type[SimulationExperiment]', mappings: 'list[str] | None' = None, weights: 'float | list[float] | None' = None, use_mapping_weights: 'bool' = False, fit_parameters: 'dict[str, list[FitParameter]] | None' = None, exclude: 'bool' = False)` A parameter fitting experiment. A parameter fitting experiment consists of multiple mapping (reference data to observable). The individual mappings can be weighted differently in the fitting. ### `FitExperiment.reduce(fit_experiments: 'Iterable[FitExperiment]') -> 'list[FitExperiment]'` Collect fit mappings of multiple FitExperiments if these can be combined. ## class `FitMapping(experiment: 'Any', reference: 'FitData', observable: 'FitData', weight: 'float | None' = None, metadata: 'MappingMetaData | None' = None)` Mapping of reference data to observable data. In the optimization the difference between the reference data (ground truth) and the observable (predicted data) is minimized. The weight allows to weight the FitMapping. ## class `FitParameter(pid: 'str', start_value: 'float | None' = None, lower_bound: 'float' = -inf, upper_bound: 'float' = inf, unit: 'str | None' = None)` Parameter adjusted in a parameter optimization. The bounds define the box in which the parameter can be varied. The start value is the initial value in the parameter fitting for algorithms which use it. ### `FitParameter.from_json(json_info: 'str | Path') -> 'FitParameter'` Load from JSON. ### `FitParameter.parameters_to_df(parameters: 'Iterable[FitParameter]') -> 'pd.DataFrame'` DataFrame of parameters. ### `FitParameter.to_dict(self) -> 'dict[str, Any]'` Convert to dictionary for serialization. ### `FitParameter.to_json(self, path: 'Path | None' = None) -> 'str | Path'` Serialize to JSON. Serializes to file if path is provided, otherwise returns JSON string. ## class `MappingMetaData(outlier: 'bool' = False) -> None` Metadata for mapping. Applications derive their metadata from this class, e.g., the tissue, the dosing or the group of a study; `outlier` marks a mapping which is excluded from the fit. ### `MappingMetaData.to_dict(self) -> 'dict[str, Any]'` Convert to dictionary for serialization. --- # sbmlsim.fit.optimization Optimization of parameter fitting problem. ## class `OptimizationProblem(opid: str, fit_experiments: list[sbmlsim.fit.objects.FitExperiment], fit_parameters: list[sbmlsim.fit.objects.FitParameter], base_path: pathlib.Path | None = None, data_path: pathlib.Path | None = None)` Parameter optimization problem. ### `OptimizationProblem.cost_least_square(self, xlog: numpy.ndarray) -> float` Get least square costs for parameters. ### `OptimizationProblem.initialize(self, residual: sbmlsim.fit.options.ResidualType | None, loss_function: sbmlsim.fit.options.LossFunctionType, weighting_curves: list[sbmlsim.fit.options.WeightingCurvesType], weighting_points: sbmlsim.fit.options.WeightingPointsType | None, variable_step_size: bool = True, relative_tolerance: float = 1e-06, absolute_tolerance: float = 1e-06) -> None` Initialize Optimization problem. Performs precalculations, resolving data, calculating weights. Creates and attaches simulator for the given problem. :param residual: handling of residuals :param loss_function: loss function for residual transformation :param weighting_curves: list of options for weighting curves (fit mappings) :param weighting_points: weighting of points :param absolute_tolerance: absolute tolerance of simulator :param relative_tolerance: relative tolerance of simulator :param variable_step_size: use variable step size in solver ### `OptimizationProblem.optimize(self, size: int = 5, algorithm: sbmlsim.fit.options.OptimizationAlgorithmType = , sampling: sbmlsim.fit.sampling.SamplingType = , seed: int | None = None, **kwargs) -> tuple[list[scipy.optimize._optimize.OptimizeResult], list]` Run parameter optimization. To change the weighting or handling of residuals reinitialize the optimization algorithm. ### `OptimizationProblem.report(self, path: pathlib.Path | None = None, print_output: bool = True) -> str` Print and write report. Can only be called after initialization. ### `OptimizationProblem.residuals(self, xlog: numpy.ndarray, complete_data=False)` Calculate residuals for given parameter vector. Optimization is performed in logarithmic parameter space to account for xtol in largely varying parameters. see https://github.com/scipy/scipy/issues/7632 :param xlog: logarithmic parameter vector :param complete_data: boolean flag to return additional information :return: vector of weighted residuals ### `OptimizationProblem.set_simulator(self, simulator: sbmlsim.simulator.simulation_serial.SimulatorSerial | None) -> None` Set the simulator on the runner and the experiments. ### `OptimizationProblem.to_dict(self) -> dict[str, typing.Any]` Convert to dictionary. ### `OptimizationProblem.to_json(self, path: pathlib.Path | None = None) -> str | pathlib.Path` Store OptimizationResult as json. Uses the to_dict method. ## class `RuntimeErrorOptimizeResult(status: str = '-1', success: bool = False, duration: float = -1.0, cost: float = inf, optimality: float = inf, x: numpy.ndarray | None = None, x0: numpy.ndarray | None = None) -> None` Result of an optimization which failed with a RuntimeError. Carries the same attributes as the `scipy.optimize.OptimizeResult` of a successful optimization, so that the results can be processed together. --- # sbmlsim.fit.options Main options for the parameter fitting. In the optimization the cost is minimized for Nk curves with every curve having Nki data points. sum(Nk)( w{k}^2 * sum(NKi) (w{i,k}^2 * res{i,k})) ## class `LossFunctionType(*values)` Determines the loss function. minimize F(x) = 0.5 * sum(rho(residuals_weighted(x)**2) The following loss functions are supported are allowed: ‘linear’ (default) : rho(z) = z. Gives a standard least-squares problem. ‘soft_l1’ : rho(z) = 2 * ((1 + z)**0.5 - 1). The smooth approximation of l1 (absolute value) loss. Usually a good choice for robust least squares. ‘cauchy’ : rho(z) = ln(1 + z). Severely weakens outliers influence, but may cause difficulties in optimization process. ‘arctan’ : rho(z) = arctan(z). Limits a maximum loss on a single residual, has properties similar to ‘cauchy’. ## class `OptimizationAlgorithmType(*values)` Type of optimization. `least square` : Least square is a local optimization method and works well in combination with many start values, i.e., many repeats of the optimization problem. See https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.least_squares.html for more information. `differential evolution` : Differential evolution is a global optimization method and normally is run with a limited number of repeats. See https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.differential_evolution.html#scipy.optimize.differential_evolution for more information. ## class `ResidualType(*values)` Handling of the residuals. How are the residuals calculated? Are the absolute residuals used, or are the residuals normalized based on the data points, i.e., relative residuals. `absolute` (default) : uses the absolute data values for calculation of the residuals:: r(i,k) = y(i,k) - f(xi,k) `normalized` : normalizing residuals of curve with 1/mean of the timecourse:: r(i,k) = (y(i,k) - f(xi,k))/mean(y(k)) This allows to use time courses with very different absolute values in a single optimization problem. `absolute_to_baseline` (experimental) : uses the absolute changes to baseline with baseline being the first data point. This requires appropriate pre-simulations for the model to reach a baseline. Data and fits have to be checked carefully. Residuals are calculated as:: r(i,k) = (y(i,k) - ybase(k)) - (f(xi,k) - fbase(k)) `normalized changes baseline` (experimental): Uses the normalized changes to baseline with baseline being the first data point. This requires appropriate pre-simulations for the model to reach a baseline. Data and fits have to be checked carefully. The residuals are calculated as:: r(i,k) = (y(i,k) - ybase(k)) - (f(xi,k) - fbase(k))/mean(y(k)) ## class `WeightingCurvesType(*values)` Weighting w_{k} of the curves k. Users can provide set of weightings for the individual curves. By default no weightings are applied, i.e. all curves are weighted equally if no weighting option is provided:: w_{k} = 1.0 `mapping` : curves k are weighted with the provided user weights in the fit mappings, e.g., counts:: w_{k} = wu_{k} `points` : weighting with the number of data points. Often time courses contain different number of data points. The residuals should contribute equally per data point:: w_{k} = 1.0/count{k} The various options can be combined, e.g. mapping and points results in:: w_{k} = wu_{k}/count{k}/mean{y(k)} ## class `WeightingPointsType(*values)` Weighting w_{i,k} of the data points i within a single fit mapping k. This decides how the data points within a single fit mapping are weighted. `no weighting` (default) : all data points are weighted equally:: w_{i,k} = 1.0 `error_weighting`: data points are weighted as ~1/error # FIXME: update documentation, These must probably be normalized also. if yerr{i,k}: w_{i,k} = 1.0/yerr{i,k} else: w_{i,k} = 1.0/yerr{i,k} --- # sbmlsim.fit.result Result of optimization. ## class `OptimizationResult(parameters: collections.abc.Iterable[sbmlsim.fit.objects.FitParameter], fits: list[scipy.optimize._optimize.OptimizeResult], trajectories: list, sid: str | None = None)` Result of optimization problem. ### `OptimizationResult.combine(opt_results: list['OptimizationResult']) -> 'OptimizationResult'` Combine results from multiple parameter fitting experiments. ### `OptimizationResult.from_json(json_info: str | pathlib.Path) -> 'OptimizationResult'` Load OptimizationResult from Path or str. :param json_info: :return: ### `OptimizationResult.process_fits(parameters: list[sbmlsim.fit.objects.FitParameter], fits: list[scipy.optimize._optimize.OptimizeResult])` Process the optimization results. ### `OptimizationResult.process_traces(parameters: list[sbmlsim.fit.objects.FitParameter], trajectories)` Process the optimization results. ### `OptimizationResult.report(self, path: pathlib.Path | None = None, print_output: bool = True) -> str` Report of optimization. ### `OptimizationResult.to_dict(self)` Convert to dictionary. ### `OptimizationResult.to_json(self, path: pathlib.Path | None = None) -> str | pathlib.Path` Store OptimizationResult as json. Uses the to_dict method. ### `OptimizationResult.to_tsv(self, path: pathlib.Path)` Store fit results as TSV. --- # sbmlsim.fit.runner Module for running parameter optimizations. The optimization can run either run serial or in a parallel version. The parallel optimization uses multiprocessing, i.e. the parallel runner starts processes on the n_cores which run optimization problems. How multiprocessing works, in a nutshell: Process() spawns (fork or similar on Unix-like systems) a copy of the original program. The copy communicates with the original to figure out that (a) it's a copy and (b) it should go off and invoke the target= function (see below). At this point, the original and copy are now different and independent, and can run simultaneously. Since these are independent processes, they now have independent Global Interpreter Locks (in CPython) so both can use up to 100% of a CPU on a multi-cpu box, as long as they dont contend for other lower-level (OS) resources. That's the "multiprocessing" part. ## function `run_optimization(problem: sbmlsim.fit.optimization.OptimizationProblem, size: int = 5, algorithm: sbmlsim.fit.options.OptimizationAlgorithmType = , residual: sbmlsim.fit.options.ResidualType = , loss_function: sbmlsim.fit.options.LossFunctionType = , weighting_curves: list[sbmlsim.fit.options.WeightingCurvesType] | None = None, weighting_points: sbmlsim.fit.options.WeightingPointsType = , seed: int | None = None, variable_step_size: bool = True, relative_tolerance: float = 1e-06, absolute_tolerance: float = 1e-06, n_cores: int | None = 1, serial: bool = False, **kwargs) -> sbmlsim.fit.result.OptimizationResult` Run optimization in parallel. The runner executes the given OptimizationProblem and returns the OptimizationResults. The size defines the repeated optimizations of the problem. Every repeat uses different initial values. To get access to the optimization problem this has to be initialized with the arguments of the runner. :param problem: uninitialized problem to optimize (pickable) :param size: integer number of optimizations :param algorithm: optimization algorithm to use :param residual: handling of residuals :param loss_function: loss function for handling outliers/residual transformation :param weighting_curves: list of options for weighting curves (fit mappings) :param weighting_points: weighting of points :param seed: integer random seed (for sampling of parameters) :param absolute_tolerance: absolute tolerance of simulator :param relative_tolerance: relative tolerance of simulator :param variable_step_size: use variable step size in solver :param n_cores: number of workers :param serial: boolean flag to execute optimization in serial fashion (debugging) :param kwargs: additional arguments for optimizer, e.g. xtol :return: OptimizationResult ## function `worker(kwargs) -> sbmlsim.fit.result.OptimizationResult` Worker for running optimization problem. --- # sbmlsim.fit.analysis Analysis of fitting results. ## class `OptimizationAnalysis(opt_result: sbmlsim.fit.result.OptimizationResult, output_name: str, output_dir: pathlib.Path, op: sbmlsim.fit.optimization.OptimizationProblem | None = None, show_plots: bool = True, show_titles: bool = True, residual: sbmlsim.fit.options.ResidualType | None = None, loss_function: sbmlsim.fit.options.LossFunctionType | None = None, weighting_curves: list[sbmlsim.fit.options.WeightingCurvesType] | None = None, weighting_points: sbmlsim.fit.options.WeightingPointsType | None = None, variable_step_size: bool = True, absolute_tolerance: float = 1e-06, relative_tolerance: float = 1e-06, image_format: str = 'svg', **kwargs) -> None` Class for analyzing optimization results. Creates all plots and results. ### `OptimizationAnalysis.html_report(self, path: pathlib.Path)` Create HTML report of the fit. ### `OptimizationAnalysis.plot_correlation(self, path: pathlib.Path) -> None` Plot correlation of parameters for analysis. ### `OptimizationAnalysis.plot_cost_bar(self, x: numpy.ndarray, path: pathlib.Path) -> None` Plot cost bar plot. Compare costs of all curves. ### `OptimizationAnalysis.plot_cost_scatter(self, x: numpy.ndarray, path: pathlib.Path)` Plot cost scatter plot. Compares cost of model parameters to the given parameter set. ### `OptimizationAnalysis.plot_datapoint_scatter(self, x: numpy.ndarray, path: pathlib.Path)` Plot cost scatter plot. Compares cost of model parameters to the given parameter set. ### `OptimizationAnalysis.plot_fit(self, output_dir: pathlib.Path, x: numpy.ndarray) -> None` Plot fitted curves with experimental data for given parameter set x. Creates an overview of all fit mappings. :param output_dir: path to figures :param x: parameters to evaluate :return: None ### `OptimizationAnalysis.plot_fit_residual(self, output_dir: pathlib.Path, x: numpy.ndarray) -> None` Plot resulting fit for all individual fit mappings. This consists of - data - prediction - residuals - weighed residuals squared For better analysis log and linear results are depicted. :param x: parameters to evaluate ### `OptimizationAnalysis.plot_residual_boxplot(self, x: numpy.ndarray, path: pathlib.Path) -> None` Plot residual boxplot. Compare costs of all curves. ### `OptimizationAnalysis.plot_residual_scatter(self, x: numpy.ndarray, path: pathlib.Path)` Plot residual plot. ### `OptimizationAnalysis.plot_traces(self, path: pathlib.Path) -> None` Plot optimization traces. Optimization time course of costs. ### `OptimizationAnalysis.plot_waterfall(self, path: pathlib.Path)` Create waterfall plot for the fit results. Plots the optimization runs sorted by cost. ### `OptimizationAnalysis.run(self, mpl_parameters: dict[str, Any] | None = None) -> None` Execute complete analysis. This creates all plots and reports. --- # sbmlsim.fit.sampling Sampling of parameter values. ## class `SamplingType(*values)` Type of sampling used. The LHS options are latin hypercube sampling types. ## function `create_samples(parameters: list[sbmlsim.fit.objects.FitParameter], size, sampling=, seed=None, min_bound=1e-10, max_bound=10000000000.0) -> pandas.DataFrame` Create samples from given parameter information. :param parameters: :param size: :param sampling: :param seed: :param min_bound: hard lower bound :param min_bound: hard upper bound :return: ## function `example_sampling() -> None` Run sampling exa how to use sampling. ## function `plot_samples(samples)` Plot samples. --- # sbmlsim.fit.rmse Calculation of statistics. ## function `aic(mse: float, N: int, k: int)` Akaike Information Criterion (AIC). N: datapoints k: parameters ## function `rmse(mse: float)` Root Mean Square Error. --- # sbmlsim.fit.helpers Helper functions for fitting. ## function `f_fitexp(experiment_classes: list[type[sbmlsim.experiment.experiment.SimulationExperiment]], metadata_filters: collections.abc.Callable | collections.abc.Iterable[collections.abc.Callable], base_path: pathlib.Path, data_path: pathlib.Path)` Generic function to get fit experiments for filter. ## function `filter_empty(fit_mapping_key: str, fit_mapping: sbmlsim.fit.objects.FitMapping) -> bool` Return all experiments/mappings. ## function `filter_outlier(fit_mapping_key: str, fit_mapping: sbmlsim.fit.objects.FitMapping) -> bool` Return non outlier experiments. ## function `filtered_fit_experiments(experiment_classes: list[type[sbmlsim.experiment.experiment.SimulationExperiment]], metadata_filters: collections.abc.Callable | collections.abc.Iterable[collections.abc.Callable], base_path: pathlib.Path, data_path: pathlib.Path) -> tuple[dict[str, list[sbmlsim.fit.objects.FitExperiment]], pandas.DataFrame]` Fit experiments based on MappingMetaData. :param experiment_classes: List of SimulationExperiment class definition :param metadata_filter: --- # sbmlsim.fit.petab_omex COMBINE archive for PEtab problems. ## function `create_petab_omex(omex_file: pathlib.Path, yaml_file: pathlib.Path) -> None` Create COMBINE archive for PETab. --- # sbmlsim.sensitivity.analysis Sensitivity analysis. ## class `AnalysisGroup(uid: str, name: str, changes: dict[str, float], color: str | None) -> None` Subgroup for analysis. ## class `SensitivityAnalysis(sensitivity_simulation: sbmlsim.sensitivity.analysis.SensitivitySimulation, parameters: list[sbmlsim.sensitivity.parameters.SensitivityParameter], groups: list[sbmlsim.sensitivity.analysis.AnalysisGroup], results_path: pathlib.Path, seed: int | None = None, n_cores: int | None = None, cache_results: bool = False) -> None` Parent class for all sensitivity analysis. ### `SensitivityAnalysis.calculate_sensitivity(self, cache_filename: str | None = None, cache: bool = False)` Calculate the sensitivity matrices. ### `SensitivityAnalysis.create_samples(self) -> None` Create and set parameter samples. ### `SensitivityAnalysis.execute(self)` Execute the sensitivity analysis. ### `SensitivityAnalysis.plot(self) -> None` Plot the results, implemented by the subclasses. ### `SensitivityAnalysis.plot_sensitivity(self, group_id: str, sensitivity_key: str, cutoff: float | None = 0.1, cluster_rows: bool = True, title: str | None = None, cmap: str = 'seismic', fig_path: pathlib.Path | None = None, **kwargs) -> None` ### `SensitivityAnalysis.read_cache(self, cache_filename: str | None, cache: bool) -> Any | None` Read cached data from the results path, None if not cached. ### `SensitivityAnalysis.results_required(self, group_id: str) -> xarray.core.dataarray.DataArray` Results of a group, raises if the samples are not simulated yet. ### `SensitivityAnalysis.results_table(self) -> pandas.DataFrame` Sizes of the result arrays per group. ### `SensitivityAnalysis.samples_required(self, group_id: str) -> xarray.core.dataarray.DataArray` Samples of a group, raises if the samples are not created yet. ### `SensitivityAnalysis.samples_table(self) -> pandas.DataFrame` Sizes of the sample arrays per group. ### `SensitivityAnalysis.sensitivity_df(self, group_id: str, key: str) -> pandas.DataFrame` Convert sensitivity information to dataframes. ### `SensitivityAnalysis.simulate_samples(self, cache_filename: str | None = None, cache: bool = False) -> None` Simulate all samples in parallel. :param cache_filename: Path to the cache path. :param cache: If True, cache the simulated samples. ### `SensitivityAnalysis.write_cache(self, data: Any, cache_filename: str | None, cache: bool) -> None` Write data to the cache file in the results path. ## class `SensitivityOutput(uid: str, name: str, unit: str | None) -> None` Output measurement for SensitivityAnalysis. ## class `SensitivitySimulation(model_path: pathlib.Path, selections: list[str], changes_simulation: dict[str, float], outputs: list[sbmlsim.sensitivity.analysis.SensitivityOutput])` Base class for sensitivity calculation. The sensitivity simulation runs a model simulation under a given set of model changes and returns a dictionary of scalar outputs. This function is called repeatedly during the sensitivity calculation. ### `SensitivitySimulation.apply_changes(r: roadrunner.roadrunner.RoadRunner, changes: dict[str, float], reset_all: bool = True) -> None` Apply changes after possible reset of the model. ### `SensitivitySimulation.load_model(model_path: pathlib.Path, selections: list[str]) -> roadrunner.roadrunner.RoadRunner` Load roadrunner model. ### `SensitivitySimulation.plot(self) -> None` Plot the model simulation. ### `SensitivitySimulation.simulate(self, r: roadrunner.roadrunner.RoadRunner, changes: dict[str, float]) -> dict[str, float]` Run a model simulation and return scalar results dictionary. ## function `run_simulation(params_tuple)` Pass all required arguments as parameter tuple. --- # sbmlsim.sensitivity.parameters Tools and helpers to handle parameters for sensitivity analysis. ## class `ParameterType(*values)` Types of model parameters. ## class `SensitivityParameter(*, uid: str, name: str, value: float = nan, lower_bound: float = nan, upper_bound: float = nan, unit: str | None = None, type: sbmlsim.sensitivity.parameters.ParameterType = , reference: str = '') -> None` Parameter for SensitivityAnalysis. ### `SensitivityParameter.parameters_from_sbml(sbml_path: 'Path', exclude_ids: 'set[str] | None' = None, exclude_na: 'bool' = True, exclude_zero: 'bool' = True) -> 'list[SensitivityParameter]'` Retrieve parameters from SBML model for the sensitivity analysis. Constant parameters, constant compartments and constant species are returned. :sbml_path: Path to the SBML file. :param exclude_ids: ids to exclude, :param exclude_na: whether to exclude NA values :return: dict[id, name] ### `SensitivityParameter.parameters_set_bounds(parameters: 'Iterable[SensitivityParameter]', bounds: 'Iterable[tuple]') -> 'None'` Set bounds for sensitivity analysis. ### `SensitivityParameter.parameters_to_df(parameters: 'Iterable[SensitivityParameter]', sort: 'bool' = True) -> 'pd.DataFrame'` Create parameter table from parameters. --- # sbmlsim.sensitivity.sensitivity_local Local sensitivity analysis using finite differences. This module implements a local, derivative-based sensitivity analysis using symmetric finite differences around a reference parameter set. Each model parameter is perturbed individually while all other parameters are kept constant. The method is intended for deterministic simulation models and is useful for: - Identifying locally influential parameters - Debugging and inspecting model behavior - Screening parameters prior to optimization or uncertainty analysis - Complementing global sensitivity analysis methods Sensitivities are computed per analysis group and output variable and are reported as both raw and normalized (dimensionless) sensitivities. Notes: For a parameter p with reference value p0, sensitivities are computed as: p_plus = p0 * (1 + difference) p_minus = p0 * (1 - difference) S = (q(p_plus) - q(p_minus)) / (p_plus - p_minus) Normalized sensitivities are defined as: S_norm = S * (p0 / q(p0)) Here a multistep method is implemented following Najjar et al. References: - Najjar A, Hamadeh A, Krause S, Schepky A, Edginton A. Global sensitivity analysis of Open Systems Pharmacology Suite physiologically based pharmacokinetic models. CPT Pharmacometrics Syst Pharmacol. 2024 Dec;13(12):2052-2067. doi: 10.1002/psp4.13256. Epub 2024 Nov 5. PMID: 39498820; PMCID: PMC11646943. ## class `LocalSensitivityAnalysis(sensitivity_simulation: sbmlsim.sensitivity.analysis.SensitivitySimulation, parameters: list[sbmlsim.sensitivity.parameters.SensitivityParameter], groups: list[sbmlsim.sensitivity.analysis.AnalysisGroup], results_path: pathlib.Path, seed: int | None = None, n_cores: int | None = None, cache_results: bool = False, difference: float = 0.01, n_var: int = 3) -> None` Local sensitivity analysis based on symmetric finite differences. Each model parameter is perturbed individually by a small relative amount around a reference parameter set, while all other parameters are held constant. For each parameter, two perturbed simulations (increase and decrease) are evaluated in addition to a reference simulation. Attributes: difference (float): Relative parameter perturbation used for the finite-difference approximation (e.g., 0.01 corresponds to ±1%). prefix (str): Prefix used for naming result files. ### `LocalSensitivityAnalysis.calculate_sensitivity(self, cache_filename: str | None = None, cache: bool = False) -> None` Compute raw and normalized local sensitivities. Sensitivities are calculated using a symmetric finite-difference scheme for each parameter–output combination. Args: cache_filename (str, optional): Filename used to read/write cached sensitivity results. cache (bool, optional): Whether cached results should be used. ### `LocalSensitivityAnalysis.create_samples(self) -> None` Create parameter samples for local sensitivity analysis. For each analysis group, this method constructs a sample matrix containing: - One reference parameter vector - n_var perturbed parameter vectors per parameter (+difference) - n_var perturbed parameter vectors per parameter (-difference) Samples are stored as an ``xarray.DataArray`` indexed by sample and parameter identifiers. ### `LocalSensitivityAnalysis.dfs_sensitivity(self) -> dict[str, pandas.DataFrame]` Return sensitivity dataframe. ### `LocalSensitivityAnalysis.plot(self) -> None` Generate plots for normalized local sensitivities. Produces heatmaps of normalized sensitivities for each analysis group and saves the figures to the results directory. Using default cutoff of 0.1 for negligible. --- # sbmlsim.sensitivity.sensitivity_sampling Sampling-based sensitivity and uncertainty analysis. This module implements a sampling-based sensitivity and uncertainty analysis approach. Model parameters are varied simultaneously within their bounds, and the resulting distribution of model outputs is analyzed statistically. Parameter samples are generated using Latin Hypercube Sampling (LHS), assuming independent and uniformly distributed parameters. For each analysis group and output variable, descriptive statistics are computed, including: - mean and median - standard deviation and coefficient of variation - minimum and maximum - lower and upper quantiles (5% and 95%) Uncertainty is calculated as Ui,j = (Percentile97.5(i,j) - Percentile2.5(i,j)) / Percentile50(i,j) This approach focuses on uncertainty propagation rather than variance-based sensitivity indices and is therefore complementary to local and Sobol-based methods. ## class `SamplingSensitivityAnalysis(sensitivity_simulation: sbmlsim.sensitivity.analysis.SensitivitySimulation, parameters: list[sbmlsim.sensitivity.parameters.SensitivityParameter], groups: list[sbmlsim.sensitivity.analysis.AnalysisGroup], results_path: pathlib.Path, N: int, seed: int | None = None, n_cores: int | None = None, cache_results: bool = False)` Sensitivity/uncertainty analysis based on sampling. ### `SamplingSensitivityAnalysis.calculate_sensitivity(self, cache_filename: str | None = None, cache: bool = False) -> None` Calculate the sensitivity matrices for sampling sensitivity. ### `SamplingSensitivityAnalysis.create_samples(self) -> None` Create LHS samples. Latin hypercube sampling (LHS) is a stratified sampling method used to generate near‑random samples from a multidimensional distribution for Monte Carlo simulations and computer experiments. Assuming uniform distributions within the provided bounds. Use LHS sampling of parameters. ### `SamplingSensitivityAnalysis.df_sampling_sensitivity(self, df_path: pathlib.Path)` Write the sampling sensitivities as a table to the given path. ### `SamplingSensitivityAnalysis.plot(self, **kwargs)` Boxplots for the Sampling sensitivity. ### `SamplingSensitivityAnalysis.plot_data(self, type: str, show_jitter: bool = True, show_violin: bool = True, **kwargs)` Boxplots for the sampled output. --- # sbmlsim.sensitivity.sensitivity_morris Morris sensitivity analysis. This module implements the Method of Morris for global screening-based sensitivity analysis. The Morris method estimates *elementary effects* by sampling trajectories through the parameter space and provides qualitative and semi-quantitative measures of parameter importance. The implementation supports: - Classical Morris sampling (Morris, 1991) - Optimized trajectories (Campolongo et al., 2007) - Local optimization of trajectories (Ruano et al., 2012) For each output variable, the following Morris indices are computed: - mu: Mean of elementary effects - mu_star: Mean of absolute elementary effects - sigma: Standard deviation of elementary effects - mu_star_conf: Confidence interval of mu_star - r: Combined importance measure References: - Morris, M.D., 1991. Factorial Sampling Plans for Preliminary Computational Experiments. Technometrics 33, 161-174. https://doi.org/10.1080/00401706.1991.10484804 - Campolongo, F., Cariboni, J., & Saltelli, A. 2007. An effective screening design for sensitivity analysis of large models. Environmental Modelling & Software, 22(10), 1509-1518. https://doi.org/10.1016/j.envsoft.2006.10.004 - Ruano, M.V., Ribes, J., Seco, A., Ferrer, J., 2012. An improved sampling strategy based on trajectory design for application of the Morris method to systems with many input factors. Environmental Modelling & Software 37, 103-109. https://doi.org/10.1016/j.envsoft.2012.03.008 ## class `MorrisSensitivityAnalysis(sensitivity_simulation: sbmlsim.sensitivity.analysis.SensitivitySimulation, parameters: list[sbmlsim.sensitivity.parameters.SensitivityParameter], groups: list[sbmlsim.sensitivity.analysis.AnalysisGroup], results_path: pathlib.Path, N: int, optimal_trajectories: int, num_levels: int = 4, local_optimization: bool = True, seed: int | None = None, n_cores: int | None = None, cache_results: bool = False, **kwargs)` Morris sensitivity analysis. Campolongo et al., introduces an optimal trajectories approach which attempts to maximize the parameter space scanned for a given number of trajectories (where optimal_trajectories). The approach accomplishes this aim by randomly generating a high number of possible trajectories (500 to 1000) and selecting a subset of r trajectories which have the highest spread in parameter space. The r variable in corresponds to the optimal_trajectories parameter here. Calculating all possible combinations of trajectories can be computationally expensive. The number of factors makes little difference, but the ratio between number of optimal trajectories and the sample size results in an exponentially increasing number of scores that must be computed to find the optimal combination of trajectories. We suggest going no higher than 4 levels from a pool of 100 samples with this “brute force” approach. Ruano et al., proposed an alternative approach with an iterative process that maximizes the distance between subgroups of generated trajectories, from which the final set of trajectories are selected, again maximizing the distance between each. The approach is not guaranteed to produce the most optimal spread of trajectories, but are at least locally maximized and significantly reduce the time taken to select trajectories. With local_optimization = True (which is default), it is possible to go higher than the previously suggested 4 levels from a pool of 100 samples. ### `MorrisSensitivityAnalysis.calculate_sensitivity(self, cache_filename: str | None = None, cache: bool = False)` Perform extended Fourier Amplitude Sensitivity Test on model outputs. Returns a dictionary with keys 'S1' and 'ST', where each entry is a list of size D (the number of parameters) containing the indices in the same order as the parameter file. Returns a result set with keys mu, mu_star, sigma, and mu_star_conf, where each entry corresponds to the parameters defined in the problem spec or parameter file. mu metric indicates the mean of the distribution mu_star metric indicates the mean of the distribution of absolute values sigma is the standard deviation of the distribution ### `MorrisSensitivityAnalysis.create_samples(self) -> None` Create samples using the Method of Morris. Three variants of Morris' sampling for elementary effects are supported: - Vanilla Morris when ``optimal_trajectories`` is ``None``/``False`` and ``local_optimization`` is ``False`` - Optimised trajectories when ``optimal_trajectories=True`` using Campolongo's enhancements and optionally Ruano's enhancement when ``local_optimization=True`` - Morris with groups when the problem definition specifies groups of parameters ### `MorrisSensitivityAnalysis.plot(self) -> None` Morris plot. --- # sbmlsim.sensitivity.sensitivity_sobol Global sensitivity analysis using Sobol indices. This module provides routines for variance-based global sensitivity analysis using Sobol indices. Sobol analysis decomposes the variance of model outputs into contributions from individual parameters and their interactions. The following indices are computed: - First-order indices (S1) - Total-effect indices (ST) - Associated confidence intervals Sampling is based on Saltelli's extension of the Sobol sequence and requires (2D + 2) * N model evaluations for D parameters. References: - Sobol, I. M. (2001). Math. Comput. Simul., 55, 271–280. - Saltelli, A. (2002). Comput. Phys. Commun., 145, 280–297. - Saltelli et al. (2010). Comput. Phys. Commun., 181, 259–270. ## class `SobolSensitivityAnalysis(sensitivity_simulation: sbmlsim.sensitivity.analysis.SensitivitySimulation, parameters: list[sbmlsim.sensitivity.parameters.SensitivityParameter], groups: list[sbmlsim.sensitivity.analysis.AnalysisGroup], results_path: pathlib.Path, N: int, seed: int | None = None, n_cores: int | None = None, cache_results: bool = False, **kwargs)` Global sensitivity analysis based on Sobol method. ### `SobolSensitivityAnalysis.calculate_sensitivity(self, cache_filename: str | None = None, cache: bool = False)` Calculate the sensitivity matrices for SOBOL analysis. ### `SobolSensitivityAnalysis.create_samples(self) -> None` Create samples for sobol. Generates model inputs using Saltelli's extension of the Sobol' sequence The Sobol' sequence is a popular quasi-random low-discrepancy sequence used to generate uniform samples of parameter space. ### `SobolSensitivityAnalysis.plot(self) -> None` Plot the Sobol indices as heatmaps and bar plots. --- # sbmlsim.sensitivity.sensitivity_fast Global sensitivity analysis using FAST (Fourier Amplitude Sensitivity Test). This module implements variance-based global sensitivity analysis using the Fourier Amplitude Sensitivity Test (FAST). FAST quantifies the contribution of individual model parameters to the variance of model outputs by mapping parameter variations onto periodic functions and analyzing the resulting output spectrum in the frequency domain. The method provides efficient estimation of first-order (main-effect) sensitivity indices and, in extended variants (eFAST), total-effect indices. Compared to Monte Carlo–based Sobol methods, FAST offers favorable scaling with the number of parameters and is well suited for medium- to large-scale deterministic models. This implementation is intended for use in computational modeling workflows, including systems biology, pharmacokinetics/pharmacodynamics, and digital twin applications, where a robust global assessment of parameter influence is required. References: Cukier, R. I., Fortuin, C. M., Shuler, K. E., Petschek, A. G., & Schaibly, J. H. (1973). Study of the sensitivity of coupled reaction systems to uncertainties in rate coefficients. I. Theory. Journal of Chemical Physics, 59, 3873–3878. https://doi.org/10.1063/1.1680571 Saltelli, A., Tarantola, S., & Chan, K. P.-S. (1999). A quantitative model-independent method for global sensitivity analysis of model output. Technometrics, 41(1), 39–56. https://doi.org/10.1080/00401706.1999.10485594 ## class `FASTSensitivityAnalysis(sensitivity_simulation: sbmlsim.sensitivity.analysis.SensitivitySimulation, parameters: list[sbmlsim.sensitivity.parameters.SensitivityParameter], groups: list[sbmlsim.sensitivity.analysis.AnalysisGroup], results_path: pathlib.Path, N: int, M: int = 4, seed: int | None = None, n_cores: int | None = None, cache_results: bool = False, **kwargs)` Global sensitivity analysis using the Fourier Amplitude Sensitivity Test. This class implements the FAST methodology for estimating first-order and total-effect sensitivity indices. It integrates with the common sensitivity analysis infrastructure provided by `SensitivityAnalysis` and supports grouped analyses and multiple model outputs. References: Cukier et al. (1973); Saltelli et al. (1999) ### `FASTSensitivityAnalysis.calculate_sensitivity(self, cache_filename: str | None = None, cache: bool = False)` Compute FAST sensitivity indices for all model outputs. This method performs the extended Fourier Amplitude Sensitivity Test (eFAST) to estimate first-order (S1) and total-effect (ST) sensitivity indices, along with corresponding confidence intervals, for each model output and parameter. Args: cache_filename: Optional filename for reading or writing cached sensitivity results. cache: Whether to read from or write results to cache. Notes: The sensitivity indices are computed independently for each output variable and stored in a structured xarray-based format. ### `FASTSensitivityAnalysis.create_samples(self) -> None` Create parameter samples for FAST analysis. This method generates FAST samples for each analysis group using the SALib FAST sampler. The resulting samples are stored as xarray objects and later used for model evaluation. ### `FASTSensitivityAnalysis.plot(self) -> None` Generate standard FAST sensitivity plots. This method creates heatmaps and bar plots for first-order (S1) and total-effect (ST) sensitivity indices for each analysis group and stores the resulting figures in the configured results directory. --- # sbmlsim.sensitivity.classification Classification of sensitivities and uncertainties. ## class `SensitivityClassification(*values)` Sensitivity classification. ## class `UncertaintyClassification(*values)` Uncertainty classification. ## function `sensitivity_classification(s: float) -> sbmlsim.sensitivity.classification.SensitivityClassification` Classification of local sensitivity as per WHO IPCS guidance. Classification based on absolute sensitivity: - High: `|Si,j| >= 0.5` - Medium: `0.2 <= |Si,j| < 0.5` - Low: `0.1 <= |Si,j| < 0.2` - Negligible: `|Si,j| < 0.1` References: International Programme on Chemical Safety (IPCS). Characterization and application of physiologically based pharmacokinetic models in risk assessment. World Health Organization; 2010. Contract No.: 9. ## function `sensitivity_classification_symbol(s: float) -> str` Calculates symbol for sensitivity classification. ## function `uncertainty_classification(u: float) -> sbmlsim.sensitivity.classification.UncertaintyClassification` Classification of uncertainty as per WHO IPCS guidance. - High: `Ui,j >= 2` - Medium: `0.3 <= |Ui,j| < 2` - Low: `0 <= |Ui,j| < 0.3` References: International Programme on Chemical Safety (IPCS). Characterization and application of physiologically based pharmacokinetic models in risk assessment. World Health Organization; 2010. Contract No.: 9. ## function `uncertainty_classification_symbol(u: float) -> str` Calculates symbol for uncertainty classification. --- # sbmlsim.sensitivity.plots Plotting functionality for sensitivity analysis. ## function `S1_ST_barplot(S1, ST, S1_conf, ST_conf, parameter_labels: dict[str, str], fig_path: pathlib.Path | None = None, title: str | None = None, ymax: float = 1.1, ymin: float = -0.1)` ## function `heatmap(df: pandas.DataFrame, parameter_labels: dict[str, str] | None = None, output_labels: dict[str, str] | None = None, cutoff: float | None = 0.1, annotate_values=True, cluster_rows: bool = True, cluster_cols: bool = False, title: str | None = None, cmap: str = 'seismic', vcenter: float = 0.0, vmin: float = -2.0, vmax: float = 2.0, fig_path: pathlib.Path | None = None)` Creates heatmap of model sensitivity. ## function `plot_S1_ST_indices(sa, fig_path: pathlib.Path)` Barplots for the S1 and ST indices. --- # sbmlsim.combine.sedml.parser SED-ML support for sbmlsim. This modules parses SED-ML based simulation experiments in the sbmlsim SimulationExperiment format and executes them. Overview SED-ML ---------------- SED-ML is build of the main classes - DataDescription - Model - Simulation - Task - DataGenerator - Output DataDescription --------------- The DataDescription allows to reference external data, and contains a description on how to access the data, in what format it is, and what subset of data to extract. Model ----- The Model class is used to reference the models used in the simulation experiment. SED-ML itself is independent of the model encoding underlying the models. The only requirement is that the model needs to be referenced by using an unambiguous identifier which allows for finding it, for example using a MIRIAM URI. To specify the language in which the model is encoded, a set of predefined language URNs is provided. The SED-ML Change class allows the application of changes to the referenced models, including changes on the XML attributes, e.g. changing the value of an observable, computing the change of a value using mathematics, or general changes on any XML element of the model representation that is addressable by XPath expressions, e.g. substituting a piece of XML by an updated one. Simulation ---------- The Simulation class defines the simulation settings and the steps taken during simulation. These include the particular type of simulation and the algorithm used for the execution of the simulation; preferably an unambiguous reference to such an algorithm should be given, using a controlled vocabulary, or ontologies. One example for an ontology of simulation algorithms is the Kinetic Simulation Algorithm Ontology KiSAO. Further information encodable in the Simulation class includes the step size, simulation duration, and other simulation-type dependent information. Task ---- SED-ML makes use of the notion of a Task class to combine a defined model (from the Model class) and a defined simulation setting (from the Simulation class). A task always holds one reference each. To refer to a specific model and to a specific simulation, the corresponding IDs are used. DataGenerator ------------- The raw simulation result sometimes does not correspond to the desired output of the simulation, e.g. one might want to normalise a plot before output, or apply post-processing like mean-value calculation. The DataGenerator class allows for the encoding of such post-processings which need to be applied to the simulation result before output. To define data generators, any addressable variable or parameter of any defined model (from instances of the Model class) may be referenced, and new entities might be specified using MathML definitions. Output ------- The Output class defines the output of the simulation, in the sense that it specifies what shall be plotted in the output. To do so, an output type is defined, e.g. 2D-plot, 3D-plot or data table, and the according axes or columns are all assigned to one of the formerly specified instances of the DataGenerator class. For information about SED-ML please refer to http://www.sed-ml.org/ and the SED-ML specification. ## class `SBMLModelTarget(selection: str, target_type: sbmlsim.combine.sedml.parser.SBMLModelTargetType)` Target in an SBML model. ### `SBMLModelTarget.sbmlsim_model_targets(r: roadrunner.roadrunner.ExecutableModel) -> dict[str, 'SBMLModelTarget']` Model targets which are supported by sbmlsim. ## class `SBMLModelTargetType(*values)` Supported target types in SBML models. ## class `SEDMLParser(sed_doc: libsedml.SedDocument, exec_dir: pathlib.Path, working_dir: pathlib.Path, name: str | None = None)` Parse SED-ML to sbmlsim.SimulationExperiment. ### `SEDMLParser.data_from_datagenerator(self, sed_dg_ref: str | None) -> sbmlsim.data.Data | None` Evaluate DataGenerator with actual data. Uses results of SimulationExperiment for evaluation. ### `SEDMLParser.data_generators_for_task(self, sed_task: libsedml.SedAbstractTask) -> list[libsedml.SedDataGenerator]` Get DataGenerators which reference the given task. ### `SEDMLParser.get_ordered_subtasks(sed_task: libsedml.SedRepeatedTask) -> list[libsedml.SedSubTask]` Ordered list of subtasks for task. ### `SEDMLParser.parse_abstract_curve(self, sed_acurve: libsedml.SedAbstractCurve) -> sbmlsim.plot.plotting.ShadedArea | sbmlsim.plot.plotting.Curve` Parse abstract curve. ### `SEDMLParser.parse_algorithm_parameter(self, sed_alg_par: libsedml.SedAlgorithmParameter) -> sbmlsim.simulation.algorithm.AlgorithmParameter` Parse algorithm parameter information. ### `SEDMLParser.parse_axis(self, sed_axis: libsedml.SedAxis) -> sbmlsim.plot.plotting.Axis | None` Parse axes information. ### `SEDMLParser.parse_change(self, sed_change: libsedml.SedChange) -> dict` Parse the libsedml.Change. Currently only a limited subset of model changes is supported. Namely changes of parameters and concentrations within a SedChangeAttribute. ### `SEDMLParser.parse_figure(self, sed_figure: libsedml.SedFigure) -> sbmlsim.plot.plotting.Figure` Parse figure information. ### `SEDMLParser.parse_fill(self, sed_fill: libsedml.SedFill) -> sbmlsim.plot.plotting.Fill | None` Parse fill information. ### `SEDMLParser.parse_line(self, sed_line: libsedml.SedLine) -> sbmlsim.plot.plotting.Line | None` Parse line information. ### `SEDMLParser.parse_marker(self, sed_marker: libsedml.SedMarker) -> sbmlsim.plot.plotting.Marker | None` Parse the line information. ### `SEDMLParser.parse_model(self, sed_model: libsedml.SedModel, source: str, sed_changes: list[libsedml.SedChange]) -> sbmlsim.model.model.AbstractModel` Convert SedModel to AbstractModel. :param sed_changes: :param source:s :param sed_model: :return: ### `SEDMLParser.parse_plot2d(self, sed_plot2d: libsedml.SedPlot2D) -> sbmlsim.plot.plotting.Plot` Parse the libsedml.Plot2D into a sbmlsim.Plot. ### `SEDMLParser.parse_plot3d(self, sed_plot3d: libsedml.SedPlot3D) -> sbmlsim.plot.plotting.Plot` Parse Plot3D. ### `SEDMLParser.parse_report(self, sed_report: libsedml.SedReport) -> dict[str, str]` Parse Report. :return dictionary of label: dataGenerator.id mapping. ### `SEDMLParser.parse_simulation(self, sed_sim: libsedml.SedSimulation) -> sbmlsim.simulation.timecourse.TimecourseSim` Parse simulation information. ### `SEDMLParser.parse_style(self, sed_style: str | libsedml.SedStyle) -> sbmlsim.plot.plotting.Style | None` Parse SED-ML style. ### `SEDMLParser.parse_task(self, sed_task: libsedml.SedAbstractTask) -> sbmlsim.task.task.Task | libsedml.SedAbstractTask` Parse arbitrary task (repeated or simple, or simple repeated). ### `SEDMLParser.parse_xpath_target(xpath: str) -> str` Resolve targets in xpath expression. Uses a heuristics to figure out the targets. ### `SEDMLParser.print_info(self) -> None` Print information. ### `SEDMLParser.required_data_from_datagenerator(self, sed_dg_ref: str) -> sbmlsim.data.Data` Evaluate a DataGenerator which is required, e.g., the x data of a curve. ### `SEDMLParser.resolve_model_changes(self)` Resolve the original model sources and full change lists. Going through the tree of model upwards until root is reached and collecting changes on the way (example models m* and changes c*) m1 (source) -> m2 (c1, c2) -> m3 (c3, c4) resolves to m1 (source) [] m2 (source) [c1,c2] m3 (source) [c1,c2,c3,c4] The order of changes is important (at least between nodes on different levels of hierarchies), because later changes of derived models could reverse earlier changes. Uses recursive search strategy, which should be okay as long as the model tree hierarchy is not getting to deep. ## class `SEDMLSerializer(exp_class: type[sbmlsim.experiment.experiment.SimulationExperiment], working_dir: pathlib.Path, sedml_filename: str, omex_path: pathlib.Path | None = None, data_path: pathlib.Path | None = None)` Serialize SimulationExperiment to SED-ML. Creates the SED-ML and the COMBINE archive containing all models and data for the simulation experiment. ### `SEDMLSerializer.datagenerator_id_from_data(self, data: sbmlsim.data.Data) -> str` Get the data generator id from data. ### `SEDMLSerializer.serialize_axis(self, axis: sbmlsim.plot.plotting.Axis, sed_axis: libsedml.SedAxis) -> None` Serialize sbmlsim.Axis to libsedml.SEDAxis. ### `SEDMLSerializer.serialize_data(self)` Serialize data generators. Write experiment data in SedDocument. ### `SEDMLSerializer.serialize_datasets(self)` Serialize sbmlsim.DataSets to libsedml.DataDescription. Write experiment datasets in SedDocument. ### `SEDMLSerializer.serialize_figures(self)` Serialize sbmlsim.Figures to libsedml.SedFigures. Write experiment figures in SedDocument. ### `SEDMLSerializer.serialize_models(self)` Serialize models. Write experiment models in SedDocument. ### `SEDMLSerializer.serialize_simulations(self)` Serialize simulations. Write experiment simulations in SedDocument. ### `SEDMLSerializer.serialize_style(self, style: sbmlsim.plot.plotting.Style, sed_style: libsedml.SedStyle) -> None` Serialize sbmlsim.Style to libsedml.Style. ### `SEDMLSerializer.serialize_tasks(self)` Serialize tasks. Write experiment tasks in SedDocument. --- # sbmlsim.combine.sedml.runner Module with helpers to execute SED-ML files and COMBINE archives. ## function `execute_sedml(path: pathlib.Path, working_dir: pathlib.Path, output_path: pathlib.Path) -> None` Execute the given SED-ML in the working directory. :param path: path to SED-ML file or OMEX archive. :param working_dir: directory for execution and resources :return: --- # sbmlsim.combine.sedml.task Task trees of SED-ML documents. ## class `Stack()` Stack implementation for nodes. ### `Stack.isEmpty(self)` Check if the stack is empty. ### `Stack.peek(self)` Return the top item without removing it. ### `Stack.pop(self)` Pop the top item. ### `Stack.push(self, item)` Push an item on the stack. ### `Stack.size(self)` Number of items on the stack. ## class `TaskNode(task: libsedml.SedAbstractTask, depth: int)` Tree implementation of task tree. ### `TaskNode.add_child(self, obj)` Add a child node. ### `TaskNode.info(self) -> str` Render the node. ### `TaskNode.is_leaf(self)` Check if the node has no children. ## class `TaskTree()` Tree of the tasks of a SED-ML document. ### `TaskTree.from_sedml_task(sed_task: libsedml.SedDocument, root_task: libsedml.SedAbstractTask) -> sbmlsim.combine.sedml.task.TaskNode` Creates task tree for given SedTask. The task tree is used to resolve the order of all simulations. ### `TaskTree.get_ordered_subtasks(repeated_task: libsedml.SedRepeatedTask) -> list[libsedml.SedSubTask]` Ordered list of subtasks for repeated task. --- # sbmlsim.combine.sedml.data Reading NUML, CSV and TSV data from DataDescriptions. ## class `DataDescriptionParser()` Class for parsing DataDescriptions. --- # sbmlsim.combine.sedml.numl Parser for NuML data. ## class `NumlParser()` Helper class for parsing Numl data files. ### `NumlParser.Library(*values)` Bugfix helper for managing the library issues. --- # sbmlsim.combine.sedml.report Reports. ## class `Report(sid: str, name: str | None = None, datasets: dict[str, str] | None = None)` Reports of simulation experiments. Collections of data generators. ### `Report.add_dataset(self, label: str, data_id: str) -> None` Add dataset for given label. --- # sbmlsim.combine.sedml.io Template functions to run the example cases. ## class `SEDMLInputType(*values)` Types of SED-ML input. SED-ML can be read from string, file or a COMBINE archive (or zip archives). ## class `SEDMLReader(source: pathlib.Path | str, working_dir: pathlib.Path | None = None)` Class for reading SED-ML document from various sources. SED-ML can be provided as string, file or as file in a COMBINE archive. Execution must be performed where the master SED-ML is located. ### `SEDMLReader.read_sedml(self) -> tuple[libsedml.SedDocument, sbmlsim.combine.sedml.io.SEDMLInputType]` Read SedMLDocument. Sets the instance variables as a result. ## function `check_sedml_doc(sed_doc: libsedml.SedDocument) -> libsedml.SedErrorLog` Check SedDocument for errors. Logs errors and warnings :param sed_doc: SedDocument. :return SedErrorLog. --- # sbmlsim.combine.datagenerator DataGenerator. ## class `DataGenerator(f: sbmlsim.combine.datagenerator.DataGeneratorFunction, xresults: dict[str, sbmlsim.result.xresult.XResult], dsets: dict[str, sbmlsim.data.DataSet] | None = None)` DataGenerator. DataGenerators allow to postprocess existing data. This can be a variety of operations. - Slicing: reduce the dimension of a given XResult, by slicing a subset on a given dimension - Cumulative processing: mean, sd, ... - Complex processing, such as pharmacokinetics calculation. ### `DataGenerator.process(self) -> dict[str, sbmlsim.result.xresult.XResult]` Process the data generator. ## class `DataGeneratorFunction()` DataGeneratorFunction. ## class `DataGeneratorIndexingFunction(index: int, dimension: str = '_time')` DataGeneratorIndexingFunction. --- # sbmlsim.combine.mathml Helper functions for evaluation of MathML expressions. Using sympy to evaluate the expressions. ## function `astnode_to_formula(astnode: libsedml.ASTNode) -> str` Write ASTNode as formula. ## function `evaluate(astnode: libsedml.ASTNode, variables: dict)` Evaluate the astnode with values. ## function `expr_from_formula(formula: str)` Parse sympy expression from given formula string. ## function `formula_to_astnode(formula: str) -> libsedml.ASTNode` Parse ASTNode from formula. ## function `parse_astnode(astnode: libsedml.ASTNode) -> Any` Parse ASTNode. An AST node in libSBML is a recursive tree structure; each node has a type, a pointer to a value, and a list of children nodes. Each ASTNode node may have none, one, two, or more children depending on its type. There are node types to represent numbers (with subtypes to distinguish integer, real, and rational numbers), names (e.g., constants or variables), simple mathematical operators, logical or relational operators and functions. see also: http://sbml.org/Software/libSBML/docs/python-api/libsedml-math.html :param mathml: :return: ## function `parse_formula(formula: str) -> libsedml.ASTNode` Parse formula to ASTNode. ## function `parse_mathml_str(mathml_str: str)` Parse MathML string. ## function `replace_piecewise(formula)` Replace libsedml piecewise with sympy piecewise. --- # sbmlsim.interpolation.interpolation Create files for interpolation of datasets. https://github.com/allyhume/SBMLDataTools https://github.com/allyhume/SBMLDataTools.git TODO: fix composition with existing models TODO: support coupling with existing models via comp The functionality is very useful, but only if this can be applied to existing models in a simple manner. ## class `Interpolation(data: pandas.DataFrame, method: str = 'linear')` Create SBML which interpolates the given data. The second to last components are interpolated against the first component. ### `Interpolation.add_interpolator_to_model(interpolator: 'Interpolator', model: libsbml.Model) -> None` Add interpolator to model. The parameters, formulas and rules have to be added to the SBML model. :param interpolator: :param model: Model :return: ### `Interpolation.create_interpolators(data: pandas.DataFrame, method: str) -> list[sbmlsim.interpolation.interpolation.Interpolator]` Create all interpolators for the given data set. The columns 1, ... (Ncol-1) are interpolated against column 0. ### `Interpolation.from_csv(csv_file: pathlib.Path | str, method: str = 'linear', sep: str = ',') -> 'Interpolation'` Interpolation object from csv file. ### `Interpolation.from_tsv(tsv_file: pathlib.Path | str, method: str = 'linear') -> 'Interpolation'` Interpolate object from tsv file. ### `Interpolation.validate_data(self) -> None` Validate the input data. * The data is expected to have at least 2 columns. * The data is expected to have at least three data rows. * The first column should be in ascending order. :return: :rtype: ### `Interpolation.write_sbml_to_file(self, sbml_out: pathlib.Path) -> None` Write the SBML file. :param sbml_out: Path to SBML file :return: ### `Interpolation.write_sbml_to_string(self) -> str` Write the SBML file. :return: SBML str ## class `Interpolator(x: pandas.Series, y: pandas.Series, z: pandas.Series | None = None, method: str = 'constant')` Interpolator class handles the interpolation of given data series. Two data series and the type of interpolation are provided. ### `Interpolator.formula(self) -> str` Get formula string. --- # sbmlsim.comparison.diff Helpers for numerical comparison of data. Allows to tests semi-automatically for problems with the various models. Used to benchmark the simulation results. ## class `DataSetsComparison(dfs_dict: dict[str, pandas.DataFrame], columns_filter=None, time_column: bool = True, title: str | None = None, selections: dict[str, str] | None = None, factors: dict[str, float] | None = None)` Comparing multiple simulation results. Only the subset of identical columns are compared. In the beginning a matching of column names is performed to find the subset of columns which can be compared. The simulations must contain a "time" column with identical time points. ### `DataSetsComparison.df_diff(self)` Dataframe of all differences between the files. https://github.com/sbmlteam/sbml-test-suite/blob/master/cases/semantic/README.md Let the following variables be defined: * `abs_tol` stand for the absolute tolerance for a tests case, * `rel_tol` stand for the relative tolerance for a tests case, * `c_ij` stand for the expected correct value for row `i`, column `j`, of the result data set for the tests case * `u_ij` stand for the corresponding value produced by a given software simulation system run by the user These absolute and relative tolerances are used in the following way: a data point `u_ij` is considered to be within tolerances if and only if the following expression is true: |c_ij - u_ij| <= (abs_tol + rel_tol * |c_ij|) ### `DataSetsComparison.is_equal(self)` Check if DataFrames are identical within numerical tolerance. ### `DataSetsComparison.plot_diff(self)` Plot lines for entries which are above epsilon treshold. ### `DataSetsComparison.report(self)` Report. ### `DataSetsComparison.report_str(self) -> str` Get report as string. ## function `get_files_by_extension(base_path: pathlib.Path, extension: str = '.json') -> dict[str, str]` Get all files by given extension. Simulation definitions are json files. --- # Development Contributions are welcome. The repository is [matthiaskoenig/sbmlsim](https://github.com/matthiaskoenig/sbmlsim); development happens against the `develop` branch via pull requests. ## Setup development environment Development needs [uv](https://docs.astral.sh/uv/) and a checkout of the repository: ```bash git clone https://github.com/matthiaskoenig/sbmlsim.git cd sbmlsim ``` A single sync creates the virtual environment in `.venv`, installs `sbmlsim` into it in editable mode and adds the complete tooling: ```bash uv sync --extra dev ``` The `dev` extra contains everything used below, i.e., pytest, ruff, ty, tox, pre-commit, zensical and bump-my-version, so nothing has to be installed separately. The python version is taken from `.python-version` (currently 3.14); to work against the oldest supported version instead use `uv sync --extra dev --python 3.13`, which replaces the environment. The tools are then run either with `uv run `, which uses the environment without activating it, or from the activated environment: ```bash source .venv/bin/activate # Linux and macOS .venv\Scripts\activate # Windows ``` The commands in this document are written without the `uv run` prefix; prepend it if the environment is not activated. The last step installs the git hook: ```bash uv run pre-commit install # install the hook, once per checkout uv run pre-commit run --all-files # check the current state of the repository ``` From now on every commit is checked with ruff (lint and format) and ty, i.e., the same checks that run in continuous integration. On a commit only the changed files are looked at, `--all-files` checks the whole repository and is what a newly added hook should be tried with. ## Testing The tests are written with pytest, tox runs them against every supported python version. The tox environments are named after the interpreter (`py3.13` to `py3.14`, see `envlist` in `tox.ini`), a single one is run with ```bash tox r -e py3.14 ``` and the complete matrix, including the `ty` environment, in parallel with ```bash tox run-parallel ``` This needs the interpreters to be available, which uv installs with `uv python install 3.13 3.14`. Continuous integration runs the same environments as `uvx --with tox-uv tox -e py3.14`. To run the tests directly against the development environment use ```bash pytest # the full suite pytest tests/simulation/test_simulation.py # a single module pytest tests/simulation/test_simulation.py::test_timecourse # a single test ``` The `conftest.py` at the root of the repository selects the non-interactive matplotlib backend for the session and puts the repository on `sys.path`, so that the tests can import the examples. Some tests are skipped on purpose: the parameter fitting and the simulation experiment examples are marked with `pytest.mark.skip` while those parts of the package are reworked, and `tests/experiment/test_covid_examples.py` waits for relative paths in SED-ML. The skips are listed with `pytest -rs`. `tests/examples/test_example_scripts.py` runs the examples as `python -m examples.` in a temporary working directory, so a broken example fails the test suite. ## Linting and formatting Linting and formatting use [ruff](https://docs.astral.sh/ruff/): ```bash ruff check # lint ruff format # format ``` The docstring rules (`D`) are enforced for the package, not for `examples/` and `tests/`, which are scripts and fixtures; the model definitions of the examples import the names of `sbmlutils.factory` with a star import, so `F403`/`F405` are ignored there as well, see `.ruff.toml`. ## Type checking Type checking is performed with [ty](https://docs.astral.sh/ty/): ```bash tox r -e ty ``` Or directly in the working tree: ```bash uvx ty check ``` The configuration lives in `[tool.ty]` in `pyproject.toml`. Warnings are treated as errors, so the codebase is kept free of diagnostics. Suppress an unavoidable diagnostic with a rule specific `# ty: ignore[rule-name]` rather than a blanket comment. libsbml, libsedml, libnuml and roadrunner have no type stubs and create their objects through a SWIG layer, so ty sees an untyped API. Annotate their objects (`doc: libsbml.SBMLDocument = ...`) and use the explicit getters (`getVariable()`) rather than the attributes the SWIG layer synthesizes (`variable`), which the type checker cannot see. The generated AMICI model code under `src/sbmlsim/comparison/` is excluded from ruff and ty, it is not written by hand. ## Examples The examples are runnable scripts in `examples/`, they are not part of the package. They are run as modules from the root of the repository: ```bash python -m examples.timecourse python -m examples.demo.demo ``` An example writes what it creates into the current working directory and never opens a window: a plotting example saves its figure to a file. `tests/examples/test_example_scripts.py` runs the example scripts in a temporary directory, so a broken example fails the test suite. See `examples/README.md`. ## Documentation The documentation is built with [Zensical](https://zensical.org/), the static site generator of the Material for MkDocs authors. The sources are markdown files in `docs/`, the site is configured in `zensical.toml` in the repository root. Nothing rendered is committed: the site is built by the `documentation` workflow on every push and published to [matthiaskoenig.github.io/sbmlsim](https://matthiaskoenig.github.io/sbmlsim) from the `develop` branch. Build the site into `site/`: ```bash uv run zensical build --clean ``` For writing, the preview rebuilds on save: ```bash uv run zensical serve ``` The API reference is rendered from the docstrings by [mkdocstrings](https://mkdocstrings.github.io/); a page in `docs/api/` only contains the module directive: ```markdown # simulation.timecourse ::: sbmlsim.simulation.timecourse ``` Docstrings are therefore the place to document functions and classes, the markdown files provide the narrative around them. Adding a module to the reference means adding such a page and an entry to `nav` in `zensical.toml`. ### Files for agents { #files-for-agents } Agents and language models read markdown, not rendered html. `scripts/llms_txt.py` writes the files of the [llms.txt convention](https://llmstxt.org/) into the built site, i.e., [llms.txt](https://matthiaskoenig.github.io/sbmlsim/llms.txt) as an annotated index of all pages, [llms-full.txt](https://matthiaskoenig.github.io/sbmlsim/llms-full.txt) with the complete documentation in a single file, and the markdown of every page next to its html (`/creation.md` for `/creation/`). The markdown of the API reference is generated from the docstrings with `inspect`, since the pages themselves only contain the mkdocstrings directive. ```bash uv run zensical build --clean uv run python scripts/llms_txt.py ``` The `documentation` workflow runs both steps, so the files are regenerated with every push. `docs/robots.txt` points crawlers at the sitemap and at these files. Zensical will provide agent context files itself at some point, then this script can go. ## Release A release is made from `develop`: 1. write the release notes for the version in `release-notes/` 2. make sure everything passes: `tox run-parallel`, `ruff check`, `tox r -e ty` 3. check the version bump: `uvx bump-my-version bump [major|minor|patch] --dry-run -vv` 4. bump the version: `uvx bump-my-version bump [major|minor|patch]`, which updates `src/sbmlsim/__init__.py` and `CITATION.cff`, commits and tags 5. `git push --tags`, which triggers the release workflow publishing to [pypi](https://pypi.org/project/sbmlsim/), followed by `git push` 6. test the installation from pypi in a fresh environment: ```bash uv venv --python 3.14 uv pip install sbmlsim ``` 7. once Zenodo has archived the release, update the citation information, i.e., `date-released` in `CITATION.cff` and the version, date and version DOI of the release in the citation of `README.md` and `docs/index.md`. `bump-my-version` only updates the version, not the date and the DOI, which are only known after the release