Python API¶
import freedyn2 loads, runs, checks and sweeps models from Python; results come as numpy
arrays. It does what the command line does: read the model (.yaml or a legacy
.fds), write the runtime model file, compile the model plugin if the model needs one, run
freedyn2_run in a subprocess and read its result CSV. The package comes with
pip install freedyn2; from the sources, PYTHONPATH=generator and a built build/freedyn2_run.
import freedyn2
m = freedyn2.load("model.yaml") # validated; ModelError if not
m.name, m.parameters, m.bodies, m.joints, m.forces
r = freedyn2.run(m, end_time=2, step=1e-3, params={"k": 2e4})
r.t # numpy array
r["rod.R_x"] # a channel (docs: result channels)
r.channels # their names, without t
r.kpi("rod.R_z", "peak-peak", t0=1.0) # max, min, max |x|, peak-peak, mean, rms, final
r.to_pandas() # a DataFrame indexed by t (needs pandas)
r.csv_path, r.log, r.simulation_seconds
Functions¶
load(path, profile="v2", params=None) |
the Model of a .yaml or .fds file; profile="legacy": .fds models with the legacy algorithms |
run(model, end_time=, step=, output_step=, params=, inputs=, options=, workdir=, profile=, runtime=) |
build and simulate a Model or a path; a Result |
check(model, results=None, run=False, **run_kwargs) |
the checks of the model, and of its results (a Result, a CSV path, or run=True); a Report |
sweep(model, parameter, values, jobs=None, **run_kwargs) |
one value over several, simulated in parallel; a list of Result |
convert(path, output=None, profile="v2", force=False) |
write a legacy .fds model as a YAML model file, as freedyn2 convert; its path |
examples(), example_path(name) |
the example models: {name: path}; the path of one ("quarter_car", "compare/pendulum.fds") |
__version__ |
the version |
params:{name: value}sets values of the model file before it is validated, as the viewer's tuning sliders do. A bare name is a model parameter (parameterssection), a path any number of the file:"forces.spring.stiffness","bodies.rod.position.2"..yamlmodels only.m.with_parameters({...})gives the changed model;mstays as it is.end_time,step,output_stepoverride the model'ssolversection,inputs({name: value}) sets runtime inputs,optionspasses further runtime options, e.g.["--linear-solver", "dense"].workdir: keep the model file, plugin and result CSV there. By default a run goes into a temporary directory that is removed with itsResult.runtime: thefreedyn2_runto use; by default found as by the command line (the installed one,FREEDYN2_RUN,build/freedyn2_run, the PATH).
Model.to_yaml(path=None, force=False) writes a .fds model (loaded with its profile) as
a YAML file, as convert:
m = freedyn2.load("crank.fds", profile="legacy")
y = m.to_yaml("crank.yaml") # runs as crank.fds with --profile legacy
r = freedyn2.run(y)
Result.check() checks the run, Result.eigenvalues(time=None) linearizes the model about
the output at time (the first by default, Lie group bodies), as the viewer's eigenmodes.
Errors are exceptions: ModelError (invalid model, unknown parameter; a ValueError),
SimulationError (freedyn2_run failed; the message holds its output),
RuntimeNotFoundError (a FileNotFoundError); FreeDynError is the base of all three.
In Jupyter¶
import freedyn2
import matplotlib.pyplot as plt
path = freedyn2.example_path("quarter_car")
m = freedyn2.load(path)
print(m.parameters)
r = freedyn2.run(m, end_time=3)
plt.plot(r.t, r["body.R_z"], label="body")
plt.plot(r.t, r["wheel.R_z"], label="wheel")
plt.xlabel("t [s]"); plt.ylabel("z [m]"); plt.legend()
print(freedyn2.check(m, r)) # the text of freedyn2 check
r.to_pandas().describe()
freedyn2 examples quarter_car copies the example next to the notebook to change it; the
viewer (freedyn2 view model.yaml) shows the same model in 3D.
Parameter studies¶
sweep builds the model once per value and runs the simulations in parallel (jobs, default
one per core), as the viewer's sweep:
import numpy as np
import freedyn2
path = freedyn2.example_path("forced_oscillator")
values = np.linspace(100, 800, 8)
runs = freedyn2.sweep(path, "k", values, end_time=5, params={"d": 2.0})
amplitude = [r.kpi("mass.R_z", "max |x|", t0=3.0) for r in runs]
A value that fails raises SimulationError naming each failed value; with strict=False it
gives None in its place. For more than one value at a time, a loop over run with params
does it, one run after the other:
for k in (1e4, 2e4):
for d in (500, 1000):
r = freedyn2.run(path, params={"k": k, "d": d}, end_time=5)
What it is not¶
The API runs whole simulations: freedyn2_run is a separate program, a run is a subprocess
and the results are read from its CSV file. There is no step-by-step control yet (advance one
step, read the state, set an input, go on); between runs, runtime inputs over time
(options=["--inputs", "inputs.csv"]) are the way to drive a model. A binding of the runtime
to Python, for co-simulation and control loops, is planned; this API is meant to stay the
same with it.