Basic Usage¶
WATTS consists of a set of Python classes that can manage simulation workflows for multiple codes where information is exchanged at a coarse level. For each code, input files rely on placeholder values that are filled in based on a set of user-defined parameters.
Parameters¶
The parameters that are used to “fill in” input files with placeholders are
managed by the Parameters
class. This class mostly behaves like
a Python dictionary but has a few extra capabilities. Setting parameters can be
done as follows:
params = watts.Parameters()
params['temperature'] = 550.0
params['option'] = True
params['values'] = [10.0, 20.0, 0.05]
Like a Python dictionary, key/value pairs can also be set when instantiating the object:
params = watts.Parameters(
temperature=550.0,
option=True,
values=[10.0, 20.0, 0.05]
)
Most native Python datatypes (int
, float
, bool
,
str
, list
, set
, tuple
, dict
) are
supported along with NumPy arrays
as well. Parameters
can be saved to an HDF5 file:
params.save('parameters.h5')
and later re-created using the from_hdf5()
method:
loaded_params = watts.Parameters.from_hdf5('parameters.h5')
By themselves, Parameters
are not very useful, but when
combined with plugin classes, they become building blocks for sophisticated
workflows.
Units¶
To handle codes that use different unit systems, WATTS relies on the
Quantity
class from astropy.units
to perform unit
conversion on parameters to ensure that the correct units are used for each
code. For instance, MOOSE-based codes use the SI units while OpenMC uses the CGS
units. With the built-in unit-conversion capability, a parameter needs only to
be set once in any unit system and WATTS can automatically convert it to the
correct unit for different codes. To use the unit-conversion capability,
parameters need to be set using the Quantity
class as
follows:
from astropy.units import Quantity
params['radius'] = Quantity(9.9, "mm")
params['inlet_temperature'] = Quantity(600, "Celsius")
params['c_p'] = Quantity(4.9184126, "BTU/(kg*K)")
with the format of Quantity(value, unit)
.
Plugins¶
Using a particular code within WATTS requires a “plugin” that controls input
file generation, execution, and post-processing. Three plugin classes,
PluginMOOSE
, PluginOpenMC
, and
PluginPyARC
, have already been added to WATTS and are available
for your use.
MOOSE Plugin¶
The PluginMOOSE
class enables MOOSE simulations using a
templated input file. This is demonstrated here for a SAM application, but other
examples based on BISON are also available. For MOOSE codes such as SAM or BISON
that use text-based input files, WATTS relies on the Jinja templating engine for handling templated
variables and expressions. The templated input file looks like a normal MOOSE
input file where some values have been replaced with variables, which are
denoted by {{
and }}
pairs and get replaced with actual values when the
template is rendered. For example, a templated input file might look as
follows:
[GlobalParams]
global_init_P = {{ He_Pressure }}
global_init_V = {{ He_velocity }}
global_init_T = {{ He_inlet_temp }}
gravity = '-9.8 0 0'
scaling_factor_var = '1 1e-3 1e-6'
Tsolid_sf = 1e-3
[]
If the templated input file is sam_template.inp
, the SAM code will rely on
the general MOOSE plugin that can be created as:
moose_plugin = watts.PluginMOOSE('sam_template.inp')
The MOOSE plugin provides the option to specify non-templated input files (in extra_inputs option) that will be copied together with the templated input file (mesh or cross-section files).
The SAM executable defaults to sam-opt
(assumed to be present on your
PATH
) but can also be specified explicitly with the
moose_exec
attribute:
moose_plugin.moose_exec = "/path/to/sam-opt"
To execute SAM, the workflow()
method is called and
expects to receive an instance of Parameters
. For the above
template, the Parameters
instance should have He_Pressure
,
He_velocity
, and He_inlet_temp
parameters present. Thus, executing SAM
with this templated input file along with corresponding parameters might look as
follows:
params = watts.Parameters()
params['He_Pressure'] = 2.0
params['He_velocity'] = 1.0
params['He_inlet_temp'] = 600.0
results = moose_plugin.workflow(params)
Calling the workflow()
method will render the templated
input file (replace variables with values from the Parameters
instance), execute SAM, and collect the output files.
Beyond simple variable substitution, Jinja has sophisticated capabilities for using logical control structures, filters, calling Python methods, and extensible templates; for advanced usage, please read through the Jinja template designer documentation.
OpenMC Plugin¶
The PluginOpenMC
class handles OpenMC execution in a similar
manner to the PluginMOOSE
class for MOOSE. However, for OpenMC,
inputs are generated programmatically through the OpenMC Python API. Instead of
writing a text template, for the OpenMC plugin you need to write a function that
accepts an instance of Parameters
and generates the necessary
XML files. For example:
def godiva_model(params):
model = openmc.Model()
pu_metal = openmc.Material()
pu_metal.set_density('sum')
pu_metal.add_nuclide('Pu239', 3.7047e-02)
pu_metal.add_nuclide('Pu240', 1.7512e-03)
pu_metal.add_nuclide('Pu241', 1.1674e-04)
pu_metal.add_element('Ga', 1.3752e-03)
model.materials.append(pu_metal)
sph = openmc.Sphere(r=params['radius'], boundary_type='vacuum')
cell = openmc.Cell(fill=pu_metal, region=-sph)
model.geometry = openmc.Geometry([cell])
model.settings.batches = 50
model.settings.inactive = 10
model.settings.particles = 1000
model.export_to_xml()
With this function, the PluginOpenMC
class can be
instantiated:
openmc_plugin = watts.PluginOpenMC(godiva_model)
Note how the function object itself is passed to the plugin. When the
workflow()
method is called, the “template” function
is called and passed the user-specified Parameters
:
params = watts.Parameters(radius=6.0)
results = openmc_plugin.workflow(params)
This will generate the OpenMC input files using the template parameters, run OpenMC, and collect the results.
PyARC Plugin¶
The PluginPyARC
class handles PyARC execution in a similar
manner to the PluginMOOSE
class for MOOSE. PyARC use text-based
input files which can be templated as follows:
surfaces{
hexagon ( hex ){ orientation=y normal = z pitch = {{ assembly_pitch }} }
plane ( z0 ) { z = 0.0 }
plane ( z10 ) { z = {{ assembly_length }} }
}
If the templated input file is pyarc_template, then the PyARC plugin can be instantiated with following command line:
pyarc_plugin = watts.PluginPyARC('pyarc_template', show_stdout=True, extra_inputs=['lumped_test5.son'])
The path to PyARC directory must be specified explicitly with the
pyarc_exec
attribute:
pyarc_plugin.pyarc_exec = "/path/to/PyARC"
To execute PyARC, the workflow()
method is called
the same way as other Plugins.
Results¶
When you run the workflow()
method on a plugin, an instance
of the Results
class specific to the plugin will be returned
that contains information about the results. Every Results
object contains a list of input and output files that were generated:
>>> results = plugin_openmc.workflow(params)
>>> results.inputs
[PosixPath('geometry.xml'),
PosixPath('settings.xml'),
PosicPath('materials.xmll')]
>>> results.outputs
[PosixPath('OpenMC_log.txt'),
PosixPath('statepoint.250.h5')]
Results
objects also contain a copy of the
Parameters
that were used at the time the workflow was run:
>>> results.parameters
<watts.parameters.Parameters at 0x0x15549e5b8d60>
>>> results.parameters['radius']
6.0
Each plugin actually returns a subclass of Results
that extends
the basic functionality by adding methods/attributes that incorporate
post-processing logic. For example, the ResultsOpenMC
class
provides a keff
attribute that provides the
k-effective value at the end of the simulation:
>>> results.keff
1.0026170700986219+/-0.003342785895893627
For MOOSE, the ResultsMOOSE
class provides a
csv_data
attribute that gathers the results from
every CSV files generated by MOOSE applications (such as SAM or BISON):
moose_result = moose_plugin.workflow(params)
for key in moose_result.csv_data:
print(key, moose_result.csv_data[key])
For PyARC, the ResultsPyARC
class
provides a results_data
attribute that gathers the
results stored in PyARC.user_object:
pyarc_result = pyarc_plugin.workflow(params)
for key in pyarc_result.results_data:
print(key, pyarc_result.results_data[key])
Database¶
When you call the workflow()
method on a plugin, the
Results
object and all accompanying files are automatically
added to a database on disk for later retrieval. Interacting with this database
can be done via the Database
class:
>>> db = watts.Database()
>>> db.results
[<watts.plugin_openmc.ResultsOpenMC at 0x15530416bfd0>,
<watts.plugin_openmc.ResultsOpenMC at 0x15530416bbb0>,
<watts.plugin_moose.ResultsMOOSE at 0x1553043c8a30>]
By default, the database will be created in a user-specific data directory (on
Linux machines, this is normally within ~/.local/share
). However, the
location of the database can be specified:
db = watts.Database('/opt/watts_db/')
Creating a database this way doesn’t change the default path used when running
plugin workflows. If you want to change the default database path used in
workflows, the set_default_path()
classmethod should be
used:
>>> watts.Database.set_default_path('/opt/watts_db')
>>> db = watts.Database()
>>> db.path
PosixPath('/opt/watts_db')
To clear results from the database, simply use the
clear()
method:
>>> db.clear()
>>> db.results
[]
Be aware that clearing the database will delete all the corresponding results on disk, including input and output files from the workflow.