System Dynamics modeling library for Python that integrates with data science tools
—
PySD's Model class provides comprehensive capabilities for setting model parameters, managing initial conditions, and accessing model state. Parameters can be constants, time series data, or callable functions.
Modify model parameters and variables with support for constants, time series, and function values.
def set_components(self, params):
"""
Set values of model parameters/variables.
Parameters:
- params: dict - Parameter names and values where values can be:
- Scalar constants (int, float)
- pandas.Series for time series data (index must be time values)
- Callable functions that accept time and return scalar values
- NumPy arrays for subscripted variables
Raises:
KeyError: If parameter name doesn't exist in model
ValueError: If value type/shape doesn't match parameter requirements
TypeError: If callable function has incorrect signature
"""Setting constant parameters:
import pysd
import pandas as pd
import numpy as np
model = pysd.read_vensim('population_model.mdl')
# Set constant values
model.set_components({
'birth_rate': 0.05,
'death_rate': 0.02,
'initial_population': 1000,
'carrying_capacity': 50000
})
results = model.run()Setting time series parameters:
# Create time series data
time_index = pd.Series(range(0, 51))
birth_rate_series = pd.Series(
np.linspace(0.05, 0.02, 51), # Declining birth rate over time
index=time_index
)
model.set_components({
'birth_rate': birth_rate_series,
'migration_rate': pd.Series([0.01] * 51, index=time_index)
})
results = model.run()Setting function parameters:
# Define custom functions
def seasonal_birth_rate(time):
"""Birth rate that varies seasonally."""
base_rate = 0.04
seasonal_variation = 0.01 * np.sin(2 * np.pi * time)
return base_rate + seasonal_variation
def policy_intervention(time):
"""Policy that changes at specific time."""
return 0.03 if time < 25 else 0.015
model.set_components({
'birth_rate': seasonal_birth_rate,
'death_rate': policy_intervention
})
results = model.run()Retrieve current values of model variables and access historical data.
def __getitem__(self, param):
"""
Get current value of model component using bracket notation.
Parameters:
- param: str - Name of model variable/parameter
Returns:
float or numpy.ndarray: Current value of the specified component
Raises:
KeyError: If parameter name doesn't exist in model
"""
def get_series_data(self, param):
"""
Get original data from lookup/data components.
Parameters:
- param: str - Name of lookup or data component
Returns:
pandas.Series or numpy.ndarray: Original data series
Raises:
KeyError: If parameter name doesn't exist
TypeError: If parameter is not a data/lookup component
"""model = pysd.read_vensim('population_model.mdl')
# Get current values
current_population = model['Population']
current_birth_rate = model['birth_rate']
print(f"Current population: {current_population}")
print(f"Current birth rate: {current_birth_rate}")
# Access original lookup table data
original_lookup = model.get_series_data('GDP_lookup_table')
print(original_lookup)Access model metadata, structure, and dependency information.
@property
def doc(self):
"""
Model documentation as pandas DataFrame.
Returns:
pandas.DataFrame: Documentation with columns for variable names,
units, limits, subscripts, types, and descriptions
"""
@property
def namespace(self):
"""
Model namespace dictionary.
Returns:
dict: Mapping of model variable names to Python function names
"""
@property
def dependencies(self):
"""
Model dependencies dictionary.
Returns:
dict: Variable dependency relationships
"""
@property
def subscripts(self):
"""
Model subscripts dictionary.
Returns:
dict: Variable subscript definitions and ranges
"""model = pysd.read_vensim('complex_model.mdl')
# View model documentation
print(model.doc)
# Check available variables
print("Available variables:", list(model.namespace.keys()))
# Examine variable dependencies
print("Dependencies for Population:", model.dependencies.get('population', []))
# Check subscripted variables
print("Subscripts:", model.subscripts)Get detailed information about model components including arguments and coordinates.
def get_args(self, param):
"""
Get function arguments for a model element.
Parameters:
- param: str - Name of model element
Returns:
list: Function argument names
Raises:
KeyError: If parameter name doesn't exist
"""
def get_coords(self, param):
"""
Get coordinates and dimensions of a model element.
Parameters:
- param: str - Name of model element
Returns:
dict: Coordinate information including dimensions and subscript ranges
Raises:
KeyError: If parameter name doesn't exist
"""# Check function arguments
args = model.get_args('complex_calculation')
print(f"Arguments for complex_calculation: {args}")
# Get coordinate information for subscripted variables
coords = model.get_coords('population_by_age_group')
print(f"Coordinates: {coords}")Export and import model state for reproducible analysis and checkpointing.
def export(self, file_name):
"""
Export model state to pickle file.
Parameters:
- file_name: str - Path to output pickle file
Raises:
IOError: If file cannot be written
"""
def import_pickle(self, file_name):
"""
Import model state from pickle file.
Parameters:
- file_name: str - Path to pickle file
Raises:
IOError: If file cannot be read
pickle.UnpicklingError: If file is corrupted or incompatible
"""# Set up model state
model.set_components({'birth_rate': 0.05, 'death_rate': 0.02})
model.run(final_time=25) # Run to midpoint
# Save state
model.export('midpoint_state.pkl')
# Later, restore state
model.import_pickle('midpoint_state.pkl')
# Continue simulation from saved state
final_results = model.run(final_time=50)Handle multi-dimensional variables with subscripts (arrays).
# Set subscripted parameter values
model.set_components({
'population_by_region': np.array([1000, 1500, 800, 1200]), # 4 regions
'birth_rate_by_age': {
'young': 0.06,
'middle': 0.04,
'old': 0.01
}
})
# Access subscripted values
regional_pop = model['population_by_region']
print(f"Population by region: {regional_pop}")
# Get specific subscript element
young_birth_rate = model['birth_rate_by_age']['young']PySD performs validation when setting parameters:
try:
model.set_components({'invalid_param': 100})
except KeyError:
print("Parameter name not found in model")
try:
model.set_components({'birth_rate': 'invalid_value'})
except ValueError:
print("Invalid parameter value type")Load parameter values from external data sources:
# Load from CSV
data = pd.read_csv('historical_data.csv', index_col='time')
model.set_components({
'birth_rate': data['births'],
'death_rate': data['deaths'],
'migration_rate': data['migration']
})
# Load from Excel with multiple sheets
excel_data = pd.read_excel('model_inputs.xlsx', sheet_name='parameters', index_col='time')
model.set_components(excel_data.to_dict('series'))Install with Tessl CLI
npx tessl i tessl/pypi-pysd