Note
Click here to download the full example code
RunSimulator: Mass Point Model
This example shows how to run multiple MassPointModel simulations in serial
and parallelly using RunSimulator
.
First, we import the class MassPointModel
and define a simulator
based on MassPointModel.run()
method.
from psimpy.simulator import MassPointModel
mpm = MassPointModel()
mpm_simulator = mpm.run
This simulator takes required input data, solves the mass point model, and
returns time history of the mass point’s location and velocity
(see MassPointModel.run()
).
- Required inputs for mpm_simulator include:
topographic data: digital elevation model (in ESRI ascii format)
friction coefficients: coulomb friction and turbulent friction coefficients
initial state: initial location and initial velocity of the masspoint
computational parameters: such as time step, end time, etc.
The synthetic topography synthetic_topo.asc
is used here for illustration.
It is located at the /tests/data/ folder.
import os
dir_data = os.path.abspath('../../../tests/data/')
elevation = os.path.join(dir_data, 'synthetic_topo.asc')
Note
You may need to modify dir_data
according to where you save
synthetic_topo.asc
on your local machine.
For this example, we are going to run multiple simulations at different values
of coulomb friction coefficient (coulomb_friction
) and turbulent friction
coefficient (turbulent_friction
) while keep other inputs fixed. Namely,
coulomb_friction
and turbulent_friction
are variable input parameters.
All other inputs are fixed input parameters.
import numpy as np
import itertools
# Variable iput parameters are defined as a list of strings. Their values will be
# passed as a numpy array (var_samples) when run simulations.
var_inp_parameter = ['coulomb_friction', 'turbulent_friction']
coulomb_friction = np.arange(0.1, 0.31, 0.1)
turbulent_friction = np.arange(500, 2001, 400)
var_samples = np.array(
[x for x in itertools.product(coulomb_friction, turbulent_friction)])
print("Number of variable input samples are: ", len(var_samples))
# Fixed input parameters are defined as a dictionary. Their values are given.
fix_inp = {'elevation': elevation, 'x0': 200, 'y0': 2000, 'tend': 50}
Number of variable input samples are: 12
Note
Parameters of mpm_simulator which are not included in
var_inp_parameter
and fix_inp
, such as ux0
, uy0
and
curvature
, will automatically take their default values.
We may want to save outputs returned by mpm_simulator at each simulation for
later inspection or processing. In that case, we need to define dir_out
and
set save_out
as True.
import os
# Here we create a folder called `temp_run_MassPointModel_example` to save outputs
# returned at each simulation.
cwd = os.getcwd()
if not os.path.exists('temp_run_MassPointModel_example'):
os.mkdir('temp_run_MassPointModel_example')
dir_out = os.path.join(cwd, 'temp_run_MassPointModel_example')
Now we can define an object of RunSimulator
by
from psimpy.simulator import RunSimulator
run_mpm_simulator = RunSimulator(mpm_simulator, var_inp_parameter, fix_inp,
dir_out=dir_out, save_out=True)
Note
Since outputs of each simulation are saved in the same folder dir_out
,
we need to give each file a unique name in order to avoid conflict. This is
realized by defining prefixes
.
serial_prefixes = ["serial"+str(i) for i in range(len(var_samples))]
parallel_prefixes = ["parallel"+str(i) for i in range(len(var_samples))]
Using RunSimulator.serial_run()
method or
RunSimulator.parallel_run()
method, we can run the simulations
in serial or parallelly.
import time
start = time.time()
run_mpm_simulator.serial_run(var_samples=var_samples, prefixes=serial_prefixes)
serial_time = time.time() - start
serial_output = run_mpm_simulator.outputs
start = time.time()
# max_workers controls maximum number of tasks running in parallel
run_mpm_simulator.parallel_run(var_samples, prefixes=parallel_prefixes, max_workers=4)
parallel_time = time.time() - start
parallel_output = run_mpm_simulator.outputs
print("Serial run time: ", serial_time)
print("Parallel run time: ", parallel_time)
Serial run time: 1.1941184997558594
Parallel run time: 0.4859800338745117
Once a simulation is done, its output is saved to dir_out
. All output files
generated by above simulations are as follows:
os.listdir(dir_out)
['serial0_output.npy', 'serial9_output.npy', 'serial11_output.npy', 'parallel10_output.npy', 'parallel0_output.npy', 'parallel5_output.npy', 'parallel11_output.npy', 'serial1_output.npy', 'serial4_output.npy', 'parallel7_output.npy', 'serial5_output.npy', 'serial8_output.npy', 'parallel6_output.npy', 'parallel4_output.npy', 'parallel2_output.npy', 'serial3_output.npy', 'serial6_output.npy', 'parallel3_output.npy', 'serial2_output.npy', 'parallel9_output.npy', 'serial7_output.npy', 'parallel1_output.npy', 'parallel8_output.npy', 'serial10_output.npy']
Once all simulations are done, their outputs can also be accessed by
RunSimulator.outputs
attribute, which is a list.
# Here we print the maximum velocity of each simulation in the parallel run.
max_v = [np.max(output[:,5]) for output in parallel_output]
print(
f"Maximum velocity of {parallel_prefixes[0]} to {parallel_prefixes[-1]} are: ",
'\n',
max_v)
Maximum velocity of parallel0 to parallel11 are:
[16.55505557211892, 22.014114896121015, 26.245546886730782, 29.78487303612985, 15.34198274528081, 20.371864007499866, 24.249957243412815, 27.485487855011687, 14.031665649204944, 18.589324449452967, 22.092803891476187, 24.99727320816709]
Warning
If one simulation failed due to whatever reason, the error massage
will be printed to the screen but other simulations will continue. In that
case, the output file of failed simulation will not be writted to dir_out
.
Also, the element of RunSimulator.outputs
corresponding to that
simulation will be a string representing the error message, instead of a
numpy array.
Here we delete the folder temp_run_MassPointModel_example and all files therein.
import shutil
shutil.rmtree(dir_out)
Total running time of the script: ( 0 minutes 1.687 seconds)