Skip to content

Running simulations

Simulations are run using the run_simulation() method of the Model class.

result = my_model.run_simulation(time=100)

The time argument is required and specifies the time span of the simulation. See the "Time specification" section below for details.

Other arguments are optional and include:

  • initial_state: A dictionary specifying the initial values of (some of) the state components. If not provided, state components are initialized to zero.

    result = my_model.run_simulation(
        time=100,
        initial_state={"x": 10.0}, 
    )
    
  • inputs: A dictionary specifying the values of (some of) the inputs, overwriting inputs provided to the Model.

    result = my_model.run_simulation(
        time=100,
        inputs={"external_force": 5.0},
    )
    
  • parameters: A dictionary specifying the values of (some of) the parameters, overwriting parameters provided to the Model.

    result = my_model.run_simulation(
        time=100,
        parameters={"mass": 2.0},
    )
    
  • relative_tolerance: The relative tolerance used by the solver. The default is 1e-5, and can be set in the settings.

    result = my_model.run_simulation(
        time=100,
        relative_tolerance=1e-7,
    )
    
  • absolute_tolerance: The absolute tolerance used by the solver. The default is 1e-7, and can be set in the settings.

    result = my_model.run_simulation(
        time=100,
        absolute_tolerance=1e-9,
    )
    
  • result_index_as_time: If True, the index of the resulting dataframe will be a pandas.TimedeltaIndex representing time, instead of a float index. See the "Time units" section below for more information.

    result = my_model.run_simulation(
        time=100,
        result_index_as_time=True,
    )
    
  • solver_output: An optional dictionary, in which the full output of the solver will be stored. This includes information such as the number of function evaluations, event triggers, etc. See the 'Returns' section on the documentation of solve_ivp() for a list of items.

    solver_output = {}
    result = my_model.run_simulation(
        time=100,
        solver_output=solver_output,
    )
    # solver_output now contains the solver output
    

DataFrame results

The result of a simulation is a pandas.DataFrame, with time as the index and inputs, state components, outputs, and post analysis results as columns. For example:

                     input1  state_a  state_b  output_x
Time (seconds)
0.0                   1.0      0.0      0.0     0.0
0.1                   1.0      0.5      0.2     0.1
0.2                   1.0      0.9      0.4     0.2
...

Pandas DataFrames provide a powerful way to analyze and visualize simulation results.

Time specification

When calling run_simulation(), the time must be specified1. The solver can dynamically determine an appropriate time step, or use a fixed time step. There are several ways to specify the time:

  • Provide the end time as integer. The simulation will start at time 0 and run until the specified end time.

    # runs from t=0 to t=100 with dynamically determined time steps
    result = my_model.run_simulation(time=100)  
    
  • Provide a tuple of (start, end) or (start, end, step). In the first case, the time step will be dynamically determined.

    # runs from t=10 to t=100 with dynamically determined time steps
    result = my_model.run_simulation(time=(10, 100))  
    
    # runs from t=20 to t=100 in steps of 0.05 time unit
    result = my_model.run_simulation(time=(20, 100, 0.05))  
    ``
    # if the time step is provided, the exact end time may not be included
    # runs from t=20 to t=99 (!) in steps of 3 time unit
    result = my_model.run_simulation(time=(0, 100, 3))  
    
  • To make the time specification more explicit, you can also provide a dictionary with attributes "start", "end" and "step". Only end is required.

    # runs from t=0 to t=10 in with dynamically determined time steps
    result = my_model.run_simulation(time={"end": 10})  
    
    # runs from t=-1 to t=1 in steps of 0.001 time unit
    result = my_model.run_simulation(time={"start": -1, "end": 1, "step": 1e-3})  
    

NB: the exact end time is not guaranteed to be included in the result if a time step was defined. Only if the end time is exactly \(n\) times the time step later than start time, it will be included. For example, if you run a simulation from 0 to 1 with a time step of 0.3, the result will include time points 0.0, 0.3, 0.6, and 0.9, but not 1.0. Contrarily, if you run a simulation from 0 to 1 with a time step of 0.2, the result will include time points 0.0, 0.2, 0.4, 0.6, 0.8, and 1.0.

Running multiple simulations in one call

It is possible to run multiple simulations in one call using run_simulations() (note the 's' and the end). Each simulation can have it's own arguments.

results = model.run_simulations(
    time=[
        {"end": 50, "step": 1},
        {"end": 50, "step": 0.1},
    ]
    parameters = {"mass": [1.0, 2.0]},
)

This call will run four simulations, combining the two time specifications with the two mass parameter values. The result will be a list of dataframes, each representing the result of one simulation. Each dataframe has the unique arguments used for that simulation stored in the attrs attribute.

for df in results:
    print("Simulation with time =", df.attrs["time"])
    print("Simulation with mass =", df.attrs["mass"])
    print(df)

  1. See the "Time units" section under "Model" for information about the units of time.