# Reno System Dynamics (`reno-sd`)
[](https://github.com/psf/black)
[](https://badge.fury.io/py/reno-sd)
[](https://anaconda.org/conda-forge/reno-sd)
[](https://github.com/ORNL/reno/actions/workflows/tests.yml)
[](https://github.com/ORNL/reno/blob/main/LICENSE)
[](https://github.com/ORNL/reno)
Reno is a tool for creating, visualizing, and analyzing system dynamics
models in Python. It additionally has the ability to convert models to PyMC,
allowing Bayesian inference on models with variables that include prior probability
distributions.
Reno models are created by defining the equations for the various stocks, flows,
and variables, and can then be simulated over time similar to something like
[Insight Maker](https://insightmaker.com/), examples of which can be seen below
and in the `notebooks` folder.
Currently, models only support discrete timesteps (technically implementing
difference equations rather than true differential equations.)
## Installation
Install from PyPI via:
```
pip install reno-sd
```
Install from conda-forge with:
```
conda install reno-sd
```
## Example
A classic system dynamics example is the predator-prey population model,
described by the [Lotka-Volterra equations](https://en.wikipedia.org/wiki/Lotka%E2%80%93Volterra_equations).
Implementing these in Reno would look something like:
```python
import reno
m = reno.Model(name="m", steps=200, doc="Classic predator-prey interaction model example")
# make stocks to monitor the predator/prey populations over time
m.rabbits = reno.Stock(init=100.0)
m.foxes = reno.Stock(init=100.0)
# free variables that can quickly be changed to influence equilibrium
m.rabbit_growth_rate = reno.Variable(.1, doc="Alpha")
m.rabbit_death_rate = reno.Variable(.001, doc="Beta")
m.fox_death_rate = reno.Variable(.1, doc="Gamma")
m.fox_growth_rate = reno.Variable(.001, doc="Delta")
# flows that define how the stocks are influenced
m.rabbit_births = reno.Flow(m.rabbit_growth_rate * m.rabbits)
m.rabbit_deaths = reno.Flow(m.rabbit_death_rate * m.rabbits * m.foxes, max=m.rabbits)
m.fox_deaths = reno.Flow(m.fox_death_rate * m.foxes, max=m.foxes)
m.fox_births = reno.Flow(m.fox_growth_rate * m.rabbits * m.foxes)
# hook up inflows/outflows for stocks
m.rabbits += m.rabbit_births
m.rabbits -= m.rabbit_deaths
m.foxes += m.fox_births
m.foxes -= m.fox_deaths
```
The stock and flow diagram for this model (obtainable via `m.graph()`) looks
like this: (green boxes are variables, white boxes are stocks, the labels between
arrows are the flows)

Once a model is defined it can be called like a function, optionally configuring
any free variables/initial values by passing them as arguments. You can print the
output of `m.get_docs()` to see a docstring showing all possible arguments and
what calling it should look like:
```
>>> print(m.get_docs())
Classic predator-prey interaction model example
Example:
m(rabbit_growth_rate=0.1, rabbit_death_rate=0.001, fox_death_rate=0.1, fox_growth_rate=0.001, rabbits_0=100.0, foxes_0=100.0)
Args:
rabbit_growth_rate: Alpha
rabbit_death_rate: Beta
fox_death_rate: Gamma
fox_growth_rate: Delta
rabbits_0
foxes_0
```
To run and plot the population stocks:
```python
m(fox_growth_rate=.002, rabbit_death_rate=.002, rabbits_0=120.0)
reno.plot_refs([(m.rabbits, m.foxes)])
```

To use Bayesian inference, we define one or more metrics that can be observed (can
have defined likelihoods.) For instance, we could determine what rabbit population
growth rate would need to be for the fox population to oscillate somewhere between
20-120. Transpiling into PyMC and running the inference process is similar to the
normal model call, but with ``.pymc()``, specifying any free variables (at least
one will need to be defined as a prior probability distribution), observations
to target, and any sampling/pymc parameters:
```python
m.minimum_foxes = reno.Metric(reno.series_min(m.foxes))
m.maximum_foxes = reno.Metric(reno.series_max(m.foxes))
trace = m.pymc(
n=1000,
fox_growth_rate=reno.Normal(.001, .0001), # specify some variables as distributions to sample from
rabbit_growth_rate=reno.Normal(.1, .01), # specify some variables as distributions to sample from
observations=[
reno.Observation(m.minimum_foxes, 5, [20]), # likelihood normally distributed around 20 with SD of 5
reno.Observation(m.maximum_foxes, 5, [120]), # likelihood normally distributed around 120 with SD of 5
]
)
```
To see the shift in prior versus posterior distributions, we can plot the random
variables and some of the relevant stocks using ``plot_trace_refs``:
```python
reno.plot_trace_refs(
m,
{"prior": trace.prior, "post": trace.posterior},
ref_list=[m.minimum_foxes, m.maximum_foxes, m.fox_growth_rate, m.rabbit_growth_rate, m.foxes, m.rabbits],
figsize=(8, 5),
)
```

showing that the `rabbit_growth_rate` needs to be around `0.07` in order for
those observations to be met.
For a more in-depth introduction to reno, see the tub example in the `./notebooks` folder.
## Documentation
For the API reference as well as (eventually) the user guide, see
[https://ornl.github.io/reno/stable](https://ornl.github.io/reno/stable)
## Citation
To cite usage of Reno, please use the following bibtex:
```bibtex
@misc{doecode_166929,
title = {Reno},
author = {Martindale, Nathan and Stomps, Jordan and Phathanapirom, Urairisa B.},
abstractNote = {Reno is a tool for creating, visualizing, and analyzing system dynamics models in Python. It additionally has the ability to convert models to PyMC, allowing Bayesian inference on models with variables that include prior probability distributions.},
doi = {10.11578/dc.20251015.1},
url = {https://doi.org/10.11578/dc.20251015.1},
howpublished = {[Computer Software] \url{https://doi.org/10.11578/dc.20251015.1}},
year = {2025},
month = {oct}
}
```
Raw data
{
"_id": null,
"home_page": null,
"name": "reno-sd",
"maintainer": null,
"docs_url": null,
"requires_python": "<3.14,>=3.11",
"maintainer_email": null,
"keywords": "system dynamics, sdm, simulation, reno, pymc, bayesian",
"author": null,
"author_email": "\"Nathan Martindale, Jordan Stomps, Birdy Phathanapirom\" <martindalena@ornl.gov>",
"download_url": "https://files.pythonhosted.org/packages/44/05/dd8c7ce20ccd1e87d0a3fc8bcdb4affa1c87fcab86efd7b745f297ae9ead/reno_sd-0.2.0.tar.gz",
"platform": null,
"description": "# Reno System Dynamics (`reno-sd`)\n\n[](https://github.com/psf/black)\n[](https://badge.fury.io/py/reno-sd)\n[](https://anaconda.org/conda-forge/reno-sd)\n[](https://github.com/ORNL/reno/actions/workflows/tests.yml)\n[](https://github.com/ORNL/reno/blob/main/LICENSE)\n[](https://github.com/ORNL/reno)\n\n\nReno is a tool for creating, visualizing, and analyzing system dynamics\nmodels in Python. It additionally has the ability to convert models to PyMC,\nallowing Bayesian inference on models with variables that include prior probability\ndistributions.\n\nReno models are created by defining the equations for the various stocks, flows,\nand variables, and can then be simulated over time similar to something like\n[Insight Maker](https://insightmaker.com/), examples of which can be seen below\nand in the `notebooks` folder.\n\nCurrently, models only support discrete timesteps (technically implementing\ndifference equations rather than true differential equations.)\n\n\n## Installation\n\nInstall from PyPI via:\n```\npip install reno-sd\n```\n\nInstall from conda-forge with:\n```\nconda install reno-sd\n```\n\n\n## Example\n\nA classic system dynamics example is the predator-prey population model,\ndescribed by the [Lotka-Volterra equations](https://en.wikipedia.org/wiki/Lotka%E2%80%93Volterra_equations).\n\nImplementing these in Reno would look something like:\n\n```python\nimport reno\n\nm = reno.Model(name=\"m\", steps=200, doc=\"Classic predator-prey interaction model example\")\n\n# make stocks to monitor the predator/prey populations over time\nm.rabbits = reno.Stock(init=100.0)\nm.foxes = reno.Stock(init=100.0)\n\n# free variables that can quickly be changed to influence equilibrium\nm.rabbit_growth_rate = reno.Variable(.1, doc=\"Alpha\")\nm.rabbit_death_rate = reno.Variable(.001, doc=\"Beta\")\nm.fox_death_rate = reno.Variable(.1, doc=\"Gamma\")\nm.fox_growth_rate = reno.Variable(.001, doc=\"Delta\")\n\n# flows that define how the stocks are influenced\nm.rabbit_births = reno.Flow(m.rabbit_growth_rate * m.rabbits)\nm.rabbit_deaths = reno.Flow(m.rabbit_death_rate * m.rabbits * m.foxes, max=m.rabbits)\nm.fox_deaths = reno.Flow(m.fox_death_rate * m.foxes, max=m.foxes)\nm.fox_births = reno.Flow(m.fox_growth_rate * m.rabbits * m.foxes)\n\n# hook up inflows/outflows for stocks\nm.rabbits += m.rabbit_births\nm.rabbits -= m.rabbit_deaths\n\nm.foxes += m.fox_births\nm.foxes -= m.fox_deaths\n```\n\nThe stock and flow diagram for this model (obtainable via `m.graph()`) looks\nlike this: (green boxes are variables, white boxes are stocks, the labels between\narrows are the flows)\n\n\n\nOnce a model is defined it can be called like a function, optionally configuring\nany free variables/initial values by passing them as arguments. You can print the\noutput of `m.get_docs()` to see a docstring showing all possible arguments and\nwhat calling it should look like:\n\n```\n>>> print(m.get_docs())\nClassic predator-prey interaction model example\n\nExample:\n\tm(rabbit_growth_rate=0.1, rabbit_death_rate=0.001, fox_death_rate=0.1, fox_growth_rate=0.001, rabbits_0=100.0, foxes_0=100.0)\n\nArgs:\n\trabbit_growth_rate: Alpha\n\trabbit_death_rate: Beta\n\tfox_death_rate: Gamma\n\tfox_growth_rate: Delta\n\trabbits_0\n\tfoxes_0\n```\n\nTo run and plot the population stocks:\n\n```python\nm(fox_growth_rate=.002, rabbit_death_rate=.002, rabbits_0=120.0)\nreno.plot_refs([(m.rabbits, m.foxes)])\n```\n\n\n\nTo use Bayesian inference, we define one or more metrics that can be observed (can\nhave defined likelihoods.) For instance, we could determine what rabbit population\ngrowth rate would need to be for the fox population to oscillate somewhere between\n20-120. Transpiling into PyMC and running the inference process is similar to the\nnormal model call, but with ``.pymc()``, specifying any free variables (at least\none will need to be defined as a prior probability distribution), observations\nto target, and any sampling/pymc parameters:\n\n```python\nm.minimum_foxes = reno.Metric(reno.series_min(m.foxes))\nm.maximum_foxes = reno.Metric(reno.series_max(m.foxes))\n\ntrace = m.pymc(\n n=1000,\n fox_growth_rate=reno.Normal(.001, .0001), # specify some variables as distributions to sample from\n rabbit_growth_rate=reno.Normal(.1, .01), # specify some variables as distributions to sample from\n observations=[\n reno.Observation(m.minimum_foxes, 5, [20]), # likelihood normally distributed around 20 with SD of 5\n reno.Observation(m.maximum_foxes, 5, [120]), # likelihood normally distributed around 120 with SD of 5\n ]\n)\n```\n\nTo see the shift in prior versus posterior distributions, we can plot the random\nvariables and some of the relevant stocks using ``plot_trace_refs``:\n\n```python\nreno.plot_trace_refs(\n m,\n {\"prior\": trace.prior, \"post\": trace.posterior},\n ref_list=[m.minimum_foxes, m.maximum_foxes, m.fox_growth_rate, m.rabbit_growth_rate, m.foxes, m.rabbits],\n figsize=(8, 5),\n)\n```\n\n\n\nshowing that the `rabbit_growth_rate` needs to be around `0.07` in order for\nthose observations to be met.\n\nFor a more in-depth introduction to reno, see the tub example in the `./notebooks` folder.\n\n\n## Documentation\n\nFor the API reference as well as (eventually) the user guide, see\n[https://ornl.github.io/reno/stable](https://ornl.github.io/reno/stable)\n\n\n## Citation\n\nTo cite usage of Reno, please use the following bibtex:\n\n\n```bibtex\n@misc{doecode_166929,\n title = {Reno},\n author = {Martindale, Nathan and Stomps, Jordan and Phathanapirom, Urairisa B.},\n abstractNote = {Reno is a tool for creating, visualizing, and analyzing system dynamics models in Python. It additionally has the ability to convert models to PyMC, allowing Bayesian inference on models with variables that include prior probability distributions.},\n doi = {10.11578/dc.20251015.1},\n url = {https://doi.org/10.11578/dc.20251015.1},\n howpublished = {[Computer Software] \\url{https://doi.org/10.11578/dc.20251015.1}},\n year = {2025},\n month = {oct}\n}\n```\n",
"bugtrack_url": null,
"license": null,
"summary": "System dynamics modeling with bayesian inference",
"version": "0.2.0",
"project_urls": {
"Changelog": "https://github.com/ORNL/reno/blob/main/CHANGELOG.md",
"Documentation": "https://ornl.github.io/reno/stable",
"Issues": "https://github.com/ORNL/reno/issues",
"Repository": "https://github.com/ORNL/reno"
},
"split_keywords": [
"system dynamics",
" sdm",
" simulation",
" reno",
" pymc",
" bayesian"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "98c967a1edf40037e9fa530111ce418c4475ce52194ed8e8a13b9da833efa079",
"md5": "887ab57cacc56cddc268872e6d391d85",
"sha256": "52c3d780aad3a32c00066bd156835d278fec4f853f97bdb8bcfc34d93e5f843d"
},
"downloads": -1,
"filename": "reno_sd-0.2.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "887ab57cacc56cddc268872e6d391d85",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<3.14,>=3.11",
"size": 102711,
"upload_time": "2025-10-24T15:53:08",
"upload_time_iso_8601": "2025-10-24T15:53:08.772918Z",
"url": "https://files.pythonhosted.org/packages/98/c9/67a1edf40037e9fa530111ce418c4475ce52194ed8e8a13b9da833efa079/reno_sd-0.2.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "4405dd8c7ce20ccd1e87d0a3fc8bcdb4affa1c87fcab86efd7b745f297ae9ead",
"md5": "2e96704278cca961e92961d568cd6342",
"sha256": "3438306a3a617c314c0282bf8dcb9c79d5d51a9ca23e69c4b44adcb7da435da2"
},
"downloads": -1,
"filename": "reno_sd-0.2.0.tar.gz",
"has_sig": false,
"md5_digest": "2e96704278cca961e92961d568cd6342",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<3.14,>=3.11",
"size": 114900,
"upload_time": "2025-10-24T15:53:10",
"upload_time_iso_8601": "2025-10-24T15:53:10.529962Z",
"url": "https://files.pythonhosted.org/packages/44/05/dd8c7ce20ccd1e87d0a3fc8bcdb4affa1c87fcab86efd7b745f297ae9ead/reno_sd-0.2.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-24 15:53:10",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "ORNL",
"github_project": "reno",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "build",
"specs": []
},
{
"name": "twine",
"specs": []
},
{
"name": "pytest",
"specs": []
},
{
"name": "sphinx",
"specs": []
},
{
"name": "pydata-sphinx-theme",
"specs": []
}
],
"lcname": "reno-sd"
}