# Configuration file The general structure of the `.yaml` configuration file is ```yaml 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](./file/kitchensink.md). ``` It has a series of simulations, each one describing its configurations. Each simulation has, among other things, a name, that must be unique in the file, and three important fields: * [Domain](./file/domain.md) * [Data and Checkpoint](./file/data.md) * [Models](./file/models.md) These fields configure some important aspects that are described in their specific topics. But there are other very important fields and inside logic for the configuration file that we must explain first. ## Parent simulation It's very common to have two very similar simulations, that differs only in some configurations. Nassu supports using a parent simulation for that. ```yaml simulations: # Parent simulation - name: parentSimulation ... # Simulation that inheritages values from 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 dicitonary and exists in the parent, it proceeds to check its dictionary keys * If the key in the child is a dicitonary and not exists 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 ```yaml 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, mantaining the `y` and `z` value the same. For the `data.bodies` field, the child simulation will have both the `sphere` and the `cube` as its keys. ### Preveting inheritage (`!not-inherit`) There are times when we want to inheritage 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 ```yaml simulations: - name: parentSimulation domain: bodies: sphere: ... - name: childSimulation parent: parentSimulation domain: bodies: !not-inherit cube: ... ``` In this case it prevents any inheritage 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 ```yaml 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). ```yaml 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: ```yaml 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}/" ``` ## Custom tags YAML support the use of custom [tags](https://yaml.org/spec/1.2.2/#3212-tags) that can have a custom behavior when loading the file. We've already showed example of this using `!unroll` and `!not-inherit`. But there are other custom tags that we support, these are: `!math` to evaluate mathematical expressions ```yaml my_45_angle_in_radians: !math 45 * (3.1415 / 180) works_on_lists_too: !math [1/2, (5*1.125)^2, 0.9*0.49] can_use_variables: !math ${my_reference_variable}*2/3 or_variables_in_lists: !math [${domain.size[0]}, ${domain.size[1]}*2, ${domain.size[2]}/4] functions_and_constants_too: !math cos(45*pi/180) number_of_euler_in_case: !math log(e^10) # The functions are the one from `math` module # https://docs.python.org/3/library/math.html available_constants: ["e", "pi"] # If there's any error, check if `math` in your python version has the function you tried to use available_functions: [ # Trigonometric functions "cos", "sin", "tan", "acos", "asin", "atan", "atan2", "degrees", "radians", # Log and exp functions "log", "log1p", "log2", "log10", "exp", "exp2", "expm1", # Other utils functions "ceil", "floor", "fabs", "fmod", "prod", # Python functions "min", "max", ] ``` `!concat` to concatenate strings ```yaml simple_concat: !concat ["first_string/", "second_one"] # results in "first_string/second_one" ``` `!range` to generate a range of values ```yaml # Note that the end is exclusive [start, end) range_end: !range [5] # [0, 1, 2, 3, 4] range_start_end: !range [2, 6] # [2, 3, 4, 5] range_start_end_step: !range [-1, 1, 0.5] # [-1, -0.5, 0, 0.5] ``` ```{important} These tags can be used in any value of the configuration. Their combination may not be compatible (as a `!math` inside a `!concat`), but it's encouraged to try out if desired. ``` ## 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](https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases) allows us to do just that. They serve as a reference that can be pointed to by anywhere in the file ```yaml 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: lnas_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. ```yaml # ./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. 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 A full example, using all presented capabilities, is presented below ```yaml 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: lnas_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] ``` ```{eval-rst} .. toctree:: :maxdepth: 1 :hidden: Domain Data Models Kitchen sink ```