Configuration file¶
This page walks you through the structure of a Nassu configuration file, from the smallest working example to the advanced features that keep large configurations manageable.
A configuration file describes one or more simulations. Read it top to bottom: the overall structure shows the skeleton every file shares, then each later section introduces one mechanic you can layer on as your needs grow (reusing a base setup, sweeping a parameter, sharing values, splitting across files). You do not need all of them at once. Start with the skeleton and add features when a real need appears.
General structure¶
The general structure of the .yaml configuration file is
simulations:
# Series of simulations
- name: SimulationName
# False if you don't wish to run the simulation
# Defaults to true
run_simul: true
# Number of time steps to run
n_steps: 50000
# Folder to use as base for saving output
save_path: "output/path/save"
# Frequency to report simulation status
report: {start_step: 0, end_step: 0, frequency: 1000}
# Domain's properties and specifications
domain: ...
# Data for export
data: ...
# Models to use
models: ...
# Another simulation in the same file
- name: AnotherSimulation
n_steps: 20000
save_path: "another/output/path/save"
report: {start_step: 0, end_step: 0, frequency: 1000}
domain: ...
data: ...
models: ...
Note
For a full example of a configuration, check the kitchensink.
A file holds a series of simulations, each one self-describing.
Every simulation has, among other things, a name that must be unique in the file, and three important fields that together define the case:
Domain: the simulated volume, the bodies inside it, and the grid refinement. This is where the flow happens and what it flows around.
Data and Checkpoint: which fields and time series are exported, when, and how the run can be restarted. This is what you get back.
Models: the numerical methods and their parameters (collision operator, LES, IBM, boundary conditions, initialization, precision). This is how the flow is solved.
Each of these has its own topic page with the full list of options. Before diving into them, the rest of this page covers the file-level mechanics that apply across all simulations: reusing setups, generating variants, sharing values, and splitting configurations across files.
Parent simulation¶
It’s very common to have two very similar simulations, that differ only in some configurations. Nassu supports using a parent simulation for that.
simulations:
# Parent simulation
- name: parentSimulation
...
# Simulation that inherits values from the parent
- name: childSimulation
parent: parentSimulation
...
# Key value to overwrite
key_to_overwrite: value
In this configuration, the child simulation inherits all configuration from the parent and overwrites only the specifics keys present in its configuration. The rules for overwriting are:
If the key in the child is a dictionary and exists in the parent, it proceeds to check its dictionary keys
If the key in the child is a dictionary and does not exist in the parent, it adds this key to the configuration
If the key in the child is not a dictionary, it’s overwritten or added to the configuration
For example
simulations:
- name: parentSimulation
domain:
domain_size:
x: 16
y: 16
z: 32
bodies:
sphere:
...
- name: childSimulation
parent: parentSimulation
domain:
domain_size:
x: 32
bodies:
cube:
...
In this case the data.domain_size.x will be overwritten in the child simulation, maintaining the y and z values the same.
For the data.bodies field, the child simulation will have both the sphere and the cube as its keys.
Preventing inheritance (!not-inherit)¶
There are times when we want to inherit most fields, but sometimes we don’t want to.
In the previous example, suppose we didn’t want the sphere to be present in the child simulation.
Then we can use the !not-inherit tag
simulations:
- name: parentSimulation
domain:
bodies:
sphere:
...
- name: childSimulation
parent: parentSimulation
domain:
bodies: !not-inherit
cube:
...
In this case it prevents any inheritance in the data.bodies field, so that we have only the cube in it.
This is very useful when you want to prevent just some specific fields of being inherited.
Simulation series (!unroll)¶
One common use case is when we want to make the same simulation, but just change some reference values or configuration.
Suppose that the only difference between two cases are the velocity, or the domain size in x, or the viscosity.
For these cases we use the !unroll tag
simulations:
- name: mySimulation
models:
LBM:
tau: !unroll [0.51, 0.505]
F:
x: !unroll [1e-5, 5e-6]
Here two simulations will be generated, one using tau=0.51 and F.x=1e-5 and the other one with tau=0.505 and F.x=5e-6.
They will both have the same name, but the first one will have ID 0 and the other ID 1.
Note
Every simulation has an ID. If it doesn’t use any unroll, it will be 0. If it unrolls any fields, the ID will be the index in the unroll list.
Note that the two !unroll tags have the same length, this is required.
If any list has a different size than the other ones, the configuration will raise an error.
Note
For parent simulations, everything is inherited, including the tags such as !unroll.
If you don’t wish to use any key, you must overwrite it.
Variables substitution (!sub)¶
There is a field in the simulation, variables, that is used to define the simulation variables and other values that may be used for other fields.
These ones can be referenced in other places as ${key_name} when using the !sub tag (it also works for the !math tag).
variables:
base_path: /home/ubuntu/my_user
u_ref: 0.05
height_ref: 8
time_scale:
reference_cst: !math ${height_ref}/${u_ref}
n_csts_run: 300
time_steps_develop: 5000
domain:
sizes: [32, 48, 64]
simulations:
- name: mySimulation
save_path: !sub ${base_path}/simulation0/results/
time_steps: !math ${time_scale.time_steps_develop} + ${time_scale.n_csts_run}*${time_scale.reference_cst}
domain:
domain_size:
x: !math ${domain.sizes[0]}
y: !math ${domain.sizes[2]}
z: !math ${domain.sizes[1]}
Special Variables¶
There are some internal special variables that you can use for substitution:
variables:
version: !sub "${NASSU_VERSION}" # x.y.z
filepath: !sub "${NASSU_FILE_PATH}" # docs/source/user_guide/02_config/file/kitchensink.nassu.yaml
foldername: !sub "${NASSU_FILE_FOLDER}" # docs/source/user_guide/02_config/file
save_folder: !sub "${NASSU_FILE_FOLDER}/results/${NASSU_VERSION}/"
Anchors and aliases¶
Suppose that we have a value or series of fields that are used in multiple parts of the file. It would be good if it was possible for us to create in one place and let the other fields reference it. Using YAML anchors and aliases allows us to do just that. They serve as a reference that can be pointed to by anywhere in the file
variables:
base_path: &ANCHOR_BASE_PATH my/path/to/use
interval_export: &ANCHOR_INTERVAL_EXPORT 5000
report_cfg: &ANCHOR_REPORT_CFG:
start_step: 1000
end_step: !math 1000*20
frequency: 100
simulation:
- name: mySimulation
output: !concat [*ANCHOR_BASE_PATH, "output/"]
report: *ANCHOR_REPORT_CFG
domain:
bodies:
building:
geometry_path: !sub "${base_path}/lnas/building.yaml"
data:
instantaneous:
default: { interval: { frequency: *ANCHOR_INTERVAL_EXPORT }, macrs: [rho, u] }
Using this feature it is easier to reference a common information in the file and do tricks as the example presents.
It’s important to notice that we create a special key for these anchors, variables, to keep all the anchors centralized.
The other tags also work in the anchors, so you can use !math, !concat or !range and it will work as expected.
Configuration from other file (dependencies)¶
It’s also possible to distribute the configurations among different files. Suppose you have a file that defines a base configuration for multiple purposes or cases. One way to reference these configurations would be to simply copy and paste this configuration into the other cases. But if anything changes in the default configuration, all files must be manually updated.
To facilitate this job of referencing a common configuration, it’s possible to load the configurations from other files before starting our own.
# ./base_simul.yaml
simulations:
- name: baseSimulation
models:
LBM:
vel_set: D2Q9
coll_oper: RRBGK
tau: 0.501
F:
x: 1e-6
# ./my_simul.yaml
dependencies:
sim_cfg_files: [./base_simul.yaml]
simulation:
- name: actualSimulation
parent: baseSimulation
domain:
bodies:
building: ...
The dependencies.sim_cfg_files consists of a list of configuration files with simulations to load.
These loaded simulations can be used as parent of our own.
It is important to mention that the simulations from other files are not added to the list of simulations of the current file.
Important
The path used must be relative to the current path of your shell, not to the file itself.
The loading algorithm is able to check for cyclic dependencies and the order in which the simulations must be stated.
Full structure example¶
This capstone example combines every mechanic introduced above (a dependency, shared variables with anchors and tags, a parent simulation, !not-inherit, and an !unroll sweep) into one realistic building case. Read it as a checklist of what you have learned rather than as a starting template.
dependencies:
sim_cfg_files: [parent_cfg.yaml]
variables:
base_path: &MY_BASE_PATH: "building_case"
interval_export: &INTERVAL_EXPORT: 5000
report_cfg: &REPORT_CFG:
start_step: 1000
end_step: !math ${report_cfg.start_step}*20
frequency: 100
simulation:
- name: BuildingSimulation
parent: baseSimulation # Defined in parent_cfg.yaml
output: !sub ${base_path}/output/"
report: *REPORT_CFG
domain:
bodies:
building:
geometry_path: !concat [*MY_BASE_PATH, "/lnas/building.yaml"]
refinement: !not-inherit
static:
default:
bodies:
- body_name: building
lvl: 5
normal_offsets: !range [-0.25, 2.1, 0.25]
data:
instantaneous:
default: { interval: { frequency: *INTERVAL_EXPORT }, macrs: [rho, u] }
models:
LBM:
vel_set: !unroll [D3Q27, D3Q19]
coll_oper: !unroll [RRBGK, HRRBGK]