reno.model.Model#

class reno.model.Model(name=None, n=1, steps=10, label=None, doc=None)#

Bases: object

Class for a distinct, simulatable set of related stocks and flows.

The expectation is to create a model instance, and then assign stocks, flows, variables, and submodels as attributes on that instance.

Parameters:
  • name (str) – Optional name to give the model, should be used when submodels are in play, as the model name is used to help visually distinguish which model things belong to.

  • n (int) – The number of samples to simulate at once (by default).

  • steps (int) – How many time steps to run the simulation for (by default).

  • label (str) – Optional visual label to use when printing model things if cleaner than using the name.

  • doc (str) – Optional docstring to explain/describe the model.

Example

>>> import reno
>>> tub = reno.Model("Tub model")
>>> tub.water_level = reno.Stock()
>>> tub.faucet = reno.Flow(5)
>>> tub.drain = reno.Flow(tub.water_level / 2)
>>> tub.water_level += tub.faucet
>>> tub.water_level -= tub.drain
>>> tub(steps=50)

Methods

__init__([name, n, steps, label, doc])

add(name, value)

Add the passed tracked reference to the model with the provided name. This is used for programmatically adding stocks/flows in a context where the name is dynamically created and you can't simply ``model.my_name = ``.

all_flows()

Get all flows from this and all submodels.

all_metrics()

Get all metrics recursively

all_refs()

Get all stocks, flows, and vars, from this and all submodels in one giant ordered list (does not include metrics, use all_metrics() separately.)

all_stocks()

Get all stocks from this and all submodels.

all_vars()

Get all vars from this and all submodels.

config(**free_refs)

Get/set model configuration.

copy([name])

Make a separate copy of this model with the desired name.

dataset()

Turn all of the model's tracked reference values into an xarray dataset, including the "configuration" of the input parameters etc.

dependency_compute_order([inits_order, debug])

Find a dependency-safe ordering for reference equations by iterating through and each time adding the first reference that doesn't depend on any references not yet added.

find_timeref_name()

If a TimeRef is ever used in an equation, get its reference name.

free_refs([recursive])

Get all free "variables" (not component variables) for this model, or things that aren't defined in terms of anything else.

from_dict(data)

Deserialize a previously saved model definition dictionary, returns new Model instance.

get_docs([as_dict])

Get a full docstring for using this model as a function, maybe useful for allowing a model to be used as a tool for LLMs?

get_nonrecursive_config()

Only get the free refs config from _this_ model, no submodels.

graph([show_vars, exclude_vars, sparklines, ...])

Generate a graphviz dot graph for all the stocks and flows of the passed model, optionally including sparklines if a simulation has been run.

latex([docs, t, sample])

Get an interactive latex ipywidget listing all of the equations in system.

load(path)

Initalize model from model definition file (JSON).

load_dataset(ds)

Take all the tracked ref sequence data and load them into the matching model's tracked refs stored values.

plot_stocks([cols, rows])

Shortcut function to quickly get a set of graphs for each stock.

pymc([n, steps, sampling_kwargs, ...])

A PyMC equivalent version of a model's __call__, convert the model to PyMC and run the simulation/Bayesian analysis.

pymc_model([observations, steps])

Generate a pymc model for bayesian analysis of this system dynamics model.

pymc_str([observations, steps])

Construct a string of python code to create a pymc model wrapping this system dynamics model.

run_metrics([n, steps])

Run all metric equations on a completed simulation.

save(path)

Save model definition at specified location.

simulate([n, steps, quiet, debug])

Run each step of the the full simulation.

simulator([n, steps, quiet, debug])

An iterator to use for running the simulation step by step.

to_dict([root])

Convert the model into a JSON-serializable dictionary.

Attributes

stocks

List of all stocks associated with this model - don't modify directly, assign stocks as attributes directly on model (e.g. model.my_stock = reno.Stock()).

flows

List of all flows associated with this model - don't modify directly, assign flows as attributes directly on model (e.g. model.my_flow = reno.Flow()).

vars

List of all variables associated with this model - don't modify directly, assign vars as attributes directly on model (e.g. model.my_var = reno.Variable()).

metrics

List of all metrics associated with this model - don't modify directly, assign metrics as attributes directly on model (e.g. model.my_metric = reno.Metric()).

models

List of submodels of this model - don't modify directly, assign submodels as attributes directly on model (e.g. model.my_submodel = reno.Model().

parent

Parent model if applicable.

label

String label to use in any visualizations/outputs to refer to this model.

steps

The number of timesteps to use by default in this model's simulations, can be overriden per model call.

n

The number of simulations to run in parallel by default, can be overriden per model call.

doc

Docstring/comment to describe this model.

trace

Arviz trace produced by the last pymc run.

trace_RVs

The set of string names of any random variables from the last pymc run.

__annotations__ = {}#
__call__(n=None, steps=None, keep_config=False, **free_refs)#

Run the model simulation, allowing specification of any free variables. Variables in submodels need to be defined as dictionaries.

Example

>>> dataset = my_model(steps=20, some_free_var=reno.Normal(5, 2), my_submodel=dict(other_free_var=4))
Parameters:
  • n (int) – Number of simulations to run in parallel, leave None to use the default set on the model.

  • steps (int) – Number of timesteps to run the simulation for, leave None to use the default set on the model.

  • keep_config (bool) – Whether to keep any changes made via free ref configurations passed in for subsequent simulations. The default is to not do this, keeping the original model unchanged.

  • **free_refs – Definitions for equations or values for any variables or initial conditions in the system.

Return type:

Dataset

__dict__ = mappingproxy({'__module__': 'reno.model', '__doc__': 'Class for a distinct, simulatable set of related stocks and flows.\n\n    The expectation is to create a model instance, and then assign stocks,\n    flows, variables, and submodels as attributes on that instance.\n\n    Args:\n        name (str): Optional name to give the model, should be used when\n            submodels are in play, as the model name is used to help\n            visually distinguish which model things belong to.\n        n (int): The number of samples to simulate at once (by default).\n        steps (int): How many time steps to run the simulation for (by default).\n        label (str): Optional visual label to use when printing model things if\n            cleaner than using the name.\n        doc (str): Optional docstring to explain/describe the model.\n\n    Example:\n        >>> import reno\n\n        >>> tub = reno.Model("Tub model")\n        >>> tub.water_level = reno.Stock()\n        >>> tub.faucet = reno.Flow(5)\n        >>> tub.drain = reno.Flow(tub.water_level / 2)\n\n        >>> tub.water_level += tub.faucet\n        >>> tub.water_level -= tub.drain\n\n        >>> tub(steps=50)\n    ', '__init__': <function Model.__init__>, '_is_init_ref': <function Model._is_init_ref>, '__setattr__': <function Model.__setattr__>, '__getattr__': <function Model.__getattr__>, 'add': <function Model.add>, 'all_stocks': <function Model.all_stocks>, 'all_flows': <function Model.all_flows>, 'all_vars': <function Model.all_vars>, 'all_refs': <function Model.all_refs>, 'all_metrics': <function Model.all_metrics>, '_reset_type_and_shape_info': <function Model._reset_type_and_shape_info>, '_recursive_sub_populate_n_steps': <function Model._recursive_sub_populate_n_steps>, '_populate': <function Model._populate>, '_find_all_extended_op_implicit_components': <function Model._find_all_extended_op_implicit_components>, 'simulator': <function Model.simulator>, 'simulate': <function Model.simulate>, 'run_metrics': <function Model.run_metrics>, 'graph': <function Model.graph>, 'latex': <function Model.latex>, 'plot_stocks': <function Model.plot_stocks>, 'copy': <function Model.copy>, 'free_refs': <function Model.free_refs>, 'get_docs': <function Model.get_docs>, 'config': <function Model.config>, 'get_nonrecursive_config': <function Model.get_nonrecursive_config>, 'load_dataset': <function Model.load_dataset>, 'dataset': <function Model.dataset>, '__call__': <function Model.__call__>, 'find_timeref_name': <function Model.find_timeref_name>, 'dependency_compute_order': <function Model.dependency_compute_order>, 'pymc_model': <function Model.pymc_model>, 'pymc_str': <function Model.pymc_str>, 'pymc': <function Model.pymc>, 'to_dict': <function Model.to_dict>, '_add_skeleton': <function Model._add_skeleton>, '_load_refs': <function Model._load_refs>, 'from_dict': <staticmethod(<function Model.from_dict>)>, 'save': <function Model.save>, 'load': <staticmethod(<function Model.load>)>, '__dict__': <attribute '__dict__' of 'Model' objects>, '__weakref__': <attribute '__weakref__' of 'Model' objects>, '__annotations__': {'stocks': 'list[reno.Stock]', 'flows': 'list[reno.Flow]', 'vars': 'list[reno.Variable]', 'metrics': 'list[reno.components.Metric]', 'models': 'list[reno.Model]', 'parent': 'reno.Model', 'name': 'str', 'label': 'str', 'steps': 'int', 'n': 'int', 'last_n': 'int', 'last_steps': 'int', 'doc': 'str', 'trace_RVs': 'list[str]'}})#
__getattr__(name)#
__module__ = 'reno.model'#
__setattr__(name, value)#

Implement setattr(self, name, value).

__weakref__#

list of weak references to the object

add(name, value)#

Add the passed tracked reference to the model with the provided name. This is used for programmatically adding stocks/flows in a context where the name is dynamically created and you can’t simply ``model.my_name = ``.

Parameters:
all_flows()#

Get all flows from this and all submodels.

Return type:

list

all_metrics()#

Get all metrics recursively

Return type:

list

all_refs()#

Get all stocks, flows, and vars, from this and all submodels in one giant ordered list (does not include metrics, use all_metrics() separately.)

Return type:

list

all_stocks()#

Get all stocks from this and all submodels.

Return type:

list

all_vars()#

Get all vars from this and all submodels.

Return type:

list

config(**free_refs)#

Get/set model configuration. This function allows specifying one or more free variables - anything not set uses the default.

Returns:

The resulting configuration dictionary, with the free ref names as keys.

Return type:

dict

copy(name=None)#

Make a separate copy of this model with the desired name.

This is useful for making a separate model instance that you can modify the equations of for comparison against the original.

Parameters:

name (str)

Return type:

Model

dataset()#

Turn all of the model’s tracked reference values into an xarray dataset, including the “configuration” of the input parameters etc.

Return type:

Dataset

dependency_compute_order(inits_order=False, debug=False)#

Find a dependency-safe ordering for reference equations by iterating through and each time adding the first reference that doesn’t depend on any references not yet added.

This function will detect circular references (equations that depend on eachother) and throw an error. Primary use for this function is to correctly set order of equations in the pymc model/step function, but this is used in normal Reno math too.

Parameters:
  • inits_order (bool) – Include stock init equations in the ordering. (When False, stocks will always be listed first since they always depend on a previous timestep’s values. This is not the case for inital equations.)

  • debug (bool)

Returns:

The dependency-ordered list of reno TrackedReferences for this model.

Return type:

list[TrackedReference]

doc: str#

Docstring/comment to describe this model.

find_timeref_name()#

If a TimeRef is ever used in an equation, get its reference name. This is necessary for the pymc code construction to ensure the correct variable name is injected.

Returns None if no TimeRef is used.

Return type:

str

flows: list[Flow]#

List of all flows associated with this model - don’t modify directly, assign flows as attributes directly on model (e.g. model.my_flow = reno.Flow())

free_refs(recursive=False)#

Get all free “variables” (not component variables) for this model, or things that aren’t defined in terms of anything else.

A free variable is a variable with no references to other variables, stocks or flows.

Parameters:

recursive (bool)

static from_dict(data)#

Deserialize a previously saved model definition dictionary, returns new Model instance.

Parameters:

data (dict)

Return type:

Model

get_docs(as_dict=False)#

Get a full docstring for using this model as a function, maybe useful for allowing a model to be used as a tool for LLMs?

Use as_dict for submodels, so example string shows args as dictionary.

Parameters:

as_dict (bool)

Return type:

str

get_nonrecursive_config()#

Only get the free refs config from _this_ model, no submodels.

Useful for assigning dataset attrs.

graph(show_vars=True, exclude_vars=None, sparklines=False, sparkdensities=False, sparkall=False, g=None, traces=None, universe=None, lr=False)#

Generate a graphviz dot graph for all the stocks and flows of the passed model, optionally including sparklines if a simulation has been run.

Parameters:
  • model (reno.model.Model) – The model to collect stocks/flows/variables from.

  • show_vars (bool) – Whether to render variables in the diagram, or just stocks and flows (for very complex models, hiding variables can make it a bit easier to visually parse.)

  • exclude_var_names (list[str]) – Specific variables to hide in the diagram, can be used with show_vars=True to just show specific variables of interest.

  • sparklines (bool) – Draw mini graphs to the right of each stock showing plotting their values through time. This assumes the model has either been run, or traces are passed in manually with the traces argument.

  • sparkdensities (bool) – Draw mini density plots/histograms next to any variables that sample from distributions. This assumes the model has either been run, or traces are passed in manually with the traces argument.

  • g (Digraph) – Graphviz Digraph instance to render the nodes/edges on. Mostly only for internal use for drawing subgraphs for submodels.

  • traces (list[xr.Dataset]) – A list of traces or model run datasets to use for drawing spark plots. Each dataset will be rendered in a different color.

  • universe (list[TrackedReference]) – Limit rendered nodes to only those listed here, this includes all of stocks/flows/variables. (This acts as an initial filter, show_vars and exclude_var_names still applies after this.)

  • lr (bool) – By default the graphviz plot tries to orient top-down. Specify True to try to orient it left-right.

  • exclude_vars (list[str])

  • sparkall (bool)

Returns:

The populated Digraph instance (Jupyter can natively render this in a cell output.)

Return type:

Digraph

label: str#

String label to use in any visualizations/outputs to refer to this model. defaults to name if not used.

latex(docs=True, t=None, sample=0)#

Get an interactive latex ipywidget listing all of the equations in system. Each equation line is clickable, clicking will highlight where else in the system that equation’s result is being used.

Parameters:
  • docs (bool) – Whether to include the documentation string beneath each equation or not.

  • t (int) – If specified, show the values of every reference at the specified timestep (note that stock values will be from the previous timestep)

  • sample (int) – Which sample (row) to show the values from if t was specified.

static load(path)#

Initalize model from model definition file (JSON).

Parameters:

path (str)

Return type:

Model

load_dataset(ds)#

Take all the tracked ref sequence data and load them into the matching model’s tracked refs stored values. This is useful for using .latex(t=...) in debug mode for diving into a specific run.

Parameters:

ds (Dataset)

metrics: list[Metric]#

List of all metrics associated with this model - don’t modify directly, assign metrics as attributes directly on model (e.g. model.my_metric = reno.Metric())

models: list[Model]#

List of submodels of this model - don’t modify directly, assign submodels as attributes directly on model (e.g. model.my_submodel = reno.Model()

n: int#

The number of simulations to run in parallel by default, can be overriden per model call.

parent: Model#

Parent model if applicable. This gets set in the __setattr__ when a submodel is assigned.

plot_stocks(cols=None, rows=None, **figargs)#

Shortcut function to quickly get a set of graphs for each stock.

Parameters:
  • cols (int)

  • rows (int)

pymc(n=None, steps=None, sampling_kwargs=None, compile_kwargs=None, compile_faster=False, observations=None, smc=True, trace_prior=None, compute_prior_only=False, keep_config=False, **free_refs)#

A PyMC equivalent version of a model’s __call__, convert the model to PyMC and run the simulation/Bayesian analysis.

Parameters:
  • n (int) – Number of simulations to run in parallel, leave None to use the default set on the model.

  • steps (int) – Number of timesteps to run the simulation for, leave None to use the default set on the model.

  • sampling_kwargs (dict) – Arguments to pass to the PyMC sampler. Uses sample_smc by default unless the smc argument is False, in which case it lets PyMC choose sampler (NUTS for continuous variables, some variant of Metropolis for discrete.)

  • compile_kwargs (dict) – Arguments to pass along to the compiler through PyMC. E.g. mode, which, if function compilation is taking forever, can for instance be set to FAST_COMPILE.

  • compile_faster (bool) – For some large/complex models, the PyMC/pytensor compilation step can take excessively long as it tries to apply a high level of optimizations. Set this to True to bump down the optimization level by one, which should dramatically speed this step up. Alternatively you can have more granular control over this by passing a mode key to the compile_kwargs dictionary parameter, see pytensor’s documentation for more details: https://pytensor.readthedocs.io/en/latest/tutorial/modes.html

  • observations (list[reno.Observation]) – Observed values (data/evidence) to use for computing posteriors, at least one should be specified if not exclusively running priors.

  • smc (bool) – Whether to use the sequential monte carlo sampler or not, the default is to do so - the regular samplers in PyMC tend not to do well if posterior distributions might have multiple peaks, see: https://www.pymc.io/projects/examples/en/latest/samplers/SMC2_gaussians.html

  • [az.InferenceData] (trace_prior) – If priors for this model have already been run, pass in that arviz inference object here, and the prior xarray dataset will be used in the output arviz object from this pymc run.

  • compute_prior_only (bool) – If set to True, don’t run the Bayesian inference, only run the priors (“forward simulation mode”).

  • keep_config (bool) – Whether to keep any changes made via free ref configurations passed in for subsequent simulations. The default is to not do this, keeping the original model unchanged.

  • **free_refs – Definitions for equations or values for any variables or initial conditions in the system.

  • trace_prior (InferenceData)

Return type:

InferenceData

pymc_model(observations=None, steps=None)#

Generate a pymc model for bayesian analysis of this system dynamics model. The general idea is that this creates corresponding pymc variables (or distributions as relevant) for each stock/flow/var in the model, and sets up the full simulation sequence computations based on the generated step function from _pt_step().

Sampling with priors should be equivalent to running the system dynamics model normally (this is essentially “forward simulation mode”.) Add observations to the pymc model variables and sample from posterior predictive to run bayesian analysis/determine how distributions of any other variables may be affected.

Parameters:
Return type:

Model

pymc_str(observations=None, steps=None)#

Construct a string of python code to create a pymc model wrapping this system dynamics model. Should be a functional (string) equivalent of the pymc_model() function. Includes the output from pymc.pt_sim_step_str(self).

Expected imports for the resulting code to run:
>>> import pytensor
>>> import pytensor.tensor as pt
>>> from pytensor.ifelse import ifelse
>>> import pymc as pm
>>> import numpy as np
Parameters:
Return type:

str

run_metrics(n=None, steps=None)#

Run all metric equations on a completed simulation. Calling this function assumes the full simulation has already run.

Parameters:
  • n (int)

  • steps (int)

save(path)#

Save model definition at specified location. Stores as a JSON using the to_dict() method

Parameters:

path (str)

simulate(n=None, steps=None, quiet=False, debug=False)#

Run each step of the the full simulation. Leaving n and/or steps None will use the model’s default (as defined in constructor.)

Parameters:
  • n (int)

  • steps (int)

  • quiet (bool)

  • debug (bool)

simulator(n=None, steps=None, quiet=False, debug=False)#

An iterator to use for running the simulation step by step. Leaving n and/or steps None will use the model’s default (as defined in constructor.)

Parameters:
  • n (int)

  • steps (int)

  • quiet (bool)

  • debug (bool)

steps: int#

The number of timesteps to use by default in this model’s simulations, can be overriden per model call.

stocks: list[Stock]#

List of all stocks associated with this model - don’t modify directly, assign stocks as attributes directly on model (e.g. model.my_stock = reno.Stock())

to_dict(root=True)#

Convert the model into a JSON-serializable dictionary.

Parameters:

root (bool)

Return type:

dict

trace#

Arviz trace produced by the last pymc run. This is used for plotting defaults on functions that aren’t explicitly passed traces.

trace_RVs: list[str]#

The set of string names of any random variables from the last pymc run.

vars: list[Variable]#

List of all variables associated with this model - don’t modify directly, assign vars as attributes directly on model (e.g. model.my_var = reno.Variable())