0
# Stateful Model Components
1
2
PySD's stateful components maintain state between simulation time steps, implementing core System Dynamics structures like stocks (integrations), delays, smoothing functions, and forecasting components.
3
4
## Capabilities
5
6
### Base Stateful Classes
7
8
Foundation classes that provide state management capabilities for model components.
9
10
```python { .api }
11
class Stateful:
12
"""
13
Base class for stateful objects.
14
15
Provides basic state management functionality including initialization,
16
state updates, and memory management for model components that maintain
17
state between time steps.
18
"""
19
20
class DynamicStateful(Stateful):
21
"""
22
Base for dynamically updating stateful objects.
23
24
Extends Stateful with capabilities for dynamic state updates,
25
dependency tracking, and integration with the simulation engine.
26
"""
27
```
28
29
### Integration Components
30
31
Core stock and flow components that perform integration over time.
32
33
```python { .api }
34
class Integ(DynamicStateful):
35
"""
36
Integration/stock elements.
37
38
Implements System Dynamics stocks (levels) that accumulate flows over time.
39
Performs numerical integration using specified integration method.
40
41
Methods:
42
- __init__(lambda_function, initial_value, lambda_init_function=None)
43
- __call__(time) - Get current integrated value
44
- init(time, initial_value) - Initialize with specific value
45
- update(time) - Update integration state for current time step
46
"""
47
48
class NonNegativeInteg(Integ):
49
"""
50
Non-negative integration.
51
52
Integration component that ensures the integrated value never goes below zero,
53
useful for physical quantities like population, inventory, or resources.
54
55
Inherits all Integ methods with additional constraint enforcement.
56
"""
57
```
58
59
#### Usage Examples
60
61
```python
62
from pysd.py_backend.statefuls import Integ, NonNegativeInteg
63
64
# Create basic integration component
65
def population_flows():
66
return births_per_time_step - deaths_per_time_step
67
68
population = Integ(population_flows, initial_value=1000)
69
70
# Non-negative integration for physical quantities
71
def inventory_flows():
72
return production_rate - consumption_rate
73
74
inventory = NonNegativeInteg(inventory_flows, initial_value=500)
75
76
# Access current values during simulation
77
current_population = population(current_time)
78
current_inventory = inventory(current_time)
79
```
80
81
### Delay Components
82
83
Components that implement various types of delays commonly used in System Dynamics models.
84
85
```python { .api }
86
class Delay(DynamicStateful):
87
"""
88
Variable delay functions.
89
90
Implements first-order variable delay where delay time can change dynamically.
91
Models processes where items spend variable time in the delay.
92
93
Methods:
94
- __init__(delay_input, delay_time, initial_value, order=1)
95
- __call__(time) - Get delayed output value
96
- init(time, initial_value) - Initialize delay chain
97
"""
98
99
class DelayN(DynamicStateful):
100
"""
101
Nth order delay.
102
103
Implements higher-order delays by chaining multiple first-order delays.
104
Provides more realistic delay behavior with gradual buildup and decay.
105
106
Methods:
107
- __init__(delay_input, delay_time, initial_value, order=3)
108
- __call__(time) - Get delayed output value
109
"""
110
111
class DelayFixed(DynamicStateful):
112
"""
113
Fixed delay.
114
115
Implements pipeline delay with fixed delay time. Items enter and exit
116
after exactly the specified delay time (FIFO queue behavior).
117
118
Methods:
119
- __init__(delay_input, delay_time, initial_value)
120
- __call__(time) - Get delayed output value
121
"""
122
```
123
124
#### Usage Examples
125
126
```python
127
from pysd.py_backend.statefuls import Delay, DelayN, DelayFixed
128
129
# Variable delay for information processing
130
def information_input():
131
return new_information_rate
132
133
def processing_time():
134
return base_processing_time * complexity_factor
135
136
information_delay = Delay(
137
delay_input=information_input,
138
delay_time=processing_time,
139
initial_value=0,
140
order=1
141
)
142
143
# Third-order delay for material flow
144
material_delay = DelayN(
145
delay_input=lambda: production_rate,
146
delay_time=lambda: 5.0, # 5 time units
147
initial_value=100,
148
order=3
149
)
150
151
# Fixed pipeline delay for manufacturing
152
manufacturing_delay = DelayFixed(
153
delay_input=lambda: orders_received,
154
delay_time=lambda: 7.0, # Exactly 7 time units
155
initial_value=50
156
)
157
158
# Get delayed values during simulation
159
processed_info = information_delay(current_time)
160
materials_out = material_delay(current_time)
161
finished_goods = manufacturing_delay(current_time)
162
```
163
164
### Smoothing and Forecasting
165
166
Components for smoothing noisy data and generating forecasts.
167
168
```python { .api }
169
class Smooth(DynamicStateful):
170
"""
171
Smoothing functions.
172
173
Implements exponential smoothing (first-order delay applied to input).
174
Reduces noise and volatility in model variables.
175
176
Methods:
177
- __init__(smooth_input, smooth_time, initial_value)
178
- __call__(time) - Get smoothed output value
179
"""
180
181
class Trend(DynamicStateful):
182
"""
183
Trend calculation.
184
185
Calculates the trend (rate of change) of input variable using
186
exponential smoothing of the derivative.
187
188
Methods:
189
- __init__(trend_input, trend_time, initial_trend=0)
190
- __call__(time) - Get current trend value
191
"""
192
193
class Forecast(DynamicStateful):
194
"""
195
Forecasting functions.
196
197
Generates forecasts by extrapolating current value and trend.
198
Combines exponential smoothing with trend analysis.
199
200
Methods:
201
- __init__(forecast_input, average_time, horizon_time, initial_trend=0)
202
- __call__(time) - Get forecasted value
203
"""
204
```
205
206
#### Usage Examples
207
208
```python
209
from pysd.py_backend.statefuls import Smooth, Trend, Forecast
210
211
# Smooth noisy sales data
212
def raw_sales():
213
return daily_sales_with_noise
214
215
smoothed_sales = Smooth(
216
smooth_input=raw_sales,
217
smooth_time=5.0, # 5-day smoothing
218
initial_value=1000
219
)
220
221
# Calculate trend in market growth
222
def market_size():
223
return current_market_size
224
225
market_trend = Trend(
226
trend_input=market_size,
227
trend_time=10.0, # 10-period trend calculation
228
initial_trend=0.05 # 5% initial growth rate
229
)
230
231
# Forecast future demand
232
demand_forecast = Forecast(
233
forecast_input=lambda: current_demand,
234
average_time=8.0, # 8-period averaging
235
horizon_time=12.0, # 12-period forecast horizon
236
initial_trend=0.02
237
)
238
239
# Access values during simulation
240
current_smooth_sales = smoothed_sales(current_time)
241
current_trend = market_trend(current_time)
242
future_demand = demand_forecast(current_time)
243
```
244
245
### Sampling and Control
246
247
Components for conditional sampling and initial value handling.
248
249
```python { .api }
250
class SampleIfTrue(DynamicStateful):
251
"""
252
Conditional sampling.
253
254
Samples and holds input value when condition is true, maintaining
255
the last sampled value when condition is false.
256
257
Methods:
258
- __init__(sample_input, condition, initial_value)
259
- __call__(time) - Get current sampled value (held if condition false)
260
"""
261
262
class Initial(Stateful):
263
"""
264
Initial value storage.
265
266
Stores and provides access to initial values of model variables.
267
Used for reference and reset operations.
268
269
Methods:
270
- __init__(initial_function)
271
- __call__(time) - Get initial value
272
"""
273
```
274
275
#### Usage Examples
276
277
```python
278
from pysd.py_backend.statefuls import SampleIfTrue, Initial
279
280
# Sample price when market is open
281
def current_price():
282
return stock_price
283
284
def market_open():
285
return trading_hours_active
286
287
sampled_price = SampleIfTrue(
288
sample_input=current_price,
289
condition=market_open,
290
initial_value=100.0
291
)
292
293
# Store initial population for comparison
294
def initial_population():
295
return starting_population_value
296
297
population_initial = Initial(initial_population)
298
299
# Use during simulation
300
price_when_open = sampled_price(current_time)
301
baseline_population = population_initial(current_time)
302
```
303
304
### State Management
305
306
All stateful components provide common state management functionality:
307
308
#### Initialization
309
310
```python
311
# Components can be initialized with specific values
312
component.init(time=0, initial_value=500)
313
314
# Or use default initialization
315
component.__init__(input_function, initial_value=100)
316
```
317
318
#### State Updates
319
320
```python
321
# Components automatically update during simulation
322
# Manual update if needed:
323
component.update(current_time)
324
325
# Access current state
326
current_value = component(current_time)
327
```
328
329
#### Memory Management
330
331
Stateful components automatically manage their internal memory:
332
- Maintain necessary historical values for calculations
333
- Clean up old data beyond required history
334
- Handle time step changes dynamically
335
336
### Integration with Models
337
338
Stateful components are typically created automatically when models are translated from Vensim/XMILE, but can also be used directly:
339
340
```python
341
import pysd
342
from pysd.py_backend.statefuls import Integ, Smooth
343
344
# In translated models, stateful components are created automatically
345
model = pysd.read_vensim('model_with_stocks.mdl')
346
results = model.run()
347
348
# Direct usage for custom components
349
def custom_flow():
350
return some_calculation()
351
352
custom_stock = Integ(custom_flow, initial_value=1000)
353
354
# Use in custom simulation loop
355
time_step = 0.25
356
current_time = 0
357
358
while current_time <= 50:
359
stock_value = custom_stock(current_time)
360
print(f"Time {current_time}: Stock = {stock_value}")
361
custom_stock.update(current_time)
362
current_time += time_step
363
```
364
365
### Error Handling
366
367
Stateful components include robust error handling:
368
369
- **Division by zero** in delay times handled gracefully
370
- **Negative delay times** raise appropriate warnings
371
- **Initialization errors** provide clear diagnostic messages
372
- **Time step consistency** automatically managed
373
374
```python
375
try:
376
problem_delay = Delay(input_func, delay_time=lambda: -1, initial_value=0)
377
except ValueError as e:
378
print(f"Invalid delay configuration: {e}")
379
```
380
381
### Performance Considerations
382
383
Stateful components are optimized for simulation performance:
384
385
- Efficient memory usage for long simulations
386
- Vectorized operations where possible
387
- Automatic caching of intermediate calculations
388
- Minimal overhead for state updates
389
390
For large models with many stateful components, consider:
391
- Using appropriate integration time steps
392
- Monitoring memory usage for very long simulations
393
- Utilizing model caching capabilities