Note
Click here to download the full example code
RunSimulator: Ravaflow24 Mixture Model
This example shows how to run multiple Ravaflow24Mixture simulations in serial
and parallelly using RunSimulator.
First, we import the class Ravaflow24Mixture.
from psimpy.simulator import Ravaflow24Mixture
To create an instance of this class, we must specify the parameter dir_sim.
It represents the directory in which output files generated by r.avaflow will be
saved.
import os
# Here we create a folder called `temp1_run_Ravaflow24Mixture_example` to save
# output files generated by r.avaflow
cwd = os.getcwd()
if not os.path.exists('temp1_run_Ravaflow24Mixture_example'):
os.mkdir('temp1_run_Ravaflow24Mixture_example')
dir_sim = os.path.join(cwd, 'temp1_run_Ravaflow24Mixture_example')
Given dir_sim, we can create an instance of Ravaflow24Mixture.
To reduce simulation time, we set time_end to \(50\). Other parameters
are set to their default values.
voellmy_model = Ravaflow24Mixture(dir_sim, time_end=50)
The simulator in this example is defined based on voellmy_model as follows.
It takes required inputs and returns the overall impact area.
import numpy as np
def simulator(prefix, elevation, hrelease, basal_friction, turbulent_friction, EPSG):
"""Preprocess required inputs, run simulation, and return output as a numpy array."""
grass_location, sh_file = voellmy_model.preprocess(prefix=prefix, elevation=elevation,
hrelease=hrelease, basal_friction=basal_friction, turbulent_friction=turbulent_friction, EPSG=EPSG)
voellmy_model.run(grass_location, sh_file) # run this line, r.avaflow will write outputs to dir_sim
impact_area = voellmy_model.extract_impact_area(prefix)
return np.array([impact_area]) # define dir_out and set save_out to True, returned numpy array will be saved to dir_out
To demonstrate how to run multiple simulations using RunSimulator,
we choose basal_friction and turbulent_friction as variable input
parameters. Each has two different values, leading to four simulations.
import itertools
var_inp_parameter = ['basal_friction', 'turbulent_friction']
basal_friction = [20, 30]
turbulent_friction = [3, 4]
var_samples = np.array(
[x for x in itertools.product(basal_friction, turbulent_friction)])
Other parameters of the simulator, including elevation, hrelease, and
EPSG are treated as fixed input. It means that their values are the same in
all simulations.
dir_data = os.path.abspath('../../../tests/data/')
elevation = os.path.join(dir_data, 'synthetic_topo.tif')
hrelease = os.path.join(dir_data, 'synthetic_rel.tif')
fix_inp = {'elevation': elevation, 'hrelease': hrelease, 'EPSG': '2326'}
The parameter prefix of the simulator is special. It is not involved in
the computational model, but only used to name output files generated by r.avaflow.
Such parameter is not defined in var_inp_parameter or fix_inp of
RunSimulator. Instead, we use a seperate parameter, called o_parameter
for this purpose.
o_parameter = 'prefix'
We may want to save outputs returned by the 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
cwd = os.getcwd()
if not os.path.exists('temp2_run_Ravaflow24Mixture_example'):
os.mkdir('temp2_run_Ravaflow24Mixture_example')
dir_out = os.path.join(cwd, 'temp2_run_Ravaflow24Mixture_example')
Note
Please note that dir_out and dir_sim are two different
parameters for different purposes.
dir_outis a parameter ofRunSimulator. The simulator returns output of interest as a numpy array for each simulation. If we want to save this returned output of interest (here impact area) to our local machine, we need to specify the value ofdir_outwhich represents the directory in which output of interest will be saved and setsave_outto True. Otherwise, we do not needdir_outand leavesave_outto False.
dir_simis a parameter ofRavaflow24Mixture.Ravaflow24Mixturerelies on the third party software r.avaflow 2.4. When we callRavaflow24Mixture.run()in the function body of above simulator, r.avaflow 2.4 will be run and it generates output files. The value ofdir_simspecifies the directory in which output files generated by r.avaflow 2.4 are going to be saved.
The value of dir_out and dir_sim can be the same if file names
have no conflict. We recommend to keep them seperate. In addition, if the function
body of the simulator does not write files to disk, dir_sim is not required. Our
MassPointModel is an example. Similarly, if we do not want to save
returned numpy array of the simulator, dir_out is not needed.
Now we can define an object of RunSimulator by
from psimpy.simulator import RunSimulator
run_simulator = RunSimulator(simulator, var_inp_parameter, fix_inp, o_parameter,
dir_out, save_out=True)
Before running simulations, we need to specify values of prefixes which
will be used to name files generated by each r.avaflow simulation and
each returned numpy array of simulator.
serial_prefixes = ["serial"+str(i) for i in range(len(var_samples))]
parallel_prefixes = ["parallel"+str(i) for i in range(len(var_samples))]
Now we can use RunSimulator.serial_run() method or
RunSimulator.parallel_run() method to run the simulations
in serial or parallelly.
import time
start = time.time()
run_simulator.serial_run(var_samples=var_samples, prefixes=serial_prefixes)
serial_time = time.time() - start
serial_output = run_simulator.outputs
print(f"serial_output: {serial_output}")
start = time.time()
# append setting to True means that simulation outputs of parallel run will be
# appended to above serial run outputs
run_simulator.parallel_run(var_samples, prefixes=parallel_prefixes,
max_workers=2, append=True)
parallel_time = time.time() - start
parallel_output = run_simulator.outputs[len(var_samples):]
print(f"parallel_output: {parallel_output}")
print("Serial run time: ", serial_time)
print("Parallel run time: ", parallel_time)
serial_output: [array([1008000.]), array([1520800.]), array([674000.]), array([869600.])]
parallel_output: [array([1008000.]), array([1520800.]), array([674000.]), array([869600.])]
Serial run time: 110.85352993011475
Parallel run time: 62.39644193649292
All output files returned by the simulator are
os.listdir(dir_out)
['serial0_output.npy', 'parallel0_output.npy', 'serial1_output.npy', 'parallel2_output.npy', 'serial3_output.npy', 'parallel3_output.npy', 'serial2_output.npy', 'parallel1_output.npy']
All output files/directories generated by r.avaflow simulations (including files/directories generated during preprocessing) are
os.listdir(dir_sim)
['serial0_glocation', 'parallel3_results', 'parallel2_glocation', 'serial3_shell.sh', 'parallel1_results', 'serial3_glocation', 'serial1_shell.sh', 'parallel2_results', 'serial0_shell.sh', 'parallel1_glocation', 'parallel3_shell.sh', 'parallel0_shell.sh', 'serial1_glocation', 'parallel1_shell.sh', 'parallel2_shell.sh', 'serial0_results', 'serial1_results', 'serial2_shell.sh', 'serial2_glocation', 'serial2_results', 'parallel3_glocation', 'parallel0_glocation', 'serial3_results', 'parallel0_results']
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.
In the end, we delete the folder temp1_run_Ravaflow24Mixture_example and temp2_run_Ravaflow24Mixture_example and all files therein.
import shutil
shutil.rmtree(dir_sim)
shutil.rmtree(dir_out)
Total running time of the script: ( 2 minutes 53.318 seconds)