atomica.model

Implements the Atomica computational graph

Fundamentally, models in Atomica can be represented as a graph, with nodes corresponding to compartments, and edges corresponding to transitions/links. This module implements the graph representation of the Framework in a form that can be numerically integrated. It also implements the methods to actually perform the integration.

Functions

run_model(settings, framework, parset[, …])

Processes the TB epidemiological model.

Classes

Characteristic(pop, name)

A characteristic represents a grouping of compartments.

Compartment(pop, name)

A class to wrap up data for one compartment within a cascade network.

Link(pop, parameter, source, dest[, tag])

A Link is a Variable that maps to a transition between compartments.

Model(settings, framework, parset[, …])

A class to wrap up multiple populations within model and handle cross-population transitions.

Parameter(pop, name)

Integration object to represent Parameters

Population(framework, name, label, progset, …)

A class to wrap up data for one population within model.

TimedCompartment(pop, name, duration, …)

Variable(pop, id)

Integration object to manage compartments, characteristics, parameters, and links

Exceptions

BadInitialization

Error for invalid conditions

exception atomica.model.BadInitialization[source]

Error for invalid conditions

This error gets raised if the simulation exited due to a bad initialization, specifically due to negative initial popsizes or an excessive residual. This commonly happens if the initial conditions are being programatically varied and thus may be an expected error. This error can then be caught and dealt with appropriately. For example:

  • calibration will catch this error and instruct ASD to reject the proposed parameters

  • Ensemble.run_sims catches this error and tries another sample

class atomica.model.Characteristic(pop, name)[source]

A characteristic represents a grouping of compartments.

add_denom(x)[source]
add_include(x)[source]
get_included_comps()[source]
preallocate(tvec, dt)[source]

Preallocate data storage

This method gets called just before integration, once the final sizes of the arrays are known. Performance is improved by preallocating the arrays. The tvec attribute is assigned as-is, so it is typically a reference to the array stored in the parent Model object. Thus, there is no duplication of storage there, and Variable.tvec is mainly for convenience when interpolating or plotting.

This method may be overloaded in derived classes to preallocate other variables specific to those classes.

Parameters
  • tvec – An array of time values

  • dt – Time step size

set_dynamic(**kwargs)[source]

Make the variable a dependency

For Compartments and Links, this does nothing. For Characteristics and Parameters, it will set the dynamic flag, but in addition, any validation constraints e.g. a Parameter that depends on Links cannot itself be dynamic, will be enforced.

This method generally must be overloaded in derived classes e.g. Parameter.set_dynamic()

update(ti)[source]

Update the value at a given time index

This method performs any required computations to update the value of the variable at a given time index. For example, Parameters may require their update function to be called, while characteristics need their source compartments to be added up.

This method generally must be overloaded in derived classes e.g. Parameter.update()

Parameters

ti – Time index to update

property vals
class atomica.model.Compartment(pop, name)[source]

A class to wrap up data for one compartment within a cascade network.

expected_duration(ti=None)[source]
expected_outflow(ti)[source]
property outflow
resolve_outflows(ti)[source]

Resolve outgoing links

For the base class, rescale outgoing links so that the compartment won’t go negative

Parameters

ti (int) – Time index at which to update link outflows

Return type

None

update(ti)[source]

Update compartment value

A compartment update passes in the time to assign (ti). We have

x(ti) = x(ti-1) + inflow(ti-1) - outflow(ti-1)

Thus, need to initialize the value and then resolve all inflows and outflows Junctions have already been flushed and thus do not need to be updated in this step

Parameters

ti (int) –

Return type

None

Returns

A Link is a Variable that maps to a transition between compartments. As such, it contains an inflow and outflow compartment. A Link’s value is drawn from a Parameter, and there may be multiple links that draw values from the same parameter. The values stored in this extended version of Variable refer to flow rates. If used in ModelPop, the Link references two cascade compartments within a single population.

plot()[source]

Produce a time series plot

This is a quick function to make a basic line plot of this Variable. Mainly intended for debugging. Production-ready plots should be generated using the plotting library functions instead

class atomica.model.Model(settings, framework, parset, progset=None, program_instructions=None)[source]

A class to wrap up multiple populations within model and handle cross-population transitions.

_set_vars_by_pop()[source]

Update cache dicts and lists

During integration, the model needs to iterate over parameters with the same name across populations. Therefore, we build a cache dict where we map code names to a list of references to the required Parameter objects. We also construct a cache list of the parameter names from the framework so we can quickly iterate over it.

Return type

None

build(settings, parset)[source]

Build the full model.

get_pop(pop_name)[source]

Allow model populations to be retrieved by name rather than index.

process()[source]

Run the full model.

update_comps()[source]

Set the compartment values at self._t_index+1 based on the current values at self._t_index and the link values at self._t_index. Values are updated by iterating over all outgoing links

update_junctions(initial_flush=False)[source]

For every compartment considered a junction, propagate the contents onwards. Do so until all junctions are empty.

Evolve model characteristics by one timestep (defaulting as 1 year). Each application of this method writes calculated values to the next position in popsize arrays. This is done regardless of dt. Thus the corresponding time vector associated with variable dt steps must be tracked externally. This function computes the link flow rates.

update_pars()[source]

Run through all parameters and characteristics flagged as dependencies for custom-function parameters. Evaluate them for the current timestep. These dependencies must be calculated in the same order as defined in settings. This also means characteristics before parameters, otherwise references may break. Also, parameters in dependency list do not need calculation unless explicitly depending on another parameter. Parameters that have special rules are usually dependent on other population values, so are included here.

update_program_cache()[source]
class atomica.model.Parameter(pop, name)[source]

Integration object to represent Parameters

A parameter is a Variable that can have a value computed via an fcn_str and a list of dependent Variables. This is a Parameter in the cascade.xlsx sense - there is one Parameter object for every item in the Parameters sheet. A parameter that maps to multiple transitions (e.g. doth_rate) will have one parameter and multiple Link instances that depend on the same Parameter instance

Parameters
  • pop – A Population instance corresponding to the population that will contain this parameter

  • name – The code name for this parameter

_dx = None

Internal cache for the value of the derivative at the current timestep

_fcn = None

Internal cache for parsed parameter function (this will be dropped when pickled)

_is_dynamic = None

If True, this parameter will be updated during integration. Note that precompute and dynamic are mutually exclusive

_precompute = None

If True, the parameter function will be computed in a vector operation prior to integration

_source_popsize_cache_time = None

Internal cache for the time at which the source popsize was previously computed

_source_popsize_cache_val = None

Internal cache for the last previously computed source popsize

constrain(ti=None)[source]

Constrain the parameter value to allowed range

If Parameter.limits is not None, then the parameter values at the specified time will be clipped to the limits. If no time index is provided, clipping will be performed on all values in a vectorized operation. Vector clipping is used for data parameters that are not evaluated during integration, while index-specific clipping is used for dependencies that are calculated during integration.

Parameters

ti – An integer index, or None (to operate on all time points)

Return type

None

deps = None

Dict of dependencies containing lists of integration objects

derivative = None

If True, the parameter function will be treated as a derivative and the value added on to the end

fcn_str = None

String representation of parameter function

limits = None

Can be a two element vector [min,max]

References to links that derive from this parameter

pop_aggregation = None

If not None, stores list of population aggregation information (special function, which weighting comp/charac and which interaction term to use)

scale_factor = None

This should be set to the product of the population-specific y_factor and the meta_y_factor from the ParameterSet

set_dynamic(progset=None)[source]

Mark this parameter as needing evaluation for integration

If a parameter has a function and set_dynamic() has been called, that means the result of the function is required to drive a transition in the model. In this context, ‘dynamic’ means that the parameter depends on values that are only known during integration (i.e. compartment sizes). It could also depend on compartment sizes indirectly, if it depends on characteristics, or if it depends on parameters that depend on compartments or characteristics. If none of these are true, then the parameter function can be evaluated before integration starts, taking advantage of vector operations.

If a parameter is overwritten by a program in this simulation, then its value may change during integration, and we again need to make this parameter dynamic. Note that this doesn’t apply to the parameter being overwritten - if it gets overwritten, that has no bearing on when its function gets evaluated. But if a parameter depends on a value that has changed, then the function must be re-evaluated. Technically this only needs to be done after the time index where programs turn on, but it’s simpler just to mark it as dynamic for the entire simulation, the gains aren’t large enough to justify the extra complexity otherwise.

Parameters

progset – A ProgramSet instance (the one contained in the Model containing this Parameter)

Return type

None

set_fcn(framework, fcn_str, progset)[source]

Add a function to this parameter

This method adds a function to the parameter. The following steps are carried out

  • The function is parsed and stored in the ._fcn attribute (until the Parameter is pickled)

  • The dependencies are extracted, and are stored as references to the actual integration objects to increase performance during integration

  • If this is a transition parameter, then dependencies will have their dynamic flag updated

Parameters
  • framework – A py:class:ProjectFramework instance, used to identify and retrieve interaction terms

  • fcn_str – The string containing the function to add

  • progset – A py:class:ProgramSet instance, used to identify parameters that will be overwritten

Return type

None

skip_function = None

Can optionally be set to a (start,stop) tuple of times. Between these times, the parameter will not be updated so the parset value will be left unchanged

source_popsize(ti)[source]
timescale = None

For transition parameters, the vals stored by the parameter is effectively a rate. The timescale attribute informs the time period corresponding to the units in which the rate has been provided. If the units are number or probability then the timescale corresponds to the denominator of the units e.g. probability/day (with timescale=1/365). If the units are duration then the timescale stores the units in which the duration has been specified e.g. duration=1 with timescale=1/52 is the same as duration=7 with timescale=1/365 The effective duration used in the simulation is duration*timescale with units of years (so it will behave correctly if one wanted to use 1/365.25 instead of 1/365)

update(ti=None)[source]

Update the value of this Parameter

If the parameter contains a function, this entails evaluating the function. Typically this is done at a single time point during integration, or over all time points for precomputing and postcomputing

Parameters

ti – An int, or a numpy array with index values. If None, all time values will be used

Return type

None

class atomica.model.Population(framework, name, label, progset, pop_type)[source]

A class to wrap up data for one population within model. Each model population must contain a set of compartments with equivalent names.

charac_lookup = None

Maps name of a compartment to a Characteristic

characs = None

List of Characteristic objects

comp_lookup = None

Maps name of a compartment to a Compartment

comps = None

List of Compartment objects

gen_cascade(framework, progset)[source]

Generate a compartmental cascade as defined in a settings object. Fill out the compartment, transition and dependency lists within the model population object. Maintaining order as defined in a cascade workbook is crucial due to cross-referencing.

get_charac(charac_name)[source]

Allow dependencies to be retrieved by name rather than index. Returns a Variable.

get_comp(comp_name)[source]

Allow compartments to be retrieved by name rather than index. Returns a Compartment.

Retrieve Links.

Return type

list

get_par(par_name)[source]

Allow dependencies to be retrieved by name rather than index. Returns a Variable.

get_variable(name)[source]

Return list of variables given code name

At the moment, names are unique across object types and within object types except for links, but if that logic changes, simple modifications can be made here

Link names in Atomica end in ‘par_name:flow’ so passing in a code name of this form will return links. Links can also be identified by the compartments that they connect. Allowed syntax is: - ‘source_name:’ - All links going out from source - ‘:dest_name’ - All links going into destination - ‘source_name:dest_name’ - All links from Source to Dest - ‘source_name:dest_name:par_name’ - All links from Source to Dest belonging to a given Parameter - ‘:dest:par_name’ - ‘source::par_name’ - As per above - ‘::par_name’ - All links with specified par_name (less efficient than ‘par_name:flow’)

Parameters

name (str) – Code name to search for

Return type

list

Returns

A list of Variables

initialize_compartments(parset, framework, t_init)[source]
label = None

The full name/label of the population

Maps name of link to a list of Links with that name

List of Link objects

name = None

The code name of the population

par_lookup = None

Maps name of a parameter to a Parameter

pars = None

List of Parameter objects

popsize(ti=None)[source]
preallocate(tvec, dt)[source]

Pre-allocate variable arrays in compartments, links and dependent variables for faster processing. Array maintains initial value but pre-fills everything else with NaNs. Thus errors due to incorrect parset value saturation should be obvious from results.

type = None

The population’s type

class atomica.model.TimedCompartment(pop, name, duration, destination)[source]
preallocate(tvec, dt)[source]

Preallocate data storage

This method gets called just before integration, once the final sizes of the arrays are known. Performance is improved by preallocating the arrays. The tvec attribute is assigned as-is, so it is typically a reference to the array stored in the parent Model object. Thus, there is no duplication of storage there, and Variable.tvec is mainly for convenience when interpolating or plotting.

This method may be overloaded in derived classes to preallocate other variables specific to those classes.

Parameters
  • tvec – An array of time values

  • dt – Time step size

class atomica.model.Variable(pop, id)[source]

Integration object to manage compartments, characteristics, parameters, and links

This is a lightweight abstract class to store arrays of values at each simulation time step. It includes functionality that is common to all integration objects, and defines the interface to be implemented by derived classes.

param pop

A Population instance. This allows references back to the population containing an object (which facilitates a number of operations such as those that require the population’s size)

param id

ID is a tuple that uniquely identifies the Variable within a model. By convention, this is a population:code_name tuple (but in the case of links, there are additional terms)

dt = None

Time step size

id = None

Unique identifier for the integration object

property name

Variable code name

This is implemented as a property method because the id of the Variable is a tuple containing the population name and the variable code name, so this property method returns just the variable code name portion. That way, storage does not need to be duplicated.

Return type

str

Returns

A code name

plot()[source]

Produce a time series plot

This is a quick function to make a basic line plot of this Variable. Mainly intended for debugging. Production-ready plots should be generated using the plotting library functions instead

Return type

None

pop = None

Reference back to the Population containing this object

preallocate(tvec, dt)[source]

Preallocate data storage

This method gets called just before integration, once the final sizes of the arrays are known. Performance is improved by preallocating the arrays. The tvec attribute is assigned as-is, so it is typically a reference to the array stored in the parent Model object. Thus, there is no duplication of storage there, and Variable.tvec is mainly for convenience when interpolating or plotting.

This method may be overloaded in derived classes to preallocate other variables specific to those classes.

Parameters
  • tvec (<built-in function array>) – An array of time values

  • dt (float) – Time step size

Return type

None

set_dynamic(**kwargs)[source]

Make the variable a dependency

For Compartments and Links, this does nothing. For Characteristics and Parameters, it will set the dynamic flag, but in addition, any validation constraints e.g. a Parameter that depends on Links cannot itself be dynamic, will be enforced.

This method generally must be overloaded in derived classes e.g. Parameter.set_dynamic()

Return type

None

t = None

Array of time values. This should be a reference to the base array stored in a model object

units = None

The units for the quantity, used for plotting and for validation. Note that the default 'unknown' units are distinct to dimensionless units, which have value ''

update(ti)[source]

Update the value at a given time index

This method performs any required computations to update the value of the variable at a given time index. For example, Parameters may require their update function to be called, while characteristics need their source compartments to be added up.

This method generally must be overloaded in derived classes e.g. Parameter.update()

Parameters

ti (int) – Time index to update

Return type

None

vals = None

The fundamental values stored by this object. Note that Characteristics implement this as a property method

atomica.model.run_model(settings, framework, parset, progset=None, program_instructions=None, name=None)[source]

Processes the TB epidemiological model. Parset-based overwrites are generally done externally, so the parset is only used for model-building. Progset-based overwrites take place internally and must be part of the processing step. The instructions dictionary is usually passed in with progset to specify when the overwrites take place.