Getting Started#
What is watts
?#
WATTS (Workflow and Template Toolkit for Simulation) consists of a set of Python classes that can manage simulation workflows for one or multiple codes. It provides the following capabilities:
An isolated execution environment when running a code;
The ability to use placeholder values in input files that are filled in programmatically;
Seamless unit conversions when working with multiple codes;
A managed database that simulation inputs and outputs are automatically saved to; and
Python classes that provide extra post-processing and analysis capabilities for each code.
Basic Execution#
There are four major types of classes within watts
. The
Plugin
class (and its subclasses) provide the main interface to
codes. As an example, let’s say we have the following input file for MCNP that
we want to run:
Bare sphere of plutonium
1 1 0.04 -1 imp:n=1
2 0 1 imp:n=0
1 so 6.5
m1 94239.70c 0.04
kcode 10000 1.0 50 150
ksrc 0 0 0
If the filename of the input file is sphere_model
, we start by creating a
watts.PluginMCNP
object:
plugin_mcnp = watts.PluginMCNP("sphere_model")
Calling the plugin class then executes the code:
result = plugin_mcnp()
When a plugin is called, any input files are copied to a temporary directory to
create an isolated execution environment. Once the code is finished executing,
all input and output files are moved to a database, and you are provided a
Results
object that provides an interface to the simulation
artifacts and methods for common post-processing tasks. In the example above,
calling the PluginMCNP
instance returns a
ResultsMCNP
object, which we can use to get a list of the output
files or determine the resulting \(k_\text{eff}\) value:
>>> result.outputs
[PosixPath('MCNP_log.txt'),
PosixPath('srctp')
PosixPath('outp')
PosixPath('runtpe')]
>>> result.keff
1.0007+/-0.00053
Templates and Parameters#
The input file shown above is just a normal MCNP input file. However, you can
also put placeholders in an input file and have watts
fill them in using
the Parameters
class. Let’s say we change the input file as
follows:
Bare sphere of plutonium
1 1 0.04 -1 imp:n=1
2 0 1 imp:n=0
1 so {{ radius }}
m1 94239.70c 0.04
kcode 10000 1.0 50 {{ cycles }}
ksrc 0 0 0
We’ve added two placeholders, {{ radius }}
and {{ cycles }}
, that will
be filled in. Before creating and calling our plugin, we now need to specify
these parameters:
params = watts.Parameters()
params['radius'] = 6.0
params['cycles'] = 200
As before, we create an instance of PluginMCNP
but instead of
calling it with no arguments, we pass it the Parameters
instance:
plugin_mcnp = watts.PluginMCNP("sphere_model")
result = plugin_mcnp(params)
If we wanted to run this model with a series of different radii, it’s now as simple as changing the corresponding parameter and calling the plugin:
for r in [2.0, 4.0, 6.0, 8.0, 10.0]:
params['radius'] = r
result = plugin_mcnp(params, name=f'r={r}')
Note that the name
argument provides a means of identifying a result both
while the code is executing as well as afterwards. During execution, the
name
will be shown in the output:
[watts] Calling MCNP (r=2.0)...
[watts] Calling MCNP (r=4.0)...
[watts] Calling MCNP (r=6.0)...
[watts] Calling MCNP (r=8.0)...
[watts] Calling MCNP (r=10.0)...
Results Database#
Results are automatically added to a database and persist between invocations of
Python. The watts
command-line tool allows you to quickly get a list of
results:
$ watts results
+-------+--------+--------+--------+----------------------------+
| Index | Job ID | Plugin | Name | Time |
+-------+--------+--------+--------+----------------------------+
| 0 | 0 | MCNP | | 2022-06-01 13:21:44.713942 |
| 1 | 1 | MCNP | | 2022-06-01 13:23:12.410774 |
| 2 | 2 | MCNP | r=2.0 | 2022-06-02 07:46:05.463723 |
| 3 | 2 | MCNP | r=4.0 | 2022-06-02 07:46:10.996932 |
| 4 | 2 | MCNP | r=6.0 | 2022-06-02 07:46:17.487411 |
| 5 | 2 | MCNP | r=8.0 | 2022-06-02 07:46:24.964455 |
| 6 | 2 | MCNP | r=10.0 | 2022-06-02 07:46:33.426781 |
+-------+--------+--------+--------+----------------------------+
Each result listed can be referenced by its index, which is used in other subcommands. For example, to determine the directory where input/output files are stored for the result with index 2, you can run:
$ watts dir 2
/home/username/.local/share/watts/3c5674ae37094d74af7a7fc5562555a3
The API also allows programmatic access to the database through the
Database
class, which provides a list-like object that contains
all previously generated Results
objects. For example, we may
want to look at the last five results to see how \(k_\text{eff}\) varies
with the radius.
>>> database = watts.Database()
>>> database
[<ResultsMCNP: 2022-06-01 13:21:44.713942>,
<ResultsMCNP: 2022-06-01 13:23:12.410774>,
<ResultsMCNP: 2022-06-02 07:46:05.463723>,
<ResultsMCNP: 2022-06-02 07:46:10.996932>,
<ResultsMCNP: 2022-06-02 07:46:17.487411>,
<ResultsMCNP: 2022-06-02 07:46:24.964455>,
<ResultsMCNP: 2022-06-02 07:46:33.426781>]
This enables us to easily look at the \(k_\text{eff}\) value for the last five MCNP simulations:
>>> [result.keff for result in database[-5:]]
[0.3523+/-0.00021,
0.68017+/-0.00042,
0.97663+/-0.00063,
1.24086+/-0.00075,
1.47152+/-0.00081]
Your First watts
Program#
To show how the various classes fit together, the example below creates a plugin
for a “code” (in this case, just the Linux cat
command) and executes the
code on a templated input file that is rendered using parameters that are
defined in a Parameters
instance. This example assumes we have a
file called triangle.txt containing the following text:
width={{ width }}
height={{ height }}
area={{ 0.5 * height * width }}
The watts
script is as follows:
import watts
# Create a plugin for the 'cat' code
plugin = watts.PluginGeneric(
executable='cat',
execute_command=['{self.executable}', '{self.input_name}'],
template_file='triangle.txt',
unit_system='cgs'
)
# Define some parameters that will be used to render the input file
params = watts.Parameters()
params['width'] = watts.Quantity(1.0, 'm')
params['height'] = watts.Quantity(1.0, 'inch')
# Execute the plugin
result = plugin(params)
# Show the resulting input file
print(result.stdout)
Running this example will produce the following output:
width=100.0
height=2.54
area=127.0
When the plugin is executed, the cat
command is called on the rendered input
file, which is just the triangle.txt file where the parameters have been filled
in. Note that the physical quantities were converted to
centimeters since we indicated that this plugin uses CGS units.