System Dynamics modeling library for Python that integrates with data science tools
—
PySD's stateful components maintain state between simulation time steps, implementing core System Dynamics structures like stocks (integrations), delays, smoothing functions, and forecasting components.
Foundation classes that provide state management capabilities for model components.
class Stateful:
"""
Base class for stateful objects.
Provides basic state management functionality including initialization,
state updates, and memory management for model components that maintain
state between time steps.
"""
class DynamicStateful(Stateful):
"""
Base for dynamically updating stateful objects.
Extends Stateful with capabilities for dynamic state updates,
dependency tracking, and integration with the simulation engine.
"""Core stock and flow components that perform integration over time.
class Integ(DynamicStateful):
"""
Integration/stock elements.
Implements System Dynamics stocks (levels) that accumulate flows over time.
Performs numerical integration using specified integration method.
Methods:
- __init__(lambda_function, initial_value, lambda_init_function=None)
- __call__(time) - Get current integrated value
- init(time, initial_value) - Initialize with specific value
- update(time) - Update integration state for current time step
"""
class NonNegativeInteg(Integ):
"""
Non-negative integration.
Integration component that ensures the integrated value never goes below zero,
useful for physical quantities like population, inventory, or resources.
Inherits all Integ methods with additional constraint enforcement.
"""from pysd.py_backend.statefuls import Integ, NonNegativeInteg
# Create basic integration component
def population_flows():
return births_per_time_step - deaths_per_time_step
population = Integ(population_flows, initial_value=1000)
# Non-negative integration for physical quantities
def inventory_flows():
return production_rate - consumption_rate
inventory = NonNegativeInteg(inventory_flows, initial_value=500)
# Access current values during simulation
current_population = population(current_time)
current_inventory = inventory(current_time)Components that implement various types of delays commonly used in System Dynamics models.
class Delay(DynamicStateful):
"""
Variable delay functions.
Implements first-order variable delay where delay time can change dynamically.
Models processes where items spend variable time in the delay.
Methods:
- __init__(delay_input, delay_time, initial_value, order=1)
- __call__(time) - Get delayed output value
- init(time, initial_value) - Initialize delay chain
"""
class DelayN(DynamicStateful):
"""
Nth order delay.
Implements higher-order delays by chaining multiple first-order delays.
Provides more realistic delay behavior with gradual buildup and decay.
Methods:
- __init__(delay_input, delay_time, initial_value, order=3)
- __call__(time) - Get delayed output value
"""
class DelayFixed(DynamicStateful):
"""
Fixed delay.
Implements pipeline delay with fixed delay time. Items enter and exit
after exactly the specified delay time (FIFO queue behavior).
Methods:
- __init__(delay_input, delay_time, initial_value)
- __call__(time) - Get delayed output value
"""from pysd.py_backend.statefuls import Delay, DelayN, DelayFixed
# Variable delay for information processing
def information_input():
return new_information_rate
def processing_time():
return base_processing_time * complexity_factor
information_delay = Delay(
delay_input=information_input,
delay_time=processing_time,
initial_value=0,
order=1
)
# Third-order delay for material flow
material_delay = DelayN(
delay_input=lambda: production_rate,
delay_time=lambda: 5.0, # 5 time units
initial_value=100,
order=3
)
# Fixed pipeline delay for manufacturing
manufacturing_delay = DelayFixed(
delay_input=lambda: orders_received,
delay_time=lambda: 7.0, # Exactly 7 time units
initial_value=50
)
# Get delayed values during simulation
processed_info = information_delay(current_time)
materials_out = material_delay(current_time)
finished_goods = manufacturing_delay(current_time)Components for smoothing noisy data and generating forecasts.
class Smooth(DynamicStateful):
"""
Smoothing functions.
Implements exponential smoothing (first-order delay applied to input).
Reduces noise and volatility in model variables.
Methods:
- __init__(smooth_input, smooth_time, initial_value)
- __call__(time) - Get smoothed output value
"""
class Trend(DynamicStateful):
"""
Trend calculation.
Calculates the trend (rate of change) of input variable using
exponential smoothing of the derivative.
Methods:
- __init__(trend_input, trend_time, initial_trend=0)
- __call__(time) - Get current trend value
"""
class Forecast(DynamicStateful):
"""
Forecasting functions.
Generates forecasts by extrapolating current value and trend.
Combines exponential smoothing with trend analysis.
Methods:
- __init__(forecast_input, average_time, horizon_time, initial_trend=0)
- __call__(time) - Get forecasted value
"""from pysd.py_backend.statefuls import Smooth, Trend, Forecast
# Smooth noisy sales data
def raw_sales():
return daily_sales_with_noise
smoothed_sales = Smooth(
smooth_input=raw_sales,
smooth_time=5.0, # 5-day smoothing
initial_value=1000
)
# Calculate trend in market growth
def market_size():
return current_market_size
market_trend = Trend(
trend_input=market_size,
trend_time=10.0, # 10-period trend calculation
initial_trend=0.05 # 5% initial growth rate
)
# Forecast future demand
demand_forecast = Forecast(
forecast_input=lambda: current_demand,
average_time=8.0, # 8-period averaging
horizon_time=12.0, # 12-period forecast horizon
initial_trend=0.02
)
# Access values during simulation
current_smooth_sales = smoothed_sales(current_time)
current_trend = market_trend(current_time)
future_demand = demand_forecast(current_time)Components for conditional sampling and initial value handling.
class SampleIfTrue(DynamicStateful):
"""
Conditional sampling.
Samples and holds input value when condition is true, maintaining
the last sampled value when condition is false.
Methods:
- __init__(sample_input, condition, initial_value)
- __call__(time) - Get current sampled value (held if condition false)
"""
class Initial(Stateful):
"""
Initial value storage.
Stores and provides access to initial values of model variables.
Used for reference and reset operations.
Methods:
- __init__(initial_function)
- __call__(time) - Get initial value
"""from pysd.py_backend.statefuls import SampleIfTrue, Initial
# Sample price when market is open
def current_price():
return stock_price
def market_open():
return trading_hours_active
sampled_price = SampleIfTrue(
sample_input=current_price,
condition=market_open,
initial_value=100.0
)
# Store initial population for comparison
def initial_population():
return starting_population_value
population_initial = Initial(initial_population)
# Use during simulation
price_when_open = sampled_price(current_time)
baseline_population = population_initial(current_time)All stateful components provide common state management functionality:
# Components can be initialized with specific values
component.init(time=0, initial_value=500)
# Or use default initialization
component.__init__(input_function, initial_value=100)# Components automatically update during simulation
# Manual update if needed:
component.update(current_time)
# Access current state
current_value = component(current_time)Stateful components automatically manage their internal memory:
Stateful components are typically created automatically when models are translated from Vensim/XMILE, but can also be used directly:
import pysd
from pysd.py_backend.statefuls import Integ, Smooth
# In translated models, stateful components are created automatically
model = pysd.read_vensim('model_with_stocks.mdl')
results = model.run()
# Direct usage for custom components
def custom_flow():
return some_calculation()
custom_stock = Integ(custom_flow, initial_value=1000)
# Use in custom simulation loop
time_step = 0.25
current_time = 0
while current_time <= 50:
stock_value = custom_stock(current_time)
print(f"Time {current_time}: Stock = {stock_value}")
custom_stock.update(current_time)
current_time += time_stepStateful components include robust error handling:
try:
problem_delay = Delay(input_func, delay_time=lambda: -1, initial_value=0)
except ValueError as e:
print(f"Invalid delay configuration: {e}")Stateful components are optimized for simulation performance:
For large models with many stateful components, consider:
Install with Tessl CLI
npx tessl i tessl/pypi-pysd