Parameter Presetting¶
Download: example_06_parameter_presetting.py
We will reuse some stuff from the previous example Custom Parameter (Strange Attractor Inside!):
Our main euler simulation job euler_scheme
The FunctionParameter to store source code
We will execute the same euler simulation as before, but now with a different differential equation yielding the Roessler Attractor. If you erase the statement
traj.f_preset_parameter(‘diff_name’, ‘diff_roessler’)
you will end up with the same results as in the previous example.
import os # For path names being viable under Windows and Linux
import matplotlib.pyplot as plt
import numpy as np
# Let's reuse the stuff from the previous example
from example_05_custom_parameter import FunctionParameter, diff_lorenz, euler_scheme
from pypet import ArrayParameter, Environment
# Now we will add some control flow to allow to switch between the differential equations
def add_parameters(traj):
"""Adds all necessary parameters to the `traj` container.
You can choose between two parameter sets. One for the Lorenz attractor and
one for the Roessler attractor.
The former is chosen for `traj.diff_name=='diff_lorenz'`, the latter for
`traj.diff_name=='diff_roessler'`.
You can use parameter presetting to switch between the two cases.
:raises: A ValueError if `traj.diff_name` is none of the above
"""
traj.f_add_parameter("steps", 10000, comment="Number of time steps to simulate")
traj.f_add_parameter("dt", 0.01, comment="Step size")
# Here we want to add the initial conditions as an array parameter, since we will simulate
# a 3-D differential equation, that is the Roessler attractor
# (https://en.wikipedia.org/wiki/R%C3%B6ssler_attractor)
traj.f_add_parameter(
ArrayParameter,
"initial_conditions",
np.array([0.0, 0.0, 0.0]),
comment="Our initial conditions, as default we will start from origin!",
)
# Per default we choose the name `'diff_lorenz'` as in the last example
traj.f_add_parameter("diff_name", "diff_lorenz", comment="Name of our differential equation")
# We want some control flow depending on which name we really choose
if traj.diff_name == "diff_lorenz":
# These parameters are for the Lorenz differential equation
traj.f_add_parameter("func_params.sigma", 10.0)
traj.f_add_parameter("func_params.beta", 8.0 / 3.0)
traj.f_add_parameter("func_params.rho", 28.0)
elif traj.diff_name == "diff_roessler":
# If we use the Roessler system we need different parameters
traj.f_add_parameter("func_params.a", 0.1)
traj.f_add_parameter("func_params.c", 14.0)
else:
raise ValueError("I don't know what %s is." % traj.diff_name)
# We need to define the Roessler function, we will assume that the value array is 3 dimensional,
# First dimension is x-component, second y-component, and third the z-component
def diff_roessler(value_array, a, c):
"""The Roessler attractor differential equation
:param value_array: 3d array containing the x,y, and z component values.
:param a: Constant attractor parameter
:param c: Constant attractor parameter
:return: 3d array of the Roessler system evaluated at `value_array`
"""
b = a
diff_array = np.zeros(3)
diff_array[0] = -value_array[1] - value_array[2]
diff_array[1] = value_array[0] + a * value_array[1]
diff_array[2] = b + value_array[2] * (value_array[0] - c)
return diff_array
# And here goes our main function
def main():
filename = os.path.join("hdf5", "example_06.hdf5")
env = Environment(
trajectory="Example_06_Euler_Integration",
filename=filename,
file_title="Example_06_Euler_Integration",
overwrite_file=True,
comment="Go for Euler!",
)
traj = env.trajectory
# 1st a) phase parameter addition
# Remember we have some control flow in the `add_parameters` function, the default parameter
# set we choose is the `'diff_lorenz'` one, but we want to deviate from that and use the
# `'diff_roessler'`.
# In order to do that we can preset the corresponding name parameter to change the
# control flow:
traj.f_preset_parameter("diff_name", "diff_roessler") # If you erase this line, you will get
# again the lorenz attractor
add_parameters(traj)
# 1st b) phase preparation
# Let's check which function we want to use
if traj.diff_name == "diff_lorenz":
diff_eq = diff_lorenz
elif traj.diff_name == "diff_roessler":
diff_eq = diff_roessler
else:
raise ValueError("I don't know what %s is." % traj.diff_name)
# And add the source code of the function as a derived parameter.
traj.f_add_derived_parameter(
FunctionParameter, "diff_eq", diff_eq, comment="Source code of our equation!"
)
# We want to explore some initial conditions
traj.f_explore(
{
"initial_conditions": [
np.array([0.01, 0.01, 0.01]),
np.array([2.02, 0.02, 0.02]),
np.array([42.0, 4.2, 0.42]),
]
}
)
# 3 different conditions are enough for now
# 2nd phase let's run the experiment
# We pass 'euler_scheme' as our top-level simulation function and
# the Roessler function as an additional argument
env.run(euler_scheme, diff_eq)
# Again no post-processing
# 4th phase analysis.
# I would recommend to do the analysis completely independent from the simulation
# but for simplicity let's do it here.
# We won't reload the trajectory this time but simply update the skeleton
traj.f_load_skeleton()
# For the fun of it, let's print the source code
print("\n ---------- The source code of your function ---------- \n %s" % traj.diff_eq)
# Let's get the exploration array:
initial_conditions_exploration_array = traj.f_get("initial_conditions").f_get_range()
# Now let's plot our simulated equations for the different initial conditions.
# We will iterate through the run names
for idx, run_name in enumerate(traj.f_get_run_names()):
# Get the result of run idx from the trajectory
euler_result = traj.results.f_get(run_name).euler_evolution
# Now we manually need to load the result. Actually the results are not so large and we
# could load them all at once, but for demonstration we do as if they were huge:
traj.f_load_item(euler_result)
euler_data = euler_result.data
# Plot fancy 3d plot
fig = plt.figure(idx)
ax = fig.add_subplot(projection="3d")
x = euler_data[:, 0]
y = euler_data[:, 1]
z = euler_data[:, 2]
ax.plot(
x, y, z, label="Initial Conditions: %s" % str(initial_conditions_exploration_array[idx])
)
plt.legend()
plt.show()
# Now we free the data again (because we assume its huuuuuuge):
del euler_data
euler_result.f_empty()
# Finally disable logging and close all log-files
env.disable_logging()
if __name__ == "__main__":
main()