API
physiomodeler
¶
Model
¶
Model(
dynamics: Callable | "Model" | list[Callable | "Model"],
*,
state_components: list[str] | None = None,
initial_state: dict[str, float] | None = None,
state_derivative_label_map: (
dict[str, str] | None
) = None,
inputs: dict[str, float | int | Callable] | None = None,
parameters: (
dict[str, int | float | str | bool | list | tuple]
| None
) = None,
events: list[Callable] | None = None,
post_analysis: list[Callable] | None = None,
time_units: TimeUnitsType | None = None,
name: str | None = None
)
A state-space model described by system dynamics and parameters.
This class can be used to describe and numerically analyze a state space model. A state-space model has at least one state variable that evolves over time based on the state, as well as the inputs to and parameters of the model.
The model is described by one or multiple dynamics (functions, submodels, or
combinations thereof) that define how the state changes. A dynamics function
can have time, state, inputs and parameters as arguments. Each
dynamics function should return a dictionary containing state derivatives and
other value outputs. Dynamics are called in the order they are provided. The
output of each dynamics is added to the input of later dynamics. State
derivatives can be returned by different dynamics, but a derivative should be
returned for all state variables.
By default, the key for a state derivative should be "d" + the state label,
e.g. "dvelocity" for the state "velocity". Alternatively, you can supply a
mapping dictionary state_derivative_label_map where the key is the state
variable label and the value is the state derivative label.
Inputs and parameters both contain values that can be used by the update
function. There is, however, a distinction between inputs and parameters.
Parameters are fixed values that are independent of time, e.g. the mass of
an object at the end of a pendulum. Inputs are disturbances added to the
system, and can be either fixed or variable with time, e.g. the force
applied to a moving mass. inputs and parameters are both dictionaries.
inputs values can be functions with the signature fun(time, inputs,
parameters), which are converted to the appropriate before being passed to
the update function.
Events are functions with a signature fun(time, state, inputs,
parameters). An event occurs when a function returns 0. The solver will
find an accurate value of time where fun(time, ...) == 0. This ensures
proper simulation around that time point. Each event function can be marked
'terminal' by adding the terminal attribute with value True to the event
function (fun.terminal = True). This results in the simulation being
terminated as soon as the event return value crosses 0. See
scipy.integrate.solve_ivp
for more details.
Post analysis functions can be supplied, which receive the output of the
system as a pandas DataFrame (see run_simulation) and either
update the dataframe or return an array, list, Series or DataFrame object
with the same length that is added to the output of the model.
Example
The example below is the classic undampended pendulum. The system is
governed by the second derivative of the angle theta. The system state has
two components: the current angle "theta" and the angular velocity
"dtheta". The function pendulum() calculates the second derivative and
returns both the first and second derivative. When this system is solved,
the new state is calculated based on the derivatives of the previous step.
>>> def pendulum(time, state, inputs, parameters):
... g = parameters["g"]
... l = parameters["l"]
... theta = state["theta"]
...
... first_derivative_theta = state["dtheta"]
... second_derivative_theta = -(g / l) * np.sin(theta)
...
... return {
... "dtheta": first_derivative_theta,
... "ddtheta": second_derivative_theta
... }
>>> model = Model(
... function=pendulum,
... state=("theta", "dtheta"),
... parameters={"g": 9.81, "l": 1},
... )
>>> df = model.run_simulation(time=10, initial_state=(0.1, 0))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
function
|
Callable | list[Callable]
|
function(s) with accepting time, state, inputs and parameters as arguments and returning a dictionary containing (at least) the state derivatives |
required |
state
|
list | dict
|
list of state labels, or a dictionary with the intial values of the state |
required |
inputs
|
dict
|
dictionary containing the inputs as name/value pairs |
None
|
parameters
|
dict
|
a dictionary containing the parameters of the model |
None
|
post_analysis
|
dict
|
optional, a dictionary containing functions to calculate values based on the output of the model |
None
|
state_derivative_label_map
|
dict
|
optional, a dictionary mapping the labels of the state derivatives to the labels of the state variables. |
None
|
Initialize a Model instance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dynamics
|
Callable | 'Model' | list[Callable | 'Model']
|
System dynamics defining state evolution. Can be a single function, Model instance, or list of callables/Models. |
required |
state_components
|
list[str] | None
|
Optional list of state variable labels. |
None
|
initial_state
|
dict[str, float] | None
|
Optional initial values for state variables as dict. |
None
|
state_derivative_label_map
|
dict[str, str] | None
|
Maps state labels to their derivative labels. If not provided, defaults are generated from settings. |
None
|
inputs
|
dict[str, float | int | Callable] | None
|
Model inputs as dict. Values can be constants or time-dependent functions. |
None
|
parameters
|
dict[str, int | float | str | bool | list | tuple] | None
|
Model parameters as dict. Fixed values independent of time. |
None
|
events
|
list[Callable] | None
|
List of event detection functions. |
None
|
post_analysis
|
list[Callable] | None
|
List of post-simulation analysis functions. |
None
|
time_units
|
TimeUnitsType | None
|
Units for time axis in output (e.g., "s", "ms"). |
None
|
name
|
str | None
|
Optional model name. Auto-generated if not provided. |
None
|
Source code in physiomodeler/model.py
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | |
run_simulation
¶
run_simulation(
*,
time: (
float
| tuple[float, float]
| tuple[float, float, float]
| TimeSpec
| list
| ndarray
),
initial_state: dict | None = None,
inputs: dict | None = None,
parameters: dict | None = None,
solve_ivp_method: Literal["Radau"] = "Radau",
relative_tolerance: float | None = None,
absolute_torelance: float | None = None,
result_index_as_time: bool = False,
solver_output: dict | None = None
)
Generate an output based on the given initial state, inputs and parameters.
This function generates the output of the model over a given time axis
based on an initial state, the inputs and parameters. If no or not all
the initial_state, inputs or parameters values are given, the
values provided to the system at initialisation are used instead.
The time can be passed in several ways. The simplest is passing a
duration. The model will be solved up to that duration. When time is
a tuple of length 2, it indicates the start and end time. (Note that
the initial state is the state at t=start, not at t=0.) The time
step will be time_step if supplied, or determined by the solver otherwise.
If time is a tuple of length 3, the last value is used as the time
step. If a list or array is supplied, the output will contain these
time points.
Relative and absolute tolerance for the solver. The solver estimates a
local error as absolute_torelance + relative_tolerance * abs(state). relative_tolerance controls a relative
accuracy ('number of correct digits') while absolute_torelance controls absolute
accuracy ('number of correct decimal places'). See
scipy.integrate.solve_ivp
for more details.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
initial_state
|
dict
|
optional, initial values for the state as a dictionary, or list/tuple of values |
None
|
inputs
|
dict
|
optional, inputs of the system as a dictionary or list/tuple of values |
None
|
parameters
|
dict
|
optional, dictionary with the parameters of the system |
None
|
time
|
float | TimeSpec | tuple | ndarray
|
Time specification. Can be: - float: total duration from 0 to time - TimeSpec: start, end, and optional step (use keyword args for clarity) - tuple[start, end]: time range - tuple[start, end, step]: time range with step - list/ndarray: specific time points Examples: time=10 # Simulate 0 to 10 time=TimeSpec(start=0, end=10) # Start to end time=TimeSpec(start=0, end=10, step=0.1) # With step |
required |
solve_ivp_method
|
str
|
solver used; currently only "Radau" is supported |
'Radau'
|
relative_tolerance
|
float | None
|
relative tolerance |
None
|
absolute_torelance
|
float | None
|
absolute tolerance |
None
|
Source code in physiomodeler/model.py
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 | |
find_equilibrium_state
¶
Find the equilibrium state of the model.
The equilibrium state is either a state of absolute equilibrium if the inputs are constant, the state at the start of a period resulting in a periodic output if the inputs are periodic.
This function runs the model from 0 to time a maximum of max_n_runs
times. The initial state is equal to the estimated equilibrium state
during the first run, and the last state of the last iteration each
subsequent run. Assuming the model converges to a stable state, each
subsequent run should start and end closer to the equilibrium state
than the previous run.
When the end state does not change by more than rtol, it is assumed
this is the equilibrium state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
time
|
float
|
the time at which the equilibrium is evaluated. |
required |
time_step
|
float | None
|
time step used in running the simulation. Defaults to None. |
required |
estimated_equilibrium_state
|
dict | None
|
starting point for finding the equilibrium state. Defaults to None. |
None
|
inputs
|
dict | None
|
inputs of the model. Defaults to None. |
required |
parameters
|
dict | None
|
parameters of the model. Defaults to None. |
required |
max_n_runs
|
int
|
maximum number of iterations before the function fails. Defaults to 100. |
required |
rtol
|
float
|
relative tolerance of model run. Defaults to 1e-4. |
required |
atol
|
float
|
absolute tolerance of the model run. Defaults to 1e-6. |
required |
rtol_eq
|
float
|
relative tolerance comparing the current to the previous iteration. Defaults to 1e-3. |
required |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
is raised then no equilibrium can be found after max_n_runs iterations. |
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict
|
the resulting equilibrium state. |
Source code in physiomodeler/model.py
replace
¶
replace(**changes) -> Self
Create a copy of the model with the given changes.
This function creates a copy of the model with the given changes.
Similar to attrs.evolve, but with proper typing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**changes
|
changes to the model |
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
Self |
Self
|
a copy of the model with the given changes |
Source code in physiomodeler/model.py
events
¶
state_crosses_value
¶
state_crosses_value(
label: str,
value: float,
*,
terminal: bool = False,
direction: Literal[-1, 0, 1] = 0
)
Create an event that detects when a state crossing the given value.
Example
This example will have an event and terminate when the volume (state) crosses the value 0.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
label
|
str
|
label of the state to track |
required |
value
|
float
|
the event will trigger when the state crosses this value |
required |
terminal
|
bool
|
whether to terminate the simulation if the event occurs |
False
|
direction
|
int
|
if 0, any crossing will be detected; if 1, only positive crossings (value goes from negative to positive) will be detected; if -1, only negative crossings will be detected. |
0
|