Package {sdbuildR}


Title: Easily Build, Simulate, and Explore Stock-and-Flow Models
Version: 2.0.0
Description: Stock-and-flow models are a computational method from the field of system dynamics. They represent how systems change over time and are mathematically equivalent to ordinary differential equations. 'sdbuildR' (system dynamics builder) provides an intuitive interface for constructing stock-and-flow models without requiring extensive domain knowledge. Models can quickly be simulated and revised, supporting iterative development. 'sdbuildR' simulates models in 'R' and 'Julia', and supports computationally intensive ensemble simulations. Additionally, 'sdbuildR' can import models created in 'Insight Maker' (https://insightmaker.com/).
License: GPL (≥ 3)
URL: https://kcevers.github.io/sdbuildR/, https://github.com/KCEvers/sdbuildR
BugReports: https://github.com/KCEvers/sdbuildR/issues
Depends: R (≥ 4.2.0)
Imports: cli, data.table, deSolve, DiagrammeR, igraph, jsonlite, JuliaConnectoR, plotly, rlang, stringi, stringr, textutils, withr, xml2
Suggests: DiagrammeRsvg, future, future.apply, htmlwidgets, kableExtra, knitr, qgraph, rsvg, testthat (≥ 3.0.0), this.path, webshot2
Config/Needs/website: rmarkdown, lavaan
Config/testthat/edition: 3
Encoding: UTF-8
RoxygenNote: 7.3.3
NeedsCompilation: no
Packaged: 2026-06-15 22:19:24 UTC; kevers1
Author: Kyra Caitlin Evers ORCID iD [aut, cre, cph]
Maintainer: Kyra Caitlin Evers <kyra.c.evers@gmail.com>
Repository: CRAN
Date/Publication: 2026-06-16 06:50:11 UTC

Create data frame of simulation results

Description

Convert simulation results to a data.frame.

Usage

## S3 method for class 'simulate_stockflow'
as.data.frame(x, row.names = NULL, optional = FALSE, direction = "long", ...)

Arguments

x

Output of simulate().

row.names

NULL or a character vector giving the row names for the data frame. Missing values are not allowed.

optional

Ignored parameter.

direction

Format of data frame, either "long" (default) or "wide".

...

Optional parameters

Value

A data.frame with simulation results. For direction = "long" (default), the data frame has three columns: time, variable, and value. For direction = "wide", the data frame has columns time followed by one column per variable.

See Also

simulate(), stockflow()

Examples

sfm <- stockflow("SIR")
sim <- simulate(sfm)
df <- as.data.frame(sim)
head(df)

# Get results in wide format
df_wide <- as.data.frame(sim, direction = "wide")
head(df_wide)


Convert stock-and-flow model to data frame

Description

Create a data frame with properties of all model variables and functions. Specify the variable types, variable names, and/or properties to get a subset of the data frame.

Usage

## S3 method for class 'stockflow'
as.data.frame(
  x,
  row.names = NULL,
  optional = FALSE,
  name = NULL,
  type = NULL,
  properties = NULL,
  ...
)

Arguments

x

A stock-and-flow model object of class stockflow.

row.names

NULL or a character vector giving the row names for the data frame. Missing values are not allowed.

optional

Ignored parameter.

name

Variable names to retain in the data frame. Defaults to NULL to include all variables.

type

Variable types to retain in the data frame. Must be one or more of 'stock', 'flow', 'constant', 'aux', 'gf', or 'func'. Defaults to NULL to include all types.

properties

Variable properties to retain in the data frame. Defaults to NULL to include all properties.

...

Optional arguments

Value

A data.frame with one row per model component. Common columns include type (component type), name (variable name), eqn (equation), and label (descriptive label). Additional columns may include to, from, non_negative, and others depending on variable types. The exact columns returned depend on the type and properties arguments. Returns an empty data.frame if no components match the filters.

Examples

as.data.frame(stockflow("SIR"))

# Only show stocks
as.data.frame(stockflow("SIR"), type = "stock")

# Only show equation and label
as.data.frame(stockflow("SIR"), properties = c("eqn", "label"))


Convert verify() results to a data frame

Description

Converts a verify_stockflow object to a data frame.

Usage

## S3 method for class 'verify_stockflow'
as.data.frame(
  x,
  row.names = NULL,
  optional = FALSE,
  which = c("tests", "sims")[1],
  direction = "long",
  test = NULL,
  label = NULL,
  ignore_case = TRUE,
  status = c("pass", "fail", "error", "skip"),
  condition = NULL,
  ...
)

Arguments

x

A verify_stockflow object (output of verify()).

row.names

NULL or a character vector giving row names (optional).

optional

Ignored; present for compatibility.

which

Character. "tests" (default) or "sims". Partial matching supported.

direction

Character. "long" (default) or "wide". Only used when which = "sims".

test

Integer vector of test number(s) to display (1-based). Defaults to NULL (show all tests). Can be combined with label (intersection).

label

Character vector of regex patterns for partial, case-insensitive label matching. A test is included if its label matches any pattern. E.g., c("non-neg", "beta") returns tests matching either fragment. Can be combined with test (intersection).

ignore_case

Logical; whether label matching is case-insensitive. Default TRUE.

status

Optional character vector of statuses to include (e.g., c("fail", "error")). Defaults to all statuses.

  • which = "tests": filters rows by test status.

  • which = "sims": filters to conditions that have at least one test with a matching status.

condition

Optional integer vector of condition numbers to filter by. For which = "sims", keeps only the matching condition simulations. For which = "tests", keeps only tests belonging to those conditions.

...

Additional arguments (unused).

Details

which = "tests" (default) returns one row per unit test with columns test, label, status, outcome, expr_str, conditions, and message. Use test, label, and status to filter.

which = "sims" returns the underlying simulation time-series in long format with columns test (test number(s) that used this simulation, as a comma-separated string), conditions (specified conditions per test, if any), time, variable, and value. Each unique condition generates one simulation; if multiple tests share a condition their numbers are combined in test (e.g. "1, 3"). When filtering with test, the displayed test value shows only the requested matching test number(s) for the retained simulation row(s). Use direction = "wide" to pivot variables into columns.

Value

A data.frame. Column set depends on which:

Examples

# Create model with 2 unit tests
sfm <- stockflow("SIR") |>
  unit_test(expr = all(susceptible >= 0)) |>
  # Add test with conditions
  unit_test(
    label = "lower infection rate",
    expr = all(susceptible >= 0),
    conditions = list(infection_rate = 0.1)
  )
res <- verify(sfm)

# Test results (default)
as.data.frame(res)

# Simulation time-series (long format)
as.data.frame(res, which = "sims") |> head()

# Simulation time-series (wide format)
as.data.frame(res, which = "sims", direction = "wide") |> head()

# Filter to simulation for test 2 only
as.data.frame(res, which = "sims", test = 2) |> head()

# Only simulations for passing tests
as.data.frame(res, which = "sims", status = "pass") |> head()

Add or modify auxiliaries

Description

Auxiliaries are dynamic variables used for intermediate calculations in the system. auxiliary() adds or changes an auxiliary variable. This is a convenience wrapper around update() with type = "aux". See the Auxiliaries section of update() for more details.

Usage

auxiliary(object, name, eqn = 0, label = name, doc = "", non_negative = FALSE)

aux(object, name, eqn = 0, label = name, doc = "", non_negative = FALSE)

Arguments

object

Stock-and-flow model, object of class stockflow.

name

Variable name. Accepts a bare symbol (e.g., population), a string ("population"), or a vector via c() (e.g., c(a, b) or c("a", "b")). Use ⁠!!⁠ to inject from a variable.

eqn

Equation (or initial value in the case of stocks). Accepts a bare expression (e.g., a * b + 1), a string ("a * b + 1"), or a numeric value. Use ⁠!!⁠ to inject from a variable. Defaults to 0.

label

Name of variable used for plotting. Defaults to the same as name.

doc

Description of variable. Defaults to "" (no description).

non_negative

If TRUE, variable is enforced to be non-negative (i.e., strictly 0 or positive). Defaults to FALSE.

Value

A stock-and-flow model object of class stockflow

See Also

update(), discard(), change_name()

Examples


# Create an auxiliary for an intermediate calculation
sfm <- stockflow() |>
  stock(population, eqn = 100) |>
  constant(carrying_capacity, eqn = 1000) |>
  auxiliary(density, eqn = population / carrying_capacity, label = "Density")


Change name of variable

Description

Change the name of a variable throughout the model. This updates the data frame and all references in equations, flow connections, and labels.

Usage

change_name(object, name, new_name)

Arguments

object

Stock-and-flow model, object of class stockflow.

name

Variable name. Accepts a bare symbol (e.g., population), a string ("population"), or a vector via c() (e.g., c(a, b) or c("a", "b")). Use ⁠!!⁠ to inject from a variable.

new_name

New name. Character vector of the same length as name. Must be unique across all existing variables.

Value

A stock-and-flow model object of class stockflow with the name changed throughout the model.

See Also

update(), discard()

Examples

sfm <- stockflow("SIR")
sfm <- change_name(sfm, c(susceptible, infected, recovered),
  new_name = c(S, I, R)
)
print(sfm)

# References to old names are updated
as.data.frame(sfm, type = "flow", properties = c("name", "eqn", "to", "from"))


Change variable type

Description

Change the type of a variable in a stock-and-flow model.

Usage

change_type(object, name, new_type)

Arguments

object

Stock-and-flow model, object of class stockflow.

name

Variable name. Accepts a bare symbol (e.g., population), a string ("population"), or a vector via c() (e.g., c(a, b) or c("a", "b")). Use ⁠!!⁠ to inject from a variable.

new_type

New variable type; one of 'stock', 'flow', 'constant', 'aux', 'gf', or 'func'. Character vector of the same length as name.

Value

A stock-and-flow model object of class stockflow with the variable type changed throughout the model. Note that changing the type may result in changes to other properties (e.g., a flow must have "to" and/or "from" properties, so these will be added if not already present), and may require changes to the equations of connected variables.

See Also

update()

Examples

# Start with a simple predator-prey model
sfm <- stockflow("predator_prey")

# Change the birth rate of predators from a constant to an auxiliary
sfm <- change_type(sfm, delta, new_type = "aux")

# This allows us to introduce time-dependence in the birth rate
# (e.g., seasonality with a sine function)
sfm <- sfm |>
  update(delta, eqn = 0.025 + 0.01 * sin(2 * pi * t / 12))
sim <- simulate(sfm)
plot(sim)


Clean variable name(s)

Description

Clean variable name(s) to create syntactically valid, unique names for use in R and Julia.

Usage

clean_name(new, protected = NULL)

Arguments

new

Vector of names to transform to valid names

protected

Optional vector of protected names, e.g., existing names in model

Value

Vector of cleaned names

Examples

sfm <- stockflow("predator_prey")
# As the variable name "predator" is already taken, clean_name() will create
# a unique name
clean_name("predator", as.data.frame(sfm)[["name"]])


Compare two stock-and-flow models

Description

Compares the structure, equations, and simulation settings of two stockflow models, and computes a nonlinearity score for each.

Usage

compare_models(sfm1, sfm2)

Arguments

sfm1

A stock-and-flow model of class stockflow.

sfm2

A stock-and-flow model of class stockflow.

Value

An object of class compare_stockflow (a list) containing:

labels

Names of the two model objects (captured expressions).

added

Variables present in sfm2 but not sfm1.

removed

Variables present in sfm1 but not sfm2.

type_changed

Variables with different types.

eqn_changed

Variables with different equations.

sim_settings_diff

Simulation settings that differ.

properties

Per-model counts and nonlinearity scores.

See Also

simulate(), summary()

Examples

sfm1 <- stockflow("SIR")
sfm2 <- stock(sfm1, "susceptible", eqn = 0.5)
compare_models(sfm1, sfm2)


Add or modify constants

Description

Constants are time-independent variables that do not change over the course of a simulation. constant() adds or changes a constant variable. This is a convenience wrapper around update() with type = "constant". See the Constants section of update() for more details.

Usage

constant(object, name, eqn = 0, label = name, doc = "", non_negative = FALSE)

Arguments

object

Stock-and-flow model, object of class stockflow.

name

Variable name. Accepts a bare symbol (e.g., population), a string ("population"), or a vector via c() (e.g., c(a, b) or c("a", "b")). Use ⁠!!⁠ to inject from a variable.

eqn

Equation (or initial value in the case of stocks). Accepts a bare expression (e.g., a * b + 1), a string ("a * b + 1"), or a numeric value. Use ⁠!!⁠ to inject from a variable. Defaults to 0.

label

Name of variable used for plotting. Defaults to the same as name.

doc

Description of variable. Defaults to "" (no description).

non_negative

If TRUE, variable is enforced to be non-negative (i.e., strictly 0 or positive). Defaults to FALSE.

Value

A stock-and-flow model object of class stockflow

See Also

update(), discard(), change_name()

Examples


# Create constants for model parameters
sfm <- stockflow() |>
  constant(growth_rate, eqn = 0.1, label = "Growth Rate") |>
  constant(carrying_capacity, eqn = 1000, label = "Carrying Capacity")


Check whether value is in vector or string

Description

Equivalent of .Contains() in Insight Maker.

Usage

contains_IM(haystack, needle)

Arguments

haystack

Vector or string to search through

needle

Value to search for

Value

Logical value

Examples

contains_IM(c("a", "b", "c"), "d") # FALSE
contains_IM(c("abcdef"), "bc") # TRUE

Create or modify custom variables or functions

Description

Custom functions are user-defined functions that can be used throughout a stock-and-flow model. custom_func() adds or changes a function. This is a convenience wrapper around update() with type = "func".

Usage

custom_func(object, name, eqn = 0, label = name, doc = "")

Arguments

object

Stock-and-flow model, object of class stockflow.

name

Name of the function variable. The equation will be assigned to this name.

eqn

Equation of the function variable. A character vector. Defaults to 0.

label

Name of variable used for plotting. Defaults to the same as name.

doc

Documentation. Defaults to "".

Value

A stock-and-flow model object of class stockflow

See Also

update(), discard(), change_name()

Examples


# Simple function
sfm <- stockflow() |>
  custom_func(double, eqn = "function(x) x * 2") |>
  constant(a, eqn = double(2))

# Function with defaults
sfm <- stockflow() |>
  custom_func(scale, eqn = "function(x, factor = 10) x * factor") |>
  constant(b, eqn = scale(2))

# If the logistic() function did not exist, you could create it yourself:
sfm <- stockflow() |>
  custom_func(my_logistic, eqn = "function(x, slope = 1, midpoint = .5){
   1 / (1 + exp(-slope*(x-midpoint)))
 }") |>
  constant(c_, eqn = my_logistic(2, slope = 50))


Find dependencies

Description

Find which other variables each variable is dependent on.

Usage

dependencies(object, name = NULL, type = NULL, reverse = FALSE)

Arguments

object

Stock-and-flow model, object of class stockflow.

name

Variable names to find dependencies for. Defaults to NULL to include all variables.

type

Variable types to find dependencies for. Must be one or more of 'stock', 'flow', 'constant', 'aux', 'gf', or 'func'. Defaults to NULL to include all types.

reverse

If FALSE, list for each variable X which variables Y it depends on for its equation definition. If TRUE, don't show dependencies but dependents. This reverses the dependencies, such that for each variable X, it lists what other variables Y depend on X.

Value

List, with for each model variable what other variables it depends on, or if reverse = TRUE, which variables depend on it

Examples

sfm <- stockflow("SIR")
dependencies(sfm)


Remove variable(s)

Description

Remove variable(s) from a stock-and-flow model. All references in flow connections and graphical function sources are also removed. A warning will be thrown if any lingering references to the removed name remain in the model.

Usage

discard(
  object,
  name,
  remove_references = c("to", "from", "source", "unit_test")
)

Arguments

object

Stock-and-flow model, object of class stockflow.

name

Name(s) to remove. Accepts bare symbols (e.g., x), strings, or vectors via c(). Must be variable names.

remove_references

Where to remove references to the discarded variables. By default, references to discarded variables in "to", "from", "source", and "unit_test" are removed. Set to NULL to keep all references (not recommended). Note that any lingering references in equations will cause errors in simulation and should be removed or updated with update() after discarding the variable.

Value

A stock-and-flow model object of class stockflow

See Also

update(), change_name()

Examples

# Add stock
sfm <- stockflow() |> stock(x)
print(sfm)

# Remove stock
sfm <- discard(sfm, x)
print(sfm)

Remove a unit test from a stock-and-flow model

Description

Remove one or more unit tests by test (integer position as shown by unit_tests()) or by label (character). Warns if a label or index is not found. Remaining tests are renumbered sequentially after removal.

Usage

discard_unit_test(object, label, test)

Arguments

object

Stock-and-flow model, object of class stockflow.

label

Character label(s) of the test(s) to remove. Supports NSE (bare symbol or string). For backward compatibility, integer values passed via label are also accepted.

test

Integer index/indices of the test(s) to remove. Corresponds to the order shown by unit_tests().

Value

The model object with the specified test(s) removed.

See Also

unit_test(), unit_tests()

Examples

sfm <- stockflow("SIR") |>
  unit_test(label = "susceptible is non-negative", expr = all(susceptible >= 0)) |>
  unit_test(label = "recovered increases", expr = all(diff(recovered) >= 0))

# Remove by test
sfm <- discard_unit_test(sfm, test = 1)

# Remove by label
sfm <- discard_unit_test(sfm, label = "recovered increases")

Run ensemble simulations

Description

Run large-scale (i.e., ensemble) simulations of stock-and-flow models, varying initial conditions and/or constants specified in conditions.

Usage

ensemble(
  object,
  n = 10,
  conditions = NULL,
  cross = TRUE,
  quantiles = c(0.025, 0.975),
  verbose = TRUE,
  ...
)

Arguments

object

Stock-and-flow model, object of class stockflow.

n

Number of simulations to run in the ensemble. When conditions is specified, n defines the number of simulations to run per condition. If each condition only needs to be run once, set n = 1. Defaults to n = 10.

conditions

A named list specifying fixed values for ensemble conditions. Names must correspond to stocks or constants in the model. Each list element should be a numeric vector of values to test.

If cross = TRUE (default), all combinations of values are generated. For example, list(param1 = c(1, 2), param2 = c(10, 20)) creates 4 conditions: ⁠(1,10), (1,20), (2,10), (2,20)⁠.

If cross = FALSE, values are paired element-wise, requiring all vectors to have equal length. For example, list(param1 = c(1, 2, 3), param2 = c(10, 20, 30)) creates 3 conditions: ⁠(1,10), (2,20), (3,30)⁠. Defaults to NULL (no parameter variation).

cross

If TRUE, cross the parameters in the conditions list to generate all possible combinations of parameters. Defaults to TRUE.

quantiles

Quantiles to calculate in the summary, e.g., c(0.025, 0.975).

verbose

If TRUE (default), print details and duration of simulation.

...

Optional arguments passed to sim_settings(); these can be used to override the simulation specifications set in the model object.

Details

It is strongly recommended to reduce the size of the simulation output by saving fewer values with save_at or save_n in sim_settings().

By default, only summary statistics across simulations are returned. To return individual simulations, set save_sims = TRUE in sim_settings() or pass save_sims = TRUE via ... in ensemble(). Note that returning individual simulations can consume a lot of memory for large ensembles.

For simulations in Julia, the ensemble can be run in parallel using multiple threads by setting nthreads in use_julia(). For simulations in R, use future::plan() to control parallel execution.

To create a reproducible ensemble simulation, set a seed using sim_settings().

If you do not see any variation within a condition of the ensemble (i.e., the confidence bands are virtually non-existent), there are likely no random elements in your model. Without these, there can be no variability in the model. Try specifying a random initial condition or adding randomness to other model elements (see examples).

Value

Object of class ensemble_stockflow, which is a list containing:

success

If TRUE, simulation was successful. If FALSE, simulation failed.

error_message

If success is FALSE, contains the error message.

df

data.frame with simulation results in long format, if save_sims is TRUE. The iteration number is indicated by column "sim". If conditions was specified, the condition is indicated by column "condition".

summary

data.frame with summary statistics of the ensemble, including quantiles specified in quantiles. If conditions was specified, summary statistics are calculated for each condition in the ensemble.

n

Number of simulations run in the ensemble (per condition if conditions is specified).

n_total

Total number of simulations run in the ensemble (across all conditions if conditions is specified).

n_conditions

Total number of conditions.

conditions

data.frame with the conditions used in the ensemble, if conditions was specified.

init

List with df (if save_sims = TRUE) and summary, containing data.frame with the initial values of the stocks used in the ensemble.

constants

List with df (if save_sims = TRUE) and summary, containing data.frame with the constant parameters used in the ensemble.

script

Script used for the ensemble simulation.

duration

Duration of the simulation in seconds.

...

Other parameters passed to ensemble

See Also

update(), stockflow(), sim_settings(), use_julia(), future::plan()

Examples

# Ensemble simulation in R (no parallelization)
# Load example
sfm <- stockflow("predator_prey")

# Set random initial conditions
sfm <- update(sfm, c(predator, prey),
  eqn = runif(1, min = 20, max = 80)
)

# For ensemble simulations, it is highly recommended to reduce the
# returned output. For example, to save only 20 values per simulation:
sfm <- sim_settings(sfm, save_n = 20)

# Run ensemble simulation with a small number of simulations
sims <- ensemble(sfm, n = 3)
if (interactive()) plot(sims)


# To plot individual trajectories, rerun the ensemble with save_sims = TRUE.
# Note that this can consume a lot of memory for large simulations.
sims <- ensemble(sfm, n = 10, save_sims = TRUE)
plot(sims, which = "sims")

# Specify which trajectories to plot
plot(sims, which = "sims", sim = 1)

# Plot the median with lighter individual trajectories
plot(sims, central_tendency = "median", which = "sims", alpha = 0.1)

# For larger ensembles, we can use parallelization with future
if (requireNamespace("future", quietly = TRUE) &&
  requireNamespace("future.apply", quietly = TRUE)) {
  future::plan(future::multisession, workers = 4)
}

# Ensembles can also be run with exact values for the initial conditions
# and parameters. Below, we vary the initial values of the predator and the
# birth rate of the predators (delta). We generate a hundred samples per
# condition. By default, the parameters are crossed, meaning that all
# combinations of the parameters are run.
sims <- ensemble(sfm,
  n = 50,
  conditions = list(predator = c(10, 50), delta = c(.025, .05))
)

plot(sims)

# By default, a maximum of nine conditions is plotted.
# Plot specific conditions:
plot(sims, condition = c(1, 3), nrows = 1)

# Generate a non-crossed design, where the length of each conditions vector
# needs to be equal:
sims <- ensemble(sfm,
  n = 10, cross = FALSE,
  conditions = list(
    predator = c(10, 20, 30),
    delta = c(.020, .025, .03)
  )
)
plot(sims, nrows = 3)

# Stop parallelization after use
if (requireNamespace("future", quietly = TRUE) &&
  requireNamespace("future.apply", quietly = TRUE)) {
  future::plan(future::sequential)
}


Expit function

Description

Inverse of the logit function

Usage

expit(x)

Arguments

x

Numerical value

Value

Numerical value

Examples

expit(1)

Export a stock-and-flow model

Description

Export a model of class stockflow to another format.

Usage

export_model(
  object,
  format = c("sdbuildR", "deSolve", "psychomodels"),
  file = NULL,
  title = object[["meta"]][["name"]],
  description = object[["meta"]][["caption"]],
  explanation = description,
  publication_doi = "",
  publication_citation = "",
  framework = "Ordinary Differential Equations",
  programming_language = "R",
  psychology_discipline = "",
  software_package = "",
  model_variable = "",
  code_repository_url = "",
  data_url = "",
  submission_remarks = "",
  created_by = "",
  updated_by = "",
  published_by = "",
  published_at = Sys.time(),
  published_pending_moderation_at = Sys.time(),
  publication_citation_fetched_at = Sys.time(),
  publication_csl_fetched_at = Sys.time(),
  publication_csl_json = "",
  id = NA,
  slug = NULL,
  include_latex = TRUE,
  pretty = TRUE
)

Arguments

object

Stock-and-flow model, object of class stockflow.

format

Export format. One of "sdbuildR", "deSolve", or "psychomodels".

file

Output file path, or NULL to return the result directly.

title

[psychomodels] Model title. Defaults to object[["meta"]][["name"]].

description

[psychomodels] Model description. Defaults to object[["meta"]][["caption"]].

explanation

[psychomodels] Free-text explanation. Defaults to description.

publication_doi

[psychomodels] DOI for the associated publication.

publication_citation

[psychomodels] Citation text.

framework

[psychomodels] Modeling framework. Defaults to "Ordinary Differential Equations".

programming_language

[psychomodels] Programming language.

psychology_discipline

[psychomodels] Discipline id(s), comma-separated.

software_package

[psychomodels] Package id(s), comma-separated.

model_variable

[psychomodels] Variable id(s), comma-separated.

code_repository_url

[psychomodels] URL to code repository.

data_url

[psychomodels] URL to model data.

submission_remarks

[psychomodels] Optional remarks.

created_by

[psychomodels] Identifier of creating user.

updated_by

[psychomodels] Identifier of last updating user.

published_by

[psychomodels] Identifier of publishing user.

published_at

[psychomodels] Publication timestamp. Defaults to current time.

published_pending_moderation_at

[psychomodels] Moderation timestamp.

publication_citation_fetched_at

[psychomodels] Citation fetch timestamp.

publication_csl_fetched_at

[psychomodels] CSL fetch timestamp.

publication_csl_json

[psychomodels] CSL JSON text.

id

[psychomodels] Optional record id.

slug

[psychomodels] Optional slug. Generated from title if NULL.

include_latex

[psychomodels] If TRUE, append LaTeX equations to explanation.

pretty

[psychomodels] If TRUE, pretty-print output JSON.

Details

sdbuildR format (format = "sdbuildR")

Returns R code that reconstructs the model using sdbuildR functions. When file = NULL, returns a character string. When file is provided, writes an .R file and returns the path invisibly. If file has no .R extension, one is appended.

deSolve format (format = "deSolve")

Returns a standalone R script using deSolve::ode() directly — no sdbuildR dependency required to run the output. When file = NULL, returns a character string. When file is provided, writes an .R file and returns the path invisibly. If file has no .R extension, one is appended. Requires sim_settings(language = "R") (the default).

Psychomodels format (format = "psychomodels")

Generates a JSON record for upload to Psychomodels. When file = NULL, returns a JSON character string. When file is provided, writes a .json file and returns the path invisibly. If file has no .json extension, one is appended.

Value

For file = NULL: a character string containing the exported content. For file specified: invisibly returns the file path.

See Also

import_insightmaker(), import_desolve()

Examples

sfm <- stockflow("SIR")

# Get sdbuildR reconstruction code
cat(export_model(sfm, format = "sdbuildR"))

# Get standalone deSolve script
cat(export_model(sfm, format = "deSolve"))

# Export to Psychomodels JSON
## Not run: 
json <- export_model(sfm,
  format = "psychomodels",
  publication_doi = "10.0000/example"
)

## End(Not run)

Save plot to a file

Description

Save a plot of a stock-and-flow diagram or a simulation to a specified file path. Note that saving plots requires additional packages to be installed (see below).

Usage

export_plot(
  pl,
  file,
  width = 3,
  height = 4,
  units = "cm",
  dpi = 300,
  font_family = ""
)

Arguments

pl

Plot object. Can be a grViz object from the DiagrammeR package (for stock-and-flow diagrams) or a plotly object from the plotly package (for (ensemble) simulation results).

file

File path to save plot to, including a file extension. For plotting a stock-and-flow model, the file extension can be one of png, pdf, svg, ps, eps, webp. For plotting a simulation, the file extension can be one of png, pdf, jpg, jpeg, webp. For plotting a qgraph graph, the file extension can be one of png, pdf, svg, ps, eps, jpg, jpeg, tiff, bmp. If no file extension is specified, it will default to png.

width

Width of image in units.

height

Height of image in units.

units

Units in which width and height are specified. Either "cm", "in", or "px".

dpi

Resolution of image. Only used if units is not "px".

font_family

Font family used for qgraph exports. For PDF/PS/EPS exports, this is applied when the graphics device is opened.

Value

Returns NULL invisibly, called for side effects.

Examples


# Only if dependencies are installed
if (requireNamespace("DiagrammeRsvg", quietly = TRUE) &&
  requireNamespace("rsvg", quietly = TRUE)) {
  sfm <- stockflow("SIR")
  file <- tempfile(fileext = ".png")
  export_plot(plot(sfm), file)

  # Remove plot
  file.remove(file)
}


## Not run: 
# requires internet
# Only if suggested dependencies are installed
if (requireNamespace("htmlwidgets", quietly = TRUE) &&
  requireNamespace("webshot2", quietly = TRUE)) {
  # Requires Chrome to save plotly plot:
  sim <- simulate(sfm)
  export_plot(plot(sim), file)

  # Remove plot
  file.remove(file)
}

## End(Not run)


Find a named argument in a list of call arguments

Description

Searches by name; also handles positional fallback for the second argument when the name is absent (e.g., expect_equal(x, y, tolerance = 0.01)).

Usage

find_named_arg(args, name)

Arguments

args

List of call arguments (from as.list(e[-1]))

name

Character name of the argument to find

Value

The argument value, or NULL if not found


Add or modify flows

Description

Flows move material and information through the system, increasing or decreasing stocks. flow() adds or changes a flow variable. This is a convenience wrapper around update() with type = "flow". See the Flows section of update() for more details.

Usage

flow(
  object,
  name,
  eqn = 0,
  to = NULL,
  from = NULL,
  label = name,
  doc = "",
  non_negative = FALSE
)

Arguments

object

Stock-and-flow model, object of class stockflow.

name

Variable name. Accepts a bare symbol (e.g., population), a string ("population"), or a vector via c() (e.g., c(a, b) or c("a", "b")). Use ⁠!!⁠ to inject from a variable.

eqn

Equation (or initial value in the case of stocks). Accepts a bare expression (e.g., a * b + 1), a string ("a * b + 1"), or a numeric value. Use ⁠!!⁠ to inject from a variable. Defaults to 0.

to

Target of flow. Accepts a bare symbol or string. Must be a stock in the model. Defaults to NULL to indicate no target.

from

Source of flow. Accepts a bare symbol or string. Must be a stock in the model. Defaults to NULL to indicate no source.

label

Name of variable used for plotting. Defaults to the same as name.

doc

Description of variable. Defaults to "" (no description).

non_negative

If TRUE, variable is enforced to be non-negative (i.e., strictly 0 or positive). Defaults to FALSE.

Value

A stock-and-flow model object of class stockflow

See Also

update(), discard(), change_name()

Examples


# Create a flow into a stock
sfm <- stockflow() |>
  stock(population, eqn = 100) |>
  flow(births, eqn = population * 0.1, to = population) |>
  flow(deaths, eqn = population * 0.05, from = population)


Check if user has internet

Description

Internal function

Usage

has_internet()

Value

Logical value

Examples

has_internet()


Print first rows of a simulation

Description

Print the first rows of a simulation data frame of a stock-and-flow model. This is a wrapper around head() that first converts the simulation results to a data frame using as.data.frame().

Usage

## S3 method for class 'simulate_stockflow'
head(x, n = 6L, ...)

Arguments

x

Output of simulate().

n

Number of rows to print. Defaults to 6.

...

Other arguments passed to as.data.frame.simulate_stockflow().

Value

A data.frame with the first rows of the simulation results.

Examples

sfm <- stockflow("SIR")
sim <- simulate(sfm)
head(sim)

Print first rows of verify results

Description

Wrapper around head() that first converts the results to a data frame using as.data.frame.verify_stockflow().

Usage

## S3 method for class 'verify_stockflow'
head(x, n = 6L, ...)

Arguments

x

A verify_stockflow object.

n

Number of rows. Defaults to 6.

...

Other arguments passed to as.data.frame.verify_stockflow().

Value

A data.frame.

Examples

sfm <- stockflow("SIR") |>
  unit_test(expr = all(susceptible >= 0))
res <- verify(sfm)
head(res)

Hill function

Description

Computes the Hill function with configurable slope, midpoint, and upper asymptote.

Usage

hill(x, slope = 1, midpoint = 0.5, upper = 1)

Arguments

x

Value at which to evaluate the function

slope

Slope of Hill function at the midpoint. Defaults to 1.

midpoint

Midpoint of Hill function where the output is upper/2. Defaults to 0.5.

upper

Upper asymptote (maximal value) of the Hill function. Defaults to 1.

Details

The Hill function is a smooth S-shaped curve (when slope > 1) bounded between 0 and upper. It transitions from near 0 to near upper around the midpoint, with the steepness of this transition controlled by slope. See https://en.wikipedia.org/wiki/Hill_equation_%28biochemistry%29 for more details.

Value

Numeric value given by

f(x) = \frac{upper \cdot x^{slope}}{midpoint^{slope} + x^{slope}}

Examples

hill(0)

# Adjust parameters
hill(0, slope = 5, midpoint = 0.5, upper = 10)


Import a deSolve model

Description

Convert a model written for deSolve into a stock-and-flow model of class stockflow.

Usage

import_desolve(model, params, init, times, method = "lsoda", name = NULL)

Arguments

model

A deSolve-style ODE function with arguments ⁠(t, state, parameters)⁠.

params

Named numeric vector of model parameters (constants).

init

Named numeric vector of initial state values (stocks).

times

Numeric vector of time points. Must be evenly spaced (e.g., from seq(start, stop, by = dt)).

method

Integration method. Defaults to "lsoda". See sim_methods().

name

Optional model name. Character scalar.

Details

The model function must follow the canonical deSolve convention:

model <- function(t, state, parameters) {
  with(as.list(c(state, parameters)), {
    dX <- <rate expression>   # d<VarName> for each state in init
    list(c(dX))
  })
}

State variable names are taken from names(init), parameter names from names(params). Each ⁠d<VarName>⁠ assignment inside the with() block is parsed as the net rate of change for stock VarName and becomes a flow in the sfm. Any other assignments in the with() block (intermediate calculations) are imported as auxiliary variables in the order they appear.

Value

A stock-and-flow model of class stockflow.

See Also

import_insightmaker(), export_model(), update()

Examples

logistic_model <- function(t, state, parameters) {
  with(as.list(c(state, parameters)), {
    dN <- r * N * (1 - N / K)
    list(c(dN))
  })
}
sfm <- import_desolve(
  model  = logistic_model,
  params = c(r = 0.3, K = 100),
  init   = c(N = 10),
  times  = seq(0, 50, by = 0.1),
  method = "lsoda",
  name   = "Logistic growth"
)

sim <- simulate(sfm)
plot(sim)

Import Insight Maker model

Description

Import a stock-and-flow model from Insight Maker. Models may be your own or another user's. Importing causal loop diagrams or agent-based models is not supported.

Usage

import_insightmaker(
  url,
  file,
  keep_nonnegative_flow = TRUE,
  keep_nonnegative_stock = FALSE
)

Arguments

url

URL to Insight Maker model. Character.

file

File path to Insight Maker model. Only used if url is not specified. Needs to be a character with suffix .InsightMaker or .json.

keep_nonnegative_flow

If TRUE, keeps original non-negativity setting of flows. Defaults to TRUE.

keep_nonnegative_stock

If TRUE, keeps original non-negativity setting of stocks. Defaults to FALSE.

Details

Insight Maker models can be imported using a URL, Insight Maker file, or ModelJSON file. Ensure the URL refers to a public (not private) model. To download a model file from Insight Maker, first clone the model if it is not your own. Then, go to "Share" (top right), "Export", and "Download Insight Maker file" or "ModelJSON File".

Value

A stock-and-flow model object of class stockflow.

See Also

update(), stockflow()

Examples


# Load a model from Insight Maker
sfm <- import_insightmaker(
  url =
    "https://insightmaker.com/insight/43tz1nvUgbIiIOGSGtzIzj/Romeo-Juliet"
)
plot(sfm)



# Simulate the model
sim <- simulate(sfm)
plot(sim)


Find index of value in vector or string

Description

Equivalent of .IndexOf() in Insight Maker.

Usage

indexof(haystack, needle)

Arguments

haystack

Vector or string to search through

needle

Value to search for

Value

Index, integer

Examples

indexof(c("a", "b", "c"), "b") # 2
indexof("haystack", "hay") # 1
indexof("haystack", "m") # 0

Convert .InsightMaker file to .json file

Description

Import and convert a stock-and-flow model from Insight Maker to a .json file. Models may be your own or another user's. Importing causal loop diagrams or agent-based models is not supported.

Usage

insightmaker_to_json(url, file, destfile = NULL)

Arguments

url

URL to Insight Maker model. Character.

file

File path to Insight Maker model. Only used if url is not specified. Needs to be a character with suffix .InsightMaker.

destfile

Output file path. Must have extension .json or no extension. Overwrites file if it already exists. If not provided, return model in json format.

Details

Insight Maker models can be imported using a URL or Insight Maker file. Ensure the URL refers to a public (not private) model. To download a model file from Insight Maker, first clone the model if it is not your own. Then, go to "Share" (top right), "Export", and "Download Insight Maker file".

Value

If destfile is not provided; object of class "json". If destfile provided, invisibly returns destfile (character string).

Examples


# Convert a model from Insight Maker to json
destfile <- tempfile(fileext = ".json")
json <- insightmaker_to_json(
  url =
    "https://insightmaker.com/insight/43tz1nvUgbIiIOGSGtzIzj/Romeo-Juliet",
  destfile = destfile
)
file.remove(destfile)


Install, update, or remove Julia environment

Description

Instantiate the Julia environment for sdbuildR to run stock-and-flow models using Julia. For more guidance, see this vignette.

Usage

install_julia_env(remove = FALSE)

Arguments

remove

If TRUE, remove Julia environment for sdbuildR. This will delete the Manifest.toml file, as well as the SystemDynamicsBuildR.jl package. All other Julia packages remain untouched.

Details

install_julia_env() will:

Note that this may take 10-25 minutes the first time as Julia downloads and compiles packages.

Value

Invisibly returns NULL after instantiating the Julia environment.

See Also

use_julia()

Examples


## Not run: 
install_julia_env()

# Remove Julia environment
install_julia_env(remove = TRUE)

## End(Not run)


Recursively interpret a parsed R expression

Description

Recursively interpret a parsed R expression

Usage

interpret(e, parent_op = NULL)

Arguments

e

A language object (from parse())

parent_op

The operator of the parent call (used for precedence decisions)

Value

A human-readable string


Length of vector or string

Description

Equivalent of .Length() in Insight Maker, which returns the number of elements when performed on a vector, but returns the number of characters when performed on a string

Usage

length_IM(x)

Arguments

x

A vector or a string

Value

The number of elements in x if x is a vector; the number of characters in x if x is a string

Examples

length_IM(c("a", "b", "c")) # 3
length_IM("abcdef") # 6

Logistic function

Description

Computes the logistic (i.e., sigmoid) function with configurable slope, midpoint, and upper asymptote.

Usage

logistic(x, slope = 1, midpoint = 0, upper = 1)

sigmoid(x, slope = 1, midpoint = 0, upper = 1)

Arguments

x

Value at which to evaluate the function

slope

Slope of logistic function at the midpoint. Defaults to 1.

midpoint

Midpoint of logistic function where the output is upper/2. Defaults to 0.

upper

Upper asymptote (maximal value) of the logistic function. Defaults to 1.

Details

The logistic function is a smooth S-shaped curve bounded between 0 and upper. It transitions from near 0 to near upper around the midpoint, with the steepness of this transition controlled by slope.

Value

Numeric value given by

f(x) = \frac{upper}{1 + e^{-slope \cdot (x - midpoint)}}

Examples

logistic(0)
# equivalent:
sigmoid(0)

# Adjust parameters
logistic(0, slope = 5, midpoint = 0.5, upper = 10)

# Visualize different slopes
x <- seq(-5, 5, length.out = 1000)
plot(x, logistic(x, slope = 1), type = "l", ylab = "f(x)", ylim = c(0, 1))
lines(x, logistic(x, slope = 5), col = "blue")
lines(x, logistic(x, slope = 50), col = "red")
legend("topleft",
  legend = c("slope = 1", "slope = 5", "slope = 50"),
  col = c("black", "blue", "red"), lty = 1
)

Logit function

Description

Logit function

Usage

logit(p)

Arguments

p

Probability, numerical value between 0 and 1

Value

Numerical value

Examples

logit(.1)

Add or modify lookup variables (graphical functions)

Description

Lookup variables define piecewise relationships using specified (x, y) points. lookup() adds or changes a lookup variable. This is a convenience wrapper around update() with type = "lookup". See the Lookup Variables section of update() for more details.

Usage

lookup(
  object,
  name,
  xpts,
  ypts,
  source = NULL,
  interpolation = "linear",
  extrapolation = "nearest",
  label = name,
  doc = "",
  non_negative = FALSE
)

Arguments

object

Stock-and-flow model, object of class stockflow.

name

Variable name. Accepts a bare symbol (e.g., population), a string ("population"), or a vector via c() (e.g., c(a, b) or c("a", "b")). Use ⁠!!⁠ to inject from a variable.

xpts

Only for graphical functions: vector of x-domain points. Must be of the same length as ypts.

ypts

Only for graphical functions: vector of y-domain points. Must be of the same length as xpts.

source

Only for graphical functions: name of the variable which will serve as the input to the graphical function. Accepts a bare symbol or string. Defaults to NULL.

interpolation

Only for graphical functions: interpolation method. Must be either "constant" or "linear". Defaults to "linear".

extrapolation

Only for graphical functions: extrapolation method. Must be either "nearest" or "NA". Defaults to "nearest".

label

Name of variable used for plotting. Defaults to the same as name.

doc

Description of variable. Defaults to "" (no description).

non_negative

If TRUE, variable is enforced to be non-negative (i.e., strictly 0 or positive). Defaults to FALSE.

Value

A stock-and-flow model object of class stockflow

See Also

update(), discard(), change_name()

Examples

# Create a lookup variable for a non-linear relationship
sfm <- stockflow() |>
  lookup(output,
    source = t,
    xpts = c(0, 5, 10),
    ypts = c(0, 10, 15),
    interpolation = "linear"
  ) |>
  stock(x) |>
  flow(x_in, eqn = output(t), to = x)

sim <- simulate(sfm)
plot(sim)


Conditionally wrap a result string in parentheses

Description

Used by infix operators to add parens when the current operator has lower precedence than the parent context.

Usage

maybe_wrap(result, current_op, parent_op)

Arguments

result

The human-readable string for this sub-expression

current_op

The current operator

parent_op

The parent operator (from the recursive call)

Value

Possibly parenthesized string


Modify meta of stock-and-flow model

Description

The meta of a stock-and-flow model contains metadata about the model, such as the name, author, and version. Modify the meta of an existing model with standard or custom properties.

Usage

meta(
  object,
  name = "My Model",
  caption = "My Model Description",
  created = Sys.time(),
  author = "Me",
  version = "1.0",
  URL = "",
  doi = "",
  ...
)

Arguments

object

Stock-and-flow model, object of class stockflow.

name

Model name. Defaults to "My Model".

caption

Model description. Defaults to "My Model Description".

created

Date the model was created. Defaults to Sys.time().

author

Creator of the model. Defaults to "Me".

version

Model version. Defaults to "1.0".

URL

URL associated with model. Defaults to "".

doi

DOI associated with the model. Defaults to "".

...

Optional other entries to add to the meta.

Value

A stock-and-flow model object of class stockflow

Examples

sfm <- stockflow() |>
  meta(
    name = "My first model",
    caption = "This is my first model",
    author = "Kyra Evers",
    version = "1.1"
  )

Determine whether parentheses are needed to preserve meaning

Description

Parentheses are needed when the inner operator has lower precedence than the parent, because without them the human reader might misinterpret the grouping.

Usage

needs_parens(inner_op, outer_op)

Arguments

inner_op

The operator inside the parentheses (or NULL)

outer_op

The operator outside (parent context, or NULL)

Value

logical


Check whether x is less than zero

Description

Check whether x is less than zero.

Usage

nonnegative(x)

Arguments

x

Value

Value

x if x is greater than 0, 0 otherwise

Examples

nonnegative(NA)
nonnegative(-1)


Get the precedence level of an operator

Description

Higher number = binds tighter. Based on R's actual operator precedence.

Usage

op_precedence(op)

Plot timeseries of ensemble simulation

Description

Visualize ensemble simulation results of a stock-and-flow model. Either summary statistics or individual trajectories can be plotted. When multiple conditions j are specified, a grid of subplots is plotted. See ensemble() for examples.

Usage

## S3 method for class 'ensemble_stockflow'
plot(
  x,
  which = c("summary", "sims")[1],
  sim = seq(1, min(c(x[["n"]], 10))),
  condition = seq(1, min(c(x[["n_conditions"]], 9))),
  vars = NULL,
  show_constants = FALSE,
  nrows = ceiling(sqrt(max(condition))),
  margin = 0.05,
  shareX = TRUE,
  shareY = TRUE,
  palette = "Dark 2",
  alpha = 0.3,
  colors = NULL,
  font_family = "Times New Roman",
  font_size = 16,
  wrap_width = 25,
  showlegend = TRUE,
  label_subplots = TRUE,
  central_tendency = c("mean", "median", FALSE)[1],
  central_tendency_width = 3,
  ...
)

Arguments

x

Output of ensemble().

which

Type of plot. Either "summary" for a summary plot with mean or median lines and confidence intervals, or "sims" for individual simulation trajectories with mean or median lines. Defaults to "summary".

sim

Indices of the individual trajectories to plot if which = "sims". Defaults to 1:10. Including a high number of trajectories will slow down plotting considerably.

condition

Indices of the condition to plot. Defaults to 1:9. If only one condition is specified, the plot will not be a grid of subplots.

vars

Variables to plot. Defaults to NULL to plot all variables.

show_constants

If TRUE, include constants in plot. Defaults to FALSE.

nrows

Number of rows in the plot grid. Defaults to ceiling(sqrt(n_conditions)).

margin

Margin between subplots. Either a single numeric or a vector of length four(left, right, top, bottom). See ?plotly::subplot() for more details. Defaults to 0.05.

shareX

If TRUE, share the x-axis across subplots. Defaults to TRUE.

shareY

If TRUE, share the y-axis across subplots. Defaults to TRUE.

palette

Colour palette. Must be one of hcl.pals().

alpha

Trajectory opacity. Defaults to 1.

colors

Vector of colours. If NULL, the color palette will be used. If specified, will override palette. The number of colours must be equal to the number of variables in the simulation data frame. Defaults to NULL.

font_family

Font family. Defaults to "Times New Roman".

font_size

Font size. Defaults to 16.

wrap_width

Width of text wrapping for labels. Must be an integer. Defaults to 25.

showlegend

Whether to show legend. Must be TRUE or FALSE. Defaults to TRUE.

label_subplots

Whether to plot labels indicating the condition of the subplot.

central_tendency

Central tendency to use for the mean line. Either "mean", "median", or FALSE to not plot the central tendency. Defaults to "mean".

central_tendency_width

Line width of central tendency. Defaults to 3.

...

Optional parameters

Value

Plotly object

See Also

ensemble()


Plot timeseries of simulation

Description

Visualize simulation results of a stock-and-flow model. Plot the evolution of stocks over time, with the option of also showing other model variables.

Usage

## S3 method for class 'simulate_stockflow'
plot(
  x,
  show_constants = FALSE,
  vars = NULL,
  palette = "Dark 2",
  colors = NULL,
  font_family = "Times New Roman",
  font_size = 16,
  wrap_width = 25,
  showlegend = TRUE,
  ...
)

Arguments

x

Output of simulate().

show_constants

If TRUE, include constants in plot. Defaults to FALSE.

vars

Variables to plot. Defaults to NULL to plot all variables.

palette

Colour palette. Must be one of hcl.pals().

colors

Vector of colours. If NULL, the color palette will be used. If specified, will override palette. The number of colours must be equal to the number of variables in the simulation data frame. Defaults to NULL.

font_family

Font family. Defaults to "Times New Roman".

font_size

Font size. Defaults to 16.

wrap_width

Width of text wrapping for labels. Must be an integer. Defaults to 25.

showlegend

Whether to show legend. Must be TRUE or FALSE. Defaults to TRUE.

...

Optional parameters

Value

Plotly object

See Also

simulate(), as.data.frame.simulate_stockflow(), plot.simulate_stockflow()

Examples

sfm <- stockflow("SIR")
sim <- simulate(sfm)
plot(sim)

# The default plot title and axis labels can be changed like so:
plot(sim, main = "Simulated trajectory", xlab = "Time", ylab = "Value")

# Add constants to the plot
plot(sim, show_constants = TRUE)


Plot stock-and-flow diagram

Description

Visualize a stock-and-flow diagram using the R package DiagrammeR. Stocks are represented as boxes. Flows are represented as arrows between stocks and/or double circles, where the latter represent what it outside of the model boundary. Thin grey edges indicate dependencies between variables. By default, constants (indicated by italic labels) are not shown. Hover over the variables to see their equations.

Usage

## S3 method for class 'stockflow'
plot(
  x,
  vars = NULL,
  format_label = TRUE,
  wrap_width = 20,
  font_size = 18,
  font_family = "Times New Roman",
  stock_col = "#83d3d4",
  flow_col = "#f48153",
  dependency_col = "#999999",
  show_dependencies = TRUE,
  show_constants = FALSE,
  show_aux = TRUE,
  minlen = 2,
  pad = 0.1,
  nodesep = 0.3,
  ...
)

Arguments

x

A stock-and-flow model object of class stockflow.

vars

Variables to plot. Defaults to NULL to plot all variables.

format_label

If TRUE, apply default formatting (removing periods and underscores) to labels if labels are the same as variable names.

wrap_width

Width of text wrapping for labels. Must be an integer. Defaults to 20.

font_size

Font size. Defaults to 18.

font_family

Font name. Defaults to "Times New Roman".

stock_col

Colour of stocks. Defaults to "#83d3d4".

flow_col

Colour of flows. Defaults to "#f48153".

dependency_col

Colour of dependency arrows. Defaults to "#999999".

show_dependencies

If TRUE, show dependencies between variables. Defaults to TRUE.

show_constants

If TRUE, show constants. Defaults to FALSE.

show_aux

If TRUE, show auxiliary variables. Defaults to TRUE.

minlen

Minimum length of edges; must be an integer. Defaults to 2.

pad

Padding around the graph. Defaults to 0.1.

nodesep

Minimum distance between nodes. Defaults to 0.3.

...

Optional arguments

Value

Stock-and-flow diagram

See Also

import_insightmaker(), stockflow(), plot.simulate_stockflow()

Examples

sfm <- stockflow("SIR")
plot(sfm)

# Don't show constants or auxiliaries
plot(sfm, show_constants = FALSE, show_aux = FALSE)

# Only show specific variables
plot(sfm, vars = "susceptible")


Plot verify results

Description

Visualize the simulation(s) used during verify(). Each condition j is displayed as a subplot. Simulations are always available since verify() unconditionally retains them.

Usage

## S3 method for class 'verify_stockflow'
plot(
  x,
  test = NULL,
  vars = NULL,
  show_constants = FALSE,
  label = NULL,
  ignore_case = TRUE,
  status = c("pass", "fail", "error", "skip"),
  condition = seq(1, min(c(x[["n_conditions"]], 9))),
  nrows = ceiling(sqrt(max(condition))),
  shareX = TRUE,
  shareY = TRUE,
  palette = "Dark 2",
  colors = NULL,
  font_family = "Times New Roman",
  font_size = 16,
  wrap_width = 25,
  showlegend = TRUE,
  label_subplots = TRUE,
  alpha = 1,
  margin = 0.05,
  ...
)

Arguments

x

Output of verify().

test

Integer vector of test numbers to plot. Combines with label and status as AND intersection.

vars

Variables to plot. Defaults to NULL to plot all variables.

show_constants

If TRUE, include constants in plot. Defaults to FALSE.

label

Character vector of regex patterns for partial, case-insensitive label matching. A test is included if its label matches any pattern.

ignore_case

Logical; whether label matching is case-insensitive. Default TRUE.

status

Optional character vector of statuses to include (e.g., c("fail", "error")). Defaults to all statuses.

  • which = "tests": filters rows by test status.

  • which = "sims": filters to conditions that have at least one test with a matching status.

condition

Integer vector of condition numbers to plot. Defaults to 1:9. If only one condition is specified, the plot will not be a grid of subplots.

nrows

Number of subplot rows. Defaults to ceiling(sqrt(condition)).

shareX

Share the x-axis across subplots. Defaults to TRUE.

shareY

Share the y-axis across subplots. Defaults to TRUE.

palette

Colour palette (see hcl.pals()). Defaults to "Dark 2".

colors

Vector of colours overriding palette. Defaults to NULL.

font_family

Font family. Defaults to "Times New Roman".

font_size

Font size. Defaults to 16.

wrap_width

Label wrap width. Defaults to 25.

showlegend

Whether to show the legend. Defaults to TRUE.

label_subplots

Whether to plot labels indicating the test number of the subplot.

alpha

Trajectory opacity. Defaults to 1.

margin

Margin between subplots. Either a single numeric or a vector of length four(left, right, top, bottom). See ?plotly::subplot() for more details. Defaults to 0.05.

...

Additional arguments passed to plot.simulate_stockflow().

Value

A plotly object.

See Also

verify(), plot.simulate_stockflow(), plot.ensemble_stockflow()

Examples

sfm <- stockflow("SIR") |>
  unit_test(expr = all(susceptible >= 0))
res <- verify(sfm)
plot(res)

Print comparison of two stock-and-flow models

Description

Print comparison of two stock-and-flow models

Usage

## S3 method for class 'compare_stockflow'
print(x, ...)

Arguments

x

An object of class compare_stockflow

...

Additional arguments (unused)

Value

Invisibly returns x.


Print simulation of a stock-and-flow model

Description

Prints the first rows of the simulation results in wide format. For a statistical summary per variable use summary().

Usage

## S3 method for class 'simulate_stockflow'
print(x, ...)

Arguments

x

A simulation result of class simulate_stockflow

...

Additional arguments (unused)

Value

Invisibly returns x

See Also

simulate.stockflow(), summary.simulate_stockflow(), plot.simulate_stockflow(), as.data.frame.simulate_stockflow()

Examples

sfm <- stockflow("SIR")
sim <- simulate(sfm)
print(sim)


Print overview of stock-and-flow model

Description

Prints a descriptive overview of the model structure, including stock-flow topology, variable names, and simulation settings. For model diagnostics, use summary().

Usage

## S3 method for class 'stockflow'
print(x, ...)

Arguments

x

A stock-and-flow model object of class stockflow

...

Additional arguments (unused)

Value

Invisibly returns x

See Also

summary.stockflow(), dependencies()

Examples

sfm <- stockflow("SIR")
print(sfm)


Print method for summary_stockflow

Description

Print method for summary_stockflow

Usage

## S3 method for class 'summary_stockflow'
print(x, ...)

Arguments

x

Object of class summary_stockflow

...

Ignored

Value

x invisibly


Create pulse function

Description

Create a pulse function that jumps from zero to a specified height at a specified time, and returns to zero after a specified width. The pulse can be repeated at regular intervals.

Usage

pulse(times, start, height = 1, width = 1, repeat_interval = NULL)

Arguments

times

Vector of simulation times

start

Start time of pulse in simulation time units.

height

Height of pulse. Defaults to 1.

width

Width of pulse in simulation time units. This cannot be equal to or less than 0. To indicate an instantaneous pulse, specify the simulation step size.

repeat_interval

Interval at which to repeat pulse. Defaults to NULL to indicate no repetition.

Details

Equivalent of Pulse() in Insight Maker

Value

Pulse interpolation function

See Also

step(), ramp(), seasonal()

Examples

# Create a simple model with a pulse function
# that starts at time 5, jumps to a height of 2
# with a width of 1, and does not repeat
sfm <- stockflow() |>
  update("a", "stock") |>
  # Specify the global variable "times" as simulation times
  update("input", "constant", eqn = "pulse(times, 5, 2, 1)") |>
  update("inflow", "flow", eqn = "input(t)", to = "a")



sim <- simulate(sfm, only_stocks = FALSE)
plot(sim)

# Create a pulse that repeats every 5 time units
sfm <- update(sfm, "input", eqn = "pulse(times, 5, 2, 1, 5)")

sim <- simulate(sfm, only_stocks = FALSE)
plot(sim)


Create ramp function

Description

Create a ramp function that increases linearly from 0 to a specified height at a specified start time, and stays at this height after the specified end time.

Usage

ramp(times, start, finish, height = 1)

Arguments

times

Vector of simulation times

start

Start time of ramp

finish

End time of ramp

height

End height of ramp, defaults to 1

Details

Equivalent of Ramp() in Insight Maker

Value

Ramp interpolation function

See Also

step(), pulse(), seasonal()

Examples

# Create a simple model with a ramp function
sfm <- stockflow() |>
  update("a", "stock") |>
  # Specify the global variable "times" as simulation times
  update("input", "constant", eqn = "ramp(times, 20, 30, 3)") |>
  update("inflow", "flow", eqn = "input(t)", to = "a")



sim <- simulate(sfm, only_stocks = FALSE)
plot(sim)

# To create a decreasing ramp, set the height to a negative value
sfm <- update(sfm, "input", eqn = "ramp(times, 20, 30, -3)")

sim <- simulate(sfm, only_stocks = FALSE)
plot(sim)


Generate random logical value

Description

Equivalent of RandBoolean() in Insight Maker

Usage

rbool(p)

Arguments

p

Probability of TRUE, numerical value between 0 and 1

Value

Logical value

Examples

rbool(.5)

Generate random number from custom distribution

Description

Equivalent of RandDist() in Insight Maker

Usage

rdist(a, b)

Arguments

a

Vector to draw sample from

b

Vector of probabilities

Value

One sample from custom distribution

Examples

rdist(c(1, 2, 3), c(.5, .25, .25))

Remainder and modulus

Description

Remainder and modulus operators. The modulus and remainder are not the same in case either a or b is negative. If you work with negative numbers, modulus is always non-negative (it matches the sign of the divisor).

Usage

rem(a, b)

mod(a, b)

a %REM% b

Arguments

a

Dividend

b

Divisor

Value

Remainder

Examples

# Modulus and remainder are the same when a and b are positive
a <- 7
b <- 3
rem(a, b)
mod(a, b)
# Modulus and remainder are NOT when either a or b is negative
a <- -7
b <- 3
rem(a, b)
mod(a, b)
a <- 7
b <- -3
rem(a, b)
mod(a, b)
# Modulus and remainder are the same when both a and b are negative
a <- -7
b <- -3
rem(a, b)
mod(a, b)

# Alternative way of computing the remainder:
a %REM% b

Round values half-up (as in Insight Maker)

Description

R rounds .5 to 0, whereas Insight Maker rounds .5 to 1. This function is the equivalent of Insight Maker's Round() function.

Usage

round_IM(x, digits = 0)

Arguments

x

Value

digits

Number of digits; optional, defaults to 0

Value

Rounded value

Examples

round_IM(.5) # 1
round(.5) # 0
round_IM(-0.5) # 0
round(-0.5) # 0
round_IM(1.5) # 2
round(1.5) # 2

Internal function to save data frame at specific times

Description

Internal function used to save the data frame at specific times in case save_at is not equal to dt in the simulation specifications.

Usage

saveat_func(df, time_col, new_times)

Arguments

df

data.frame in wide format

time_col

Name of the time column

new_times

Vector of new times to save the data frame at

Value

Interpolated data.frame. The data frame has columns time followed by one column per variable.

Examples

# Recommended: Use save_at in sim_settings() to downsample simulations
sfm <- stockflow("SIR") |> sim_settings(dt = 0.01, save_at = 1)
sim <- simulate(sfm)
df <- as.data.frame(sim)
nrow(df) # Returns only times at intervals of 1
head(df)

# The saveat_func() is the underlying function used by simulate()
# Direct use is not recommended, but shown here for completeness:
sfm <- sfm |> sim_settings(save_at = 0.01)
sim <- simulate(sfm)
df <- as.data.frame(sim)
nrow(df) # Many more rows

# Manual downsampling (not recommended - use save_at instead)
new_times <- seq(min(df$time), max(df$time), by = 1)
df_wide <- as.data.frame(sim, direction = "wide")
df_manual <- saveat_func(df_wide, "time", new_times)
nrow(df_manual)


Create a seasonal wave function

Description

Create a seasonal wave function that oscillates between -1 and 1, with a specified period and shift. The wave peaks at the specified shift time.

Usage

seasonal(times, period = 1, shift = 0)

Arguments

times

Vector of simulation times

period

Duration of wave in simulation time units. Defaults to 1.

shift

Timing of wave peak in simulation time units. Defaults to 0.

Details

Equivalent of Seasonal() in Insight Maker

Value

Seasonal interpolation function

See Also

step(), pulse(), ramp()

Examples

# Create a simple model with a seasonal wave
sfm <- stockflow() |>
  update("a", "stock") |>
  # Specify the global variable "times" as simulation times
  update("input", "constant", eqn = "seasonal(times, 10, 0)") |>
  update("inflow", "flow", eqn = "input(t)", to = "a")

sim <- simulate(sfm, only_stocks = FALSE)
plot(sim)


Translate between deSolve and DifferentialEquations.jl solver names

Description

Translate between deSolve and DifferentialEquations.jl solver names, or validate that a given solver name is recognized in either language. This is used internally to allow users to specify familiar R solvers when using Julia for simulation, and to provide warnings when an exact equivalent is not available.

Usage

sim_methods(method, from = NULL, to = NULL)

Arguments

method

Solver name to validate or translate.

from

Source solver family, either "R" or "Julia".

to

Target solver family when translating, either "R" or "Julia".

Value

A character scalar (validated or translated solver name), a character vector of solver names when method is omitted, or a named list of solver names for both languages when called with no arguments.

Examples

# List supported solvers
sim_methods()

# List supported R solvers
sim_methods(from = "R")

# List supported Julia solvers
sim_methods(from = "Julia")

# Validate or translate specific solvers
sim_methods("rk4", from = "R", to = "Julia")

Modify simulation specifications

Description

Simulation specifications are the settings that determine how the model is simulated, such as the integration method (i.e., solver), start and stop time, and timestep. Modify these specifications for an existing stock-and-flow model.

Usage

sim_settings(
  object,
  method = "euler",
  start = 0,
  stop = 100,
  dt = 0.01,
  save_at = 0.1,
  save_n = NULL,
  seed = NULL,
  time_units = "seconds",
  language = "R",
  only_stocks = TRUE,
  vars = NULL,
  keep_nonnegative_stock = FALSE,
  keep_nonnegative_flow = TRUE,
  save_sims = FALSE
)

Arguments

object

Stock-and-flow model, object of class stockflow.

method

Integration method. Defaults to "euler".

start

Start time of simulation. Defaults to 0.

stop

End time of simulation. Defaults to 100.

dt

Timestep of solver; controls simulation accuracy. Smaller = more accurate but slower. Defaults to 0.01.

save_at

Controls which time points are saved in the output. Either:

  • A single number: save every N time units (interval). Must be >= dt. Use larger than dt to reduce output size without sacrificing accuracy. Example: dt = 0.01, save_at = 1 saves every 100th computed point.

  • A numeric vector: explicit time points to include in output. Values must lie within ⁠[start, stop]⁠.

Pass NA, NULL, or "" to reset to saving all dt steps. Mutually exclusive with save_n. Defaults to NULL (save all).

save_n

Save exactly N evenly-spaced time points from start to stop. save_n = 1 saves only the final time point (stop). Pass NA, NULL, or "" to reset to saving all dt steps. Mutually exclusive with save_at. Defaults to NULL (save all).

seed

Seed number to ensure reproducibility across runs in case of random elements. Must be an integer. Defaults to NULL (no seed).

time_units

Simulation time unit. Defaults to "seconds".

language

Coding language in which to simulate model. Either "R" or "Julia". Defaults to "R".

only_stocks

If TRUE, only return stocks in output, discarding flows and auxiliaries. If FALSE, flows and auxiliaries are saved, which slows down the simulation. Defaults to TRUE.

vars

Character vector of variable names to save in simulation output. If specified, this overrides only_stocks.

keep_nonnegative_stock

If TRUE, keeps original non-negativity setting of stocks. Defaults to FALSE.

keep_nonnegative_flow

If TRUE, keeps original non-negativity setting of flows. Defaults to TRUE.

save_sims

If TRUE, individual simulations are retained in ensemble() output. Defaults to FALSE.

Value

A stock-and-flow model object of class stockflow

See Also

sim_methods()

Examples

sfm <- stockflow("predator_prey") |>
  sim_settings(start = 0, stop = 50, dt = 0.1)
sim <- simulate(sfm)
plot(sim)

# Change the simulation method to "rk4"
sfm <- sim_settings(sfm, method = "rk4")

# Change the time units to "years", such that one time unit is one year
sfm <- sim_settings(sfm, time_units = "years")

# Save at an interval to reduce output size without affecting accuracy
sfm <- sim_settings(sfm, save_at = 1)
sim <- simulate(sfm)
head(as.data.frame(sim))

# Save exactly 11 evenly-spaced time points (t=0, 5, 10, ..., 50)
sfm <- sim_settings(sfm, save_n = 11)
sim <- simulate(sfm)
head(as.data.frame(sim))

# Add stochastic initial condition but specify seed to obtain same result
sfm <- sim_settings(sfm, seed = 1) |>
  update(c(predator, prey), eqn = runif(1, 20, 50))


Simulate stock-and-flow model

Description

Simulate a stock-and-flow model with simulation specifications defined by sim_settings(). If sim_settings(language = "julia"), the Julia environment will first be set up with use_julia(). If any problems are detected by summary(), the model cannot be simulated.

Usage

## S3 method for class 'stockflow'
simulate(object, nsim = 1, seed = NULL, ...)

Arguments

object

Stock-and-flow model, object of class stockflow.

nsim

Number of simulations to run (unused; see ensemble() for running multiple simulations).

seed

Seed number to ensure reproducibility across runs in case of random elements. Must be an integer. Defaults to NULL (no seed).

...

Optional arguments passed to sim_settings(); these can be used to override the simulation specifications set in the model object.

Value

Object of class simulate_stockflow, a list containing:

object

Stock-and-flow model object of class stockflow

df

Data frame: simulation results (time, variable, value)

init

Named vector: initial stock values

constants

Named vector: constant parameters

script

Character: generated simulation code (R or Julia)

duration

Numeric: simulation time in seconds

success

Logical: TRUE if completed without errors

error_message

NULL if completed without errors

Use as.data.frame() to extract results, plot() to visualize.

See Also

update(), stockflow(), summary(), sim_settings(), use_julia()

Examples

sfm <- stockflow("SIR")
sim <- simulate(sfm)
plot(sim)

# Obtain all model variables
sim <- simulate(sim_settings(sfm, only_stocks = FALSE))
plot(sim, show_constants = TRUE)


Create step function

Description

Create a step function that jumps from zero to a specified height at a specified time, and remains at that height until the end of the simulation time.

Usage

step(times, start, height = 1)

Arguments

times

Vector of simulation times

start

Start time of step

height

Height of step, defaults to 1

Details

Equivalent of Step() in Insight Maker

Value

Step interpolation function

See Also

ramp(), pulse(), seasonal()

Examples

# Create a simple model with a step function
# that jumps at time 50 to a height of 5
sfm <- stockflow() |>
  update("a", "stock") |>
  # Specify the global variable "times" as simulation times
  update("input", "constant", eqn = "step(times, 50, 5)") |>
  update("inflow", "flow", eqn = "input(t)", to = "a")



sim <- simulate(sfm, only_stocks = FALSE)
plot(sim)

# Negative heights are also possible
sfm <- update(sfm, "input", eqn = "step(times, 50, -10)")

sim <- simulate(sfm, only_stocks = FALSE)
plot(sim)

Add or modify stocks

Description

Stocks accumulate material or information over time, defining the state of the system. stock() adds or changes a stock variable. This is a convenience wrapper around update() with type = "stock". See the Stocks section of update() for more details.

Usage

stock(object, name, eqn = 0, label = name, doc = "", non_negative = FALSE)

Arguments

object

Stock-and-flow model, object of class stockflow.

name

Variable name. Accepts a bare symbol (e.g., population), a string ("population"), or a vector via c() (e.g., c(a, b) or c("a", "b")). Use ⁠!!⁠ to inject from a variable.

eqn

Equation (or initial value in the case of stocks). Accepts a bare expression (e.g., a * b + 1), a string ("a * b + 1"), or a numeric value. Use ⁠!!⁠ to inject from a variable. Defaults to 0.

label

Name of variable used for plotting. Defaults to the same as name.

doc

Description of variable. Defaults to "" (no description).

non_negative

If TRUE, variable is enforced to be non-negative (i.e., strictly 0 or positive). Defaults to FALSE.

Value

A stock-and-flow model object of class stockflow

See Also

update(), discard(), change_name()

Examples


# Create a stock with an initial value
sfm <- stockflow() |>
  stock(population, eqn = 100, label = "Population")

# Multiple stocks
sfm <- stockflow() |>
  stock(susceptible, eqn = 999, label = "susceptible") |>
  stock(infected, eqn = 1, label = "infected") |>
  stock(recovered, eqn = 0, label = "recovered")


Create a new stock-and-flow model

Description

Initialize a stock-and-flow model of class stockflow. You can either create an empty stock-and-flow model or load a template from the model library.

Usage

stockflow(template = NULL)

Arguments

template

Name of the template to load. If NULL, an empty stock-and-flow model will be created with default simulation parameters and a default meta. If specified, template should be one of the available templates (case-insensitive):

  • logistic_model: Population growth with carrying capacity

  • sir: Epidemic model (Susceptible-Infected-Recovered)

  • predator_prey: Lotka-Volterra dynamics

  • cusp: Cusp catastrophe model

  • crielaard2022: Eating behavior (doi: 10.1037/met0000484)

  • coffee_cup: Temperature equilibration (Meadows)

  • bank_account: Compound interest (Meadows)

  • lorenz: Lorenz attractor (chaotic)

  • rossler: Rossler attractor (chaotic)

  • vanderpol: Van der Pol oscillator

  • duffing: Forced Duffing oscillator

  • chua: Chua's circuit (chaotic)

  • jdr: Job Demands-Resources Theory as formalized in Evers et al. (under review)

Details

Do not edit the object manually; this will likely lead to errors downstream. Rather, use meta(), sim_settings(), update(), and custom_func() for safe manipulation.

Value

A stock-and-flow model object of class stockflow. Its structure is based on XML Interchange Language for System Dynamics (XMILE). It is a nested list, containing:

meta

Meta-information about model. A list containing arguments listed in meta().

sim_settings

Simulation specifications. A list containing arguments listed in sim_settings().

model

Model variables, grouped under the variable types stock, flow, aux (auxiliaries), constant, gf (graphical functions), and func (custom functions). Each variable contains arguments as listed in update().

Use summary() to run model diagnostics, as.data.frame() to convert to a data.frame, plot() to visualize.

See Also

update(), meta(), custom_func(), sim_settings()

Examples

sfm <- stockflow()
summary(sfm)



# Load a template
sfm <- stockflow("lorenz")
sim <- simulate(sfm)
plot(sim)

Summarise simulation results

Description

Returns a data frame with per-variable summary statistics (min, mean, max, and final value) over the simulated time range.

Usage

## S3 method for class 'simulate_stockflow'
summary(object, ...)

Arguments

object

A simulation result of class simulate_stockflow

...

Additional arguments (unused)

Value

A data.frame with columns variable, min, mean, max, final.

See Also

print.simulate_stockflow(), simulate.stockflow()

Examples

sfm <- stockflow("SIR")
sim <- simulate(sfm)
summary(sim)


Run model diagnostics

Description

Check for common formulation problems in a stock-and-flow model.

Usage

## S3 method for class 'stockflow'
summary(object, ...)

Arguments

object

Stock-and-flow model, object of class stockflow.

...

Additional arguments (currently unused).

Details

The following problems are detected:

The following potential problems are detected:

Value

Object of class summary_stockflow. A flat named list with one entry per check. Each entry contains a problem field ("none", "warning", or "error") and type-specific data fields.

Examples

# No issues
sfm <- stockflow("SIR")
summary(sfm)

# Detect absence of stocks or flows
sfm <- stockflow()
summary(sfm)

# Detect stocks without inflows or outflows
sfm <- stockflow() |> update("Prey", "stock")
summary(sfm)

# Detect circularity in equation definitions
sfm <- stockflow() |>
  update("Prey", "stock", eqn = "Predator") |>
  update("Predator", "stock", eqn = "Prey")
summary(sfm)


Print last rows of a simulation

Description

Print the last rows of a simulation data frame of a stock-and-flow model. This is a wrapper around tail() that first converts the simulation results to a data frame using as.data.frame().

Usage

## S3 method for class 'simulate_stockflow'
tail(x, n = 6L, ...)

Arguments

x

Output of simulate().

n

Number of rows to print. Defaults to 6.

...

Other arguments passed to as.data.frame.simulate_stockflow().

Value

A data.frame with the last rows of the simulation results.

Examples

sfm <- stockflow("SIR")
sim <- simulate(sfm)
tail(sim)

Print last rows of verify results

Description

Wrapper around tail() that first converts the results to a data frame using as.data.frame.verify_stockflow().

Usage

## S3 method for class 'verify_stockflow'
tail(x, n = 6L, ...)

Arguments

x

A verify_stockflow object.

n

Number of rows. Defaults to 6.

...

Other arguments passed to as.data.frame.verify_stockflow().

Value

A data.frame.

Examples

sfm <- stockflow("SIR") |>
  unit_test(expr = all(susceptible >= 0))
res <- verify(sfm)
tail(res)

Add or modify unit tests

Description

Unit tests are assertions about model behavior that can be evaluated against simulation results. For example, you might assert that a stock remains non-negative, or that a certain variable reaches a threshold by the end of the simulation. Unit tests can be added to a model such that they can be evaluated with verify(). All unit tests can be displayed with unit_tests().

Usage

unit_test(object, test, expr, label, conditions = list(), active = TRUE)

Arguments

object

Stock-and-flow model, object of class stockflow.

test

Integer number of the test to modify. Must be a positive integer (a warning is issued and the value rounded when a non-integer is supplied). When test exceeds the current number of tests a warning is issued and a new test is appended instead. Can be omitted when adding a new test.

expr

An expression to evaluate against simulation results. Variable names in the expression refer to model variables; each resolves to a numeric vector of time-series values. Required when adding a new test; optional when modifying (keeps the current expression if omitted).

label

A descriptive label for the test. If omitted when adding, auto-generated from expr. If omitted when modifying, the current label is kept. Labels must be unique.

conditions

A named list of constant or initial stock overrides used when evaluating this test. If non-empty, verify.stockflow() will re-simulate the model with these parameter values before evaluating expr.

active

If FALSE, the test is defined but skipped during verify(). Defaults to TRUE.

Details

The expr argument accepts a plain logical expression:

When label is omitted, a human-readable label is generated automatically by parsing the expression (e.g., all(S >= 0)"S is at least 0 (for all values)").

Value

The model object with the unit test added or modified, invisibly.

Adding vs. modifying

When modifying, only the arguments you explicitly supply are changed; all other fields keep their current value.

Uniqueness

Labels must be unique across all unit tests. An error is thrown if a new or modified label would create a duplicate. Expressions must also be unique; an error is thrown if an identical expr already exists on another test.

See Also

verify(), unit_tests(), discard_unit_test()

Examples

sfm <- stockflow("SIR") |>
  unit_test(expr = all(susceptible >= 0))

# Run unit tests
verify(sfm)

# Add test with label
sfm <- unit_test(sfm,
  label = "recovered increases",
  expr = all(diff(recovered) >= 0)
)
verify(sfm)

# Add test with conditions
sfm <- unit_test(sfm,
  expr = all(infected == infected[1]),
  label = "When infection_rate is zero, no one gets infected",
  conditions = list(infection_rate = 0)
)
verify(sfm)

# View all tests
unit_tests(sfm)

# Deactivate test test 1
sfm <- unit_test(sfm, test = 1, active = FALSE)
verify(sfm)

# Modify test by label, e.g., to change the expression
sfm <- unit_test(sfm,
  label = "recovered increases over time",
  expr = all(diff(recovered) > -1)
)
verify(sfm)


Display unit tests defined on a stock-and-flow model

Description

Returns an overview of all unit tests attached to the model. The result has a print() method.

Usage

unit_tests(object, test = NULL, label = NULL, ignore_case = TRUE)

Arguments

object

Stock-and-flow model, object of class stockflow.

test

Integer vector of test number(s) to display (1-based). Defaults to NULL (show all tests). Can be combined with label (intersection).

label

Character vector of regex patterns for partial, case-insensitive label matching. A test is included if its label matches any pattern. E.g., c("non-neg", "beta") returns tests matching either fragment. Can be combined with test (intersection).

ignore_case

Logical; whether label matching is case-insensitive. Default TRUE.

Value

An object of class unit_tests_stockflow, printed automatically.

See Also

unit_test(), verify()

Examples

sfm <- stockflow("SIR") |>
  unit_test(expr = all(susceptible >= 0)) |>
  unit_test(
    label = "recovered increases over time",
    expr = all(diff(recovered) >= 0)
  )

unit_tests(sfm)
unit_tests(sfm, test = 1L)
unit_tests(sfm, label = "increases")

Create or modify variables

Description

Add or change variables in a stock-and-flow model. Variables may be stocks, flows, constants, auxiliaries, or graphical functions. When creating new variables, only "name", "type", and "eqn" (initial value for stocks) are required. When modifying existing variables, only "name" is required to identify the variable to modify, and any other properties can be updated by including the corresponding arguments.

Usage

## S3 method for class 'stockflow'
update(
  object,
  name,
  type = NULL,
  eqn = 0,
  label = name,
  doc = "",
  to = NULL,
  from = NULL,
  non_negative = FALSE,
  xpts = NULL,
  ypts = NULL,
  source = NULL,
  interpolation = "linear",
  extrapolation = "nearest",
  df = NULL,
  ...
)

Arguments

object

Stock-and-flow model, object of class stockflow.

name

Variable name. Accepts a bare symbol (e.g., population), a string ("population"), or a vector via c() (e.g., c(a, b) or c("a", "b")). Use ⁠!!⁠ to inject from a variable.

type

Type of building block(s); accepts a bare symbol or string. One of stock, flow, constant, aux, lookup, or func. Does not need to be specified to modify an existing variable.

eqn

Equation (or initial value in the case of stocks). Accepts a bare expression (e.g., a * b + 1), a string ("a * b + 1"), or a numeric value. Use ⁠!!⁠ to inject from a variable. Defaults to 0.

label

Name of variable used for plotting. Defaults to the same as name.

doc

Description of variable. Defaults to "" (no description).

to

Target of flow. Accepts a bare symbol or string. Must be a stock in the model. Defaults to NULL to indicate no target.

from

Source of flow. Accepts a bare symbol or string. Must be a stock in the model. Defaults to NULL to indicate no source.

non_negative

If TRUE, variable is enforced to be non-negative (i.e., strictly 0 or positive). Defaults to FALSE.

xpts

Only for graphical functions: vector of x-domain points. Must be of the same length as ypts.

ypts

Only for graphical functions: vector of y-domain points. Must be of the same length as xpts.

source

Only for graphical functions: name of the variable which will serve as the input to the graphical function. Accepts a bare symbol or string. Defaults to NULL.

interpolation

Only for graphical functions: interpolation method. Must be either "constant" or "linear". Defaults to "linear".

extrapolation

Only for graphical functions: extrapolation method. Must be either "nearest" or "NA". Defaults to "nearest".

df

A data.frame with variable properties to add and/or modify. Each row represents one variable to update. Required columns depend on the variable type being created:

  • All types require: 'type', 'name'

  • Stocks require: 'eqn' (initial value)

  • Flows require: 'eqn', and at least one of 'from' or 'to'

  • Constants require: 'eqn'

  • Auxiliaries require: 'eqn'

  • Graphical functions require: 'xpts', 'ypts'

Optional columns for all types: 'label', 'doc', 'non_negative' Optional columns for graphical functions: 'source', 'interpolation', 'extrapolation'

Columns not applicable to a variable type should be set to NA. See Examples for a complete demonstration.

...

Additional arguments (currently unused).

Value

A stock-and-flow model object of class stockflow

Stocks

Stocks define the state of the system. They accumulate material or information over time, such as people, products, or beliefs, which creates memory and inertia in the system. As such, stocks need not be tangible. Stocks are variables that can increase and decrease, and can be measured at a single moment in time. The value of a stock is increased or decreased by flows. A stock may have multiple inflows and multiple outflows. The net change in a stock is the sum of its inflows minus the sum of its outflows.

The obligatory properties of a stock are "name", "type", and "eqn". Optional additional properties are "label", "doc", "non_negative".

Flows

Flows move material and information through the system. Stocks can only decrease or increase through flows. A flow must flow from and/or flow to a stock. If a flow is not flowing from a stock, the source of the flow is outside of the model boundary. Similarly, if a flow is not flowing to a stock, the destination of the flow is outside the model boundary. Flows are defined in units of material or information moved over time, such as birth rates, revenue, and sales.

The obligatory properties of a flow are "name", "type", "eqn", and either "from", "to", or both. Optional additional properties are "label", "doc", "non_negative".

Constants

Constants are variables that do not change over the course of the simulation - they are time-independent. These may be numbers, but also functions. They can depend only on other constants.

The obligatory properties of a constant are "name", "type", and "eqn". Optional additional properties are "label", "doc", "non_negative".

Auxiliaries

Auxiliaries are dynamic variables that change over time. They are used for intermediate calculations in the system, and can depend on other flows, auxiliaries, constants, and stocks.

The obligatory properties of an auxiliary are "name", "type", and "eqn". Optional additional properties are "label", "doc", "non_negative".

Graphical functions

Graphical functions, also known as table or lookup functions, are interpolation functions used to define the desired output (y) for a specified input (x). They are defined by a set of x- and y-domain points, which are used to create a piecewise linear function. The interpolation method defines the behavior of the graphical function between x-points ("constant" to return the value of the previous x-point, "linear" to linearly interpolate between defined x-points), and the extrapolation method defines the behavior outside of the x-points ("NA" to return NA values outside of defined x-points, "nearest" to return the value of the closest x-point).

The obligatory properties of a graphical function are "name", "type", "xpts", and "ypts". "xpts" and "ypts" must be of the same length. Optional additional properties are "label", "doc", "source", "interpolation", "extrapolation".

Non-standard evaluation (NSE)

The name, type, eqn, to, from, and source arguments support non-standard evaluation. This means you can pass bare symbols and expressions instead of quoted strings:

# These are equivalent:
stock(sfm, "population", eqn = "birth_rate * 0.1")
stock(sfm, population, eqn = birth_rate * 0.1)

To inject the value of a variable (rather than its name), use the ⁠!!⁠ (bang-bang) operator from rlang:

my_name <- "population"
stock(sfm, !!my_name, eqn = 100)

The label, doc, non_negative, xpts, ypts, interpolation, and extrapolation arguments are not affected by NSE and are evaluated normally.

See Also

stockflow() to initialize a model, simulate() to simulate a model, and summary() to run model diagnostics. Variable-specific helper functions stock(), flow(), constant(), aux(), and lookup() are also available as wrappers around update() that set the "type" argument for convenience. Further helper functions for modifying models are change_name() to rename a variable, change_type() to change a variable's type, and discard() to remove a variable.

Examples


# First initialize an empty model
sfm <- stockflow()
print(sfm)


# Add two stocks. Specify their initial values in the "eqn" property
# and their plotting label.
sfm <- stock(sfm, predator, eqn = 10, label = "Predator") |>
  stock(prey, eqn = 50, label = "Prey")


# Add four flows: the births and deaths of both the predators and prey. The
# "eqn" property of flows represents the rate of the flow. In addition, we
# specify which stock the flow is coming from ("from") or flowing to ("to").
sfm <- flow(sfm, predator_births,
  eqn = delta * prey * predator,
  label = "Predator Births", to = predator
) |>
  flow(predator_deaths,
    eqn = gamma * predator,
    label = "Predator Deaths", from = predator
  ) |>
  flow(prey_births,
    eqn = alpha * prey,
    label = "Prey Births", to = prey
  ) |>
  flow(prey_deaths,
    eqn = beta * prey * predator,
    label = "Prey Deaths", from = prey
  )
plot(sfm)

# The flows make use of four other variables: "delta", "gamma", "alpha", and
# "beta". Define these as constants in a vectorized manner for efficiency.
sfm <- constant(sfm, c(delta, gamma, alpha, beta),
  eqn = c(.025, .5, .5, .05),
  label = c("Delta", "Gamma", "Alpha", "Beta"),
  doc = c(
    "Birth rate of predators", "Death rate of predators",
    "Birth rate of prey", "Death rate of prey by predators"
  )
)

# We now have a complete predator-prey model which is ready to be simulated.
sim <- simulate(sfm)
plot(sim)

# Modify a variable - note that we no longer need to specify type
sfm <- update(sfm, delta, eqn = .03, label = "DELTA")

# To add and/or modify variables more quickly, pass a data.frame.
# The data.frame is processed per row.
# For instance, to create a logistic population growth model:
df <- data.frame(
  type = c("stock", "flow", "flow", "constant", "constant"),
  name = c("X", "inflow", "outflow", "r", "K"),
  eqn = c(.01, "r * X", "r * X^2 / K", 0.1, 1),
  label = c(
    "Population size", "Births", "Deaths", "Growth rate",
    "Carrying capacity"
  ),
  to = c(NA, "X", NA, NA, NA),
  from = c(NA, NA, "X", NA, NA)
)
sfm <- update(stockflow(), df = df)

# Run model diagnostics
summary(sfm)

# --- Programmatic usage ---

# To inject the value of an R variable, use !! (bang-bang)
my_name <- "growth"
sfm <- constant(sfm, !!my_name, eqn = 0.1)

# Strings also work
sfm <- constant(sfm, "growth", eqn = 0.2)


Extract Insight Maker model from URL

Description

Create XML string from Insight Maker URL. For internal use; use import_insightmaker() to import an Insight Maker model.

Usage

url_to_insightmaker(url, file = NULL)

Arguments

url

String with URL to an Insight Maker model

file

If specified, file path to save Insight Maker model to. If NULL, do not save model.

Value

XML string with Insight Maker model

See Also

import_insightmaker()

Examples


url <- "https://insightmaker.com/insight/43tz1nvUgbIiIOGSGtzIzj/Romeo-Juliet"
xml <- url_to_insightmaker(url)

# Save model to file
file <- tempfile(fileext = ".InsightMaker")
xml <- url_to_insightmaker(url, file = file)
file.remove(file)


Start Julia and activate environment

Description

Start Julia session and activate Julia environment to simulate stock-and-flow models. To do so, Julia needs to be installed (see https://julialang.org/install/) and findable from within R. See this vignette for guidance. In addition, the Julia environment specifically for sdbuildR needs to have been instantiated. This can be set up with install_julia_env().

Usage

use_julia(stop = FALSE, restart = FALSE, nthreads = NULL)

Arguments

stop

If TRUE, stop active Julia session. Defaults to FALSE.

restart

If TRUE, force Julia session to restart.

nthreads

If not NULL, set the number of threads for Julia to use. This will temporarily set the environment variable JULIA_NUM_THREADS and restart Julia if it is already running to apply the new thread setting. See this page for more details on threading in Julia.

Details

In every R session, use_julia() needs to be run once (which is done automatically in simulate()), which can take around 30-60 seconds.

Value

Returns NULL invisibly, used for side effects

See Also

install_julia_env()

Examples


# Start a Julia session and activate the Julia environment for sdbuildR
use_julia()

# Start Julia with 2 threads (only works if threading is supported)
use_julia(nthreads = 2)

# Restart Julia session (in case of issues)
use_julia(restart = TRUE)

# Stop Julia session
use_julia(stop = TRUE)


Verify model behavior with unit tests

Description

Verify model behavior with unit tests

Usage

verify(object, ...)

Arguments

object

A model object to verify.

...

Additional arguments passed to specific methods.


Verify unit tests against simulation results

Description

Run all active unit tests defined on a stock-and-flow model. Use unit_test() to define tests; use unit_tests() to display them.

Usage

## S3 method for class 'stockflow'
verify(object, test = NULL, ...)

Arguments

object

An stockflow object.

test

Integer vector of test number(s) to run (numbers-based, as shown by unit_tests()). Defaults to NULL (run all tests).

...

Additional arguments passed to sim_settings() (e.g., seed, dt).

Details

Calling verify() on a stockflow model will first simulate the model, then run all tests — including those that require re-simulation under alternative conditions. Simulations are always retained in the returned object so that plot.verify_stockflow() works without any extra arguments.

For repeated-run robustness testing use ensemble() instead.

Value

An object of class verify_stockflow, returned invisibly. Use as.data.frame() to extract results as a data frame and plot() to visualize the simulations used. The object contains:

results

List of test result entries, one per test (including inactive tests, which appear with status = "skip"). Each entry has label, expr_str, conditions, status, error_type, message, and outcome.

object

The stockflow model the tests were run against.

sims

Nested list of simulate_stockflow objects used internally by plot.verify_stockflow(). Always present (never NULL).

j

Named integer vector mapping each test label to its condition index. Used internally by plot.verify_stockflow().

n

Number of simulations run per condition.

n_conditions

Number of unique simulation conditions.

test_indices

Integer vector of the original 1-based test numbers that were run (as shown by unit_tests()). Equal to seq_along(results) when test = NULL (all tests run).

See Also

unit_test(), unit_tests(), simulate.stockflow(), as.data.frame.verify_stockflow(), plot.verify_stockflow()

Examples

sfm <- stockflow("SIR") |>
  unit_test(expr = all(susceptible >= 0)) |>
  unit_test(
    label = "recovered increases over time",
    expr = all(diff(recovered) >= 0)
  )

verify(sfm)