Tutorial

Your parameterized model templates are usually stored in the project directory. The default location is a subdirectory modelTemplates in the root of the project directory, but this can be customized by changing the project configuration settings. In some cases it is practical to store all model templates in a centralized location, separate from the project directory.

In this tutorial we will set up a very simple simulation project example, with one model template located inside the project directory.

For this tutorial, we assume that you work in Linux or Cygwin.

Parsim is a command-line tool. The main program is called psm. Use it with option -h to get information about available subcommands. You get detailed information about each subcommand using the command psm help <command>.

Creating a project

Let’s call our project “myProject” and assume we want it in project directory my_proj in our home directory. We create the project directory and initialize it using the psm init command:

$ mkdir ~/my_proj
$ cd ~/my_proj
$ psm init myProject
INFO: Parsim project "myProject" successfully created

Creating a simple model template

Assume we have the following Python script, named boxcalc.py, which represents a very simple “simulation model”:

#! /usr/bin/env python

from __future__ import print_function
import json

# Model parameters

#- Geometry
length = 12
width = 4
height = 1.5

#- Material properties
density = 1000 # kg/m3
color = 'black'

# Calculations...

base_area = length * width
volume = base_area * height

mass = volume * density

# Print output (in json format)

output = {
            'base_area': base_area,
            'volume': volume,
            'mass': mass
         }

print('base_area =', base_area)
print('volume =', volume)
print('mass =', mass)

with open('output.json', 'w') as f:
    f.write(json.dumps(output))
    print('Successfully written results to output file "output.json"')

You can run this script through the Python interpreter:

$ python boxcalc.py
base_area = 48
volume = 72.0
mass = 72000.0
Successfully written results to output file "output.json"

If you run Linux, you can give the script executable permissions and run it directly:

$ ./boxcalc.py
base_area = 48
volume = 72.0
mass = 72000.0
Successfully written results to output file "output.json"

The script uses the json library to format the output dictionary in json format. The resulting output file output.json has the following content:

{"base_area": 48, "volume": 72.0, "mass": 72000.0}

This script has hard-coded parameter values. We can say that these values define a working reference case, where the results are known and “validated”, in some sense. This is a good starting point for creating a parameterized model template, which we will call box.

By default, Parsim assumes that model templates are stored in a subdirectory modelTemplates in the project directory. Let us start by creating the model template directory, and copy our existing model files there. For your convenience, the boxcalc.py script can be found in the demo subdirectory of your Parsim installation; in our case, Parsim is installed in $HOME/psm. As we copy, we change the name of the script to calc.py. Inside the project directory,

$ mkdir modelTemplate
$ mkdir modelTemplate/box
$ copy $HOME/psm/demo/boxcalc.py modelTemplate/box/calc.py

Now we go to the template directory, and continue there:

cd modelTemplate/box

In a real application, the executable for running the simulation would usually read input data from another file, but in our example the input data is hard-coded in the script itself. This means that the script itself will be parameterized. Let us start by adding the extension .macro to the file name, so that Parsim will parse it for parameter substitution when you create cases from the template. We also set execute permissions on the script file (permission of the parameterized file will be inherited by the resulting script file, when cases are created):

$ mv calc.py calc.py.macro
$ chmod u+x calc.py.macro

The file contains the following numerical model parameters: length, width, height and density. It also contains the string parameter color. We note that the name of the output file is also hard-coded in the script file; while we’re at it, we let this file name be a parameter, too.

We now create a default parameter file with the standard name default.parameters. We use the hard-coded values as the default values, as this represents our known and presumably validated reference case… The contents of default.parameters could then be:

#==========================================================
# Model template "Box"
#
# Computes base area, volume and mass of a rectangular box
#==========================================================
#-----------------------
# Geometry
#-----------------------
length:     12         # [m]
width:      4          # [m]
height:     1.5        # [m]
#-----------------------
# Material properties
#-----------------------
density:    1000       # [kg/m3] Density of the solid material
color:      'black'    # Color of the box
#-----------------------
# Configuration
#-----------------------
output_file: 'results.json'   # Holds scalar results, written as a json-format dictionary

Note that we this file is in parameter file format, and that we have used comments to document the model template.

The next step is to substitute the hard-coded values in the script file with the parameters we defined:

#! /usr/bin/env python

from __future__ import print_function
import json

# Model parameters

#- Geometry
length = $(length)
width = $(width)
height = $(height)

#- Material properties
density = $(density) # kg/m3
color = '$(color)'

# Calculations...

base_area = length * width
volume = base_area * height

mass = volume * density

# Print output (in json format)

output = {
            'base_area': base_area,
            'volume': volume,
            'mass': mass
         }

print('base_area =', base_area)
print('volume =', volume)
print('mass =', mass)

with open('$(output_file)', 'w') as f:
    f.write(json.dumps(output))
    print('Successfully written results to output file "%s"' % '$(output_file)')

Note that we put quotes around $(output_file), as the parameter substitution returns the string without quotes.

That’s it, our first model template is ready!

Creating a case and running the simulation

To create cases, we use the psm case command. See psm case for details. The following creates a case in a case directory case_ref, with the default parameters defined above:

$ psm case --template box ref
INFO: Found template in project template directory: C:\Users\Ola\PycharmProjects\psm\doc\demo\modelTemplates\box
INFO: Parsim case "ref" successfully created

In this simple example, we can easily make a custom case with parameters modified on the command-line. For example, let’s make a case “bigBox”, with higher box and larger density:

$ psm case --template box --define height=20,density=1200 bigBox
INFO: Found template in project template directory: C:\Users\Ola\PycharmProjects\psm\doc\demo\modelTemplates\box
INFO: Parsim case "bigBox" successfully created

Let’s run the “simulation” of this larger box, using the psm run command:

$ psm run bigBox calc.py
INFO: Executing command/script/executable: calc.py 
bigBox: Running executable...
INFO: Executable finished successfully (runtime: 0:00:00.059957)
   Executable : C:\Users\Ola\PycharmProjects\psm\doc\demo\case_bigBox\calc.py 
   stdout     : C:\Users\Ola\PycharmProjects\psm\doc\demo\case_bigBox\calc.out 
   stderr     : C:\Users\Ola\PycharmProjects\psm\doc\demo\case_bigBox\calc.err

As indicated by the console output, the standard output of the script is found in the file calc.out in the case directory:

Creating and running a study

It is equally simple to setup and operate on a whole parameter study, containing several cases, using the psm study command.

You would usually use a parameter file to define parameter values that are common to all cases and a caselist file to define the case names and the parameters that differ between cases. In our simple tutorial example, however, it is sufficient to use only a caselist file.

Let us assume that we want to create a study named variants, with case names and parameters defined in a caselist file named variants_caselist, as follows:

We now use the psm study command to create the study and all its cases:

$ psm study --template box --description "Variants in series A and B" --name "variants" variants_caselist
INFO: Found template in project template directory: C:\Users\Ola\PycharmProjects\psm\doc\demo\modelTemplates\box
INFO: Parsim study "variants" successfully created
INFO: Found template as absolute or relative path: C:\Users\Ola\PycharmProjects\psm\doc\demo\modelTemplates\box
INFO: Parsim case "A1" successfully created
INFO: Found template as absolute or relative path: C:\Users\Ola\PycharmProjects\psm\doc\demo\modelTemplates\box
INFO: Parsim case "A2" successfully created
INFO: Found template as absolute or relative path: C:\Users\Ola\PycharmProjects\psm\doc\demo\modelTemplates\box
INFO: Parsim case "A3" successfully created
INFO: Found template as absolute or relative path: C:\Users\Ola\PycharmProjects\psm\doc\demo\modelTemplates\box
INFO: Parsim case "B1" successfully created
INFO: Found template as absolute or relative path: C:\Users\Ola\PycharmProjects\psm\doc\demo\modelTemplates\box
INFO: Parsim case "B2" successfully created
INFO: Found template as absolute or relative path: C:\Users\Ola\PycharmProjects\psm\doc\demo\modelTemplates\box
INFO: Parsim case "B3" successfully created

This creates a study directory study_variants in the current directory; the study directory contains all the case directories. Note that we used option --name for naming the study and the option --description to provide useful information about its content.

We can run the simulation of all cases of the study with one single command:

$ psm run variants calc.py
INFO: Starts RUN operation on cases (executable: calc.py)...
INFO: Executing command/script/executable: calc.py 
A1: Running executable...
INFO: Executable finished successfully (runtime: 0:00:00.070959)
   Executable : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_A1\calc.py 
   stdout     : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_A1\calc.out 
   stderr     : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_A1\calc.err
INFO: Executing command/script/executable: calc.py 
A2: Running executable...
INFO: Executable finished successfully (runtime: 0:00:00.058967)
   Executable : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_A2\calc.py 
   stdout     : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_A2\calc.out 
   stderr     : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_A2\calc.err
INFO: Executing command/script/executable: calc.py 
A3: Running executable...
INFO: Executable finished successfully (runtime: 0:00:00.056948)
   Executable : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_A3\calc.py 
   stdout     : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_A3\calc.out 
   stderr     : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_A3\calc.err
INFO: Executing command/script/executable: calc.py 
B1: Running executable...
INFO: Executable finished successfully (runtime: 0:00:00.058966)
   Executable : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_B1\calc.py 
   stdout     : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_B1\calc.out 
   stderr     : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_B1\calc.err
INFO: Executing command/script/executable: calc.py 
B2: Running executable...
INFO: Executable finished successfully (runtime: 0:00:00.057967)
   Executable : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_B2\calc.py 
   stdout     : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_B2\calc.out 
   stderr     : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_B2\calc.err
INFO: Executing command/script/executable: calc.py 
B3: Running executable...
INFO: Executable finished successfully (runtime: 0:00:00.061950)
   Executable : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_B3\calc.py 
   stdout     : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_B3\calc.out 
   stderr     : C:\Users\Ola\PycharmProjects\psm\doc\demo\study_variants\case_B3\calc.err
INFO: Successfully finished RUN operation!

Collecting results from a Study

We can use the psm collect command to collect the output data from all cases and present it in one single table.

$ psm collect variants
INFO: Start collecting case results...
Case results files (input)   : ['results.json']
Study results file (output) : results.txt
INFO: Successfully finished collecting case results!

By default, results are read from a JSON formatted file results.json in the case directories. For the example here, a results file in a case would look something like this:

{"base_area": 40, "volume": 800, "mass": 720000}

The --input option can be used to specify a custom file path inside the case directory (a comma-separated list of multiple files is allowed). Unless a delimited format is requested (by defining a delimiter with the --delim option), the output is in tabular format, white fixed column spacing. The name of the output file is derived from the name of the (first) input file, unless specified explicitly with the --output option. ) In this example, the output is written to the file results.txt, located in the study directory:

CASENAME  base_area  mass     volume  density  height  length
A1        40         576000   480     1200     12      10    
A2        40         640000   640     1000     16      10    
A3        40         720000   800     900      20      10    
B1        60         864000   720     1200     12      15    
B2        60         960000   960     1000     16      15    
B3        60         1080000  1200    900      20      15    

If the input file is missing or incomplete for one or more cases, this will be reported. Only the succesfully processed cases will be included in the output file.

Every time the collect command is run (e.g. collecting additional results from another simulation in the same Study), a tabular text file “study.results” inside the Study directory will be updated with the new data. This file will then contain all aggregated results for the study. All cases are reported in this file, even if data is missing. Missing data is reported as “NaN” in the table. Input parameters are not included in “study.results”. These can instead be found separately in the file “study.caselist”. The files “study.results” and “study.caselist” could be conveniently imported into pandas DataFrame objects for further processing. As an example, consider a study with 16 cases created from the “box” template. One simulation executable outputs results variables “basearea”, “mass” and “volume” for all 16 cases. Another simulation outputs the result variable “d_eff” in another results file, but this simulation fails for several of the cases. The psm collect command is run separately after each of the simulations, to collect results. The file “study.results” would now look something like this:

CASENAME  base_area      volume           mass
       1  69.976002  118.945208  124886.521349
       2  49.999998   84.989997   89235.246931
       3  42.007998   71.405195   74971.884491
       4  30.016002   51.021200   53569.709150
       5  69.976002   90.982798   95527.388551
       6  49.999998   65.009997   68257.246770
       7  42.007998   54.618799   57347.008010
       8  30.016002   39.026806   40976.194750
       9  69.976002  118.945208  113003.895050
      10  49.999998   84.989997   80744.746270
      11  42.007998   71.405195   67838.505510
      12  30.016002   51.021200   48472.691250
      13  69.976002   90.982798   86438.207050
      14  49.999998   65.009997   61762.748029
      15  42.007998   54.618799   51890.589990
      16  30.016002   39.026806   37077.416851

Note that all output variables of both simulations are included in the file, but there is missing data, “NaN”, in the “d_eff” column for some of the cases.

Object information and event logs

We can use the psm info to get information about the properties of a case, study or about the project as a whole. For example, let’s look at the properties of the bigBox case:

$ psm info bigBox
Case ID             : bigBox
Creation date       : 2020-07-30 12:03:22
Description         : 
Project name        : myProject
Study name          : 
Template path       : C:\Users\Ola\PycharmProjects\psm\doc\demo\modelTemplates\box
Creation log file   : C:\Users\Ola\PycharmProjects\psm\doc\demo\case_bigBox\.psm\create.log
Parsim version      : 2.0.0
Project path        : C:\Users\Ola\PycharmProjects\psm\doc\demo
Study path          : 
--------------------------------------------------------
User parameters (command-line or parameter file)
--------------------------------------------------------
density               : 1200
height                : 20
--------------------------------------------------------
Default parameters (defined in template)
--------------------------------------------------------
color                 : black
length                : 12
output_file           : results.json
width                 : 4

All Parsim objects (cases, studies and project) have an event log, where all events and operations are logged. The psm log command prints the event log to the console. For example, we look at the event log of the bigBox case:

$ psm log bigBox
2020-07-30 12:03:22 - INFO: Parsim case "bigBox" successfully created
2020-07-30 12:03:23 - INFO: Executing command/script/executable: calc.py 
2020-07-30 12:03:23 - INFO: Executable finished successfully (runtime: 0:00:00.059957)
   Executable : C:\Users\Ola\PycharmProjects\psm\doc\demo\case_bigBox\calc.py 
   stdout     : C:\Users\Ola\PycharmProjects\psm\doc\demo\case_bigBox\calc.out 
   stderr     : C:\Users\Ola\PycharmProjects\psm\doc\demo\case_bigBox\calc.err