/*! @page pag_runfile Run File
\section sec_runfile Run File

This section describes the format of a run-file.
Run-files are a new way of passing arguments to a PMSCO process which avoids cluttering up the command line.
It is more flexible than the command line
because run-files can assign a value to any property of the project object in an abstract way.
Moreover, there is no necessity for the project code to parse the command line.


\subsection sec_runfile_how How It Works

Run-files are text files in [JSON](https://en.wikipedia.org/wiki/JSON) format
which shares most syntax elements with Python.
JSON files contain nested dictionaries, lists, strings and numbers.

In PMSCO, run-files contain a dictionary of parameters for the project object
which is the main container for calculation parameters, model objects and links to data files.
An abstract run-file parser reads the run-file,
constructs the specified project object based on the custom project class
and assigns the attributes of the project object.
It's important to note that the parser does not recognize specific data types or classes.
All specific data handling is done by the instantiated objects, mainly the project class.

The parser can handle the following situations:

- Strings, numbers as well as dictionaries and lists of simple objects can be assigned directly to project attributes.
- If the project class defines an attribute as a _property_,
  the class can execute custom code to import or validate data.
- The parser can instantiate an object from a class in the namespace of the project module
  and assign its properties.


\subsection sec_runfile_general General File Format

Run-files must adhere to the [JSON](https://en.wikipedia.org/wiki/JSON) format,
which shares most syntax elements with Python.
Specifically, a JSON file can declare dictionaries, lists and simple objects
such as strings, numbers and `null`.
As one extension to plain JSON, PMSCO ignores line comments starting with a hash `#` or double-slash `//`.
This can be used to temporarily hide a parameter from the parser.

For example run-files, have a look at the twoatom demo project.


\subsection sec_runfile_project Project Specification


The following minimum run-file demonstrates how to specify the project at the top level:

~~~~~~{.py}
{
  "project": {
    "__module__": "projects.twoatom.twoatom",
    "__class__": "TwoatomProject",
    "mode": "single",
    "output_file": "twoatom0001"
  }
}
~~~~~~

Here, the `project` keyword denotes the dictionary that is used to construct the project object.

Within the project dictionary, the `__module__` key selects the Python module file that contains the project code,
and `__class__` refers to the name of the actual project class.
Further dictionary items correspond to attributes of the project class.

The module name is the same as would be used in a Python import statement.
It must be findable on the Python path.
PMSCO ensures that the directory containing the `pmsco` and `projects` sub-directories is on the Python path.
The class name must be in the namespace of the loaded module.

As PMSCO starts, it imports the specified module,
constructs an object of the specified project class,
and assigns any further items to project attributes.
In the example above, `twoatom0001` is assigned to the `output_file` property.
Any attributes not specified in the run-file will remain at their default values
that were set byt the `__init__` method of the project class.

Note that parameter names must start with an alphabetic character, else they are ignored.
This provides another way to temporarily ignore an item from the file besides line comments.

Also note that PMSCO does not spell-check parameter names.
The parameter values are just written to the corresponding object attribute.
If a name is misspelled, the value will be written under the wrong name and missed by the code eventually.

PMSCO carries out only some most important checks on the given parameter values.
Incorrect values may lead to improper operation or exceptions later in the calculations.


\subsection sec_runfile_common Common Arguments

The following table lists some important parameters controlling the calculations.
They are declared in the pmsco.projects.Project class.

| Key | Values | Description |
| --- | --- | --- |
| mode | `single` (default), `grid`, `swarm`, `genetic`, `table`, `test`, `validate` | Operation mode. `validate` can be used to check the syntax of the run-file, the process exits before starting calculations. |
| directories | dictionary | This dictionary lists common file paths used in the project. It contains keys such as `home`, `project`, `output` (see documentation of Project class in pmsco.project). Enclosed in curly braces, the keys can be used as placeholders in filenames. |
| output_dir | path | Shortcut for directories["output"] |
| data_dir | path | Shortcut for directories["data"] |
| job_name | string, must be a valid file name | Base name for all produced output files. It is recommended to set a unique name for each calculation run. Do not include a path. The path can be set in _output_dir_. |
| cluster_generator | dictionary | Class name and attributes of the cluster generator. See below. |
| atomic_scattering_factory | string<br>Default: InternalAtomicCalculator from pmsco.calculators.calculator | Class name of the atomic scattering calculator. This name must be in the namespace of the project module. |
| multiple_scattering_factory | string<br>Default: EdacCalculator from  pmsco.calculators.edac | Class name of the multiple scattering calculator. This name must be in the namespace of the project module. |
| model_space | dictionary | See @ref sec_runfile_space below. |
| domains | list of dictionaries | See @ref sec_runfile_domains below. |
| scans | list of dictionaries | See @ref sec_runfile_scans below. |
| optimizer_params | dictionary | See @ref sec_runfile_optimizer below. |

The following table lists some common control parameters and metadata
that affect the behaviour of the program but do not affect the calculation results.
The job metadata is used to identify and describe a job in the results database if requested.

| Key | Values | Description |
| --- | --- | --- |
| job_tags | list of strings | User-specified job tags (metadata). |
| description | string | Description of the calculation job (metadata) |
| time_limit | decimal number<br>Default: 24. | Wall time limit in hours. The optimizers try to finish before the limit. This cannot be guaranteed, however. |
| keep_files | list of file categories | Output file categories to keep after the calculation. Multiple values can be specified and must be separated by spaces. By default, cluster and model (simulated data) of a limited number of best models are kept. See @ref sec_runfile_files below. |
| keep_best | integer number<br>Default: 10 | number of best models for which result files should be kept. |
| keep_level | integer number<br>Default: 1 | numeric task level down to which files are kept. 1 = scan level, 2 = domain level, etc. |
| log_level | DEBUG, INFO, WARNING, ERROR, CRITICAL | Minimum level of messages that should be added to the log. Empty string turns off logging. |
| log_file | file system path<br>Default: job_name + ".log". | Name of the main log file. Under MPI, the rank of the process is inserted before the extension. The log name is created in the working directory.  |


\subsection sec_runfile_space Model Space

The `model_space` parameter is a dictionary of model parameters.
The key is the name of the parameter as used by the cluster and input-formatting code,
the value is a dictionary holding the `start`, `min`, `max`, `step` values to be used by the optimizer.

~~~~~~{.py}
{
  "project": {
    // ...
    "model_space": {
      "dAB": {
        "start": 2.109,
        "min": 2.0,
        "max": 2.25,
        "step": 0.05
      },
      "pAB": {
        "start": 15.0,
        "min": 0.0,
        "max": 30.0,
        "step": 1.0
      },
      // ...
    }
  }
}
~~~~~~


\subsection sec_runfile_domains Domains

Domains is a list of dictionaries.
Each dictionary holds keys describing the domain to the cluster and input-formatting code.
The meaning of these keys is up to the project.

~~~~~~{.py}
{
  "project": {
    // ...
    "domains": [
      {"surface": "Te", "doping": null, "zrot": 0.0},
      {"surface": "Te", "doping": null, "zrot": 60.0}
    ],
  }
}
~~~~~~


\subsection sec_runfile_scans Experimental Scan Files

The pmsco.project.Scan objects used in the calculation cannot be instantiated from the run-file directly.
Instead, the scans object is a list of scan creators/loaders which specify what to do to create a Scan object.
The pmsco.project module defines three scan creators: ScanLoader, ScanCreator and ScanKey.
The following code block shows an example of each of the three:

~~~~~~{.py}
{
  "project": {
    // ...
    "scans": [
      {
        "__class__": "pmsco.project.ScanCreator",
        "filename": "twoatom_energy_alpha.etpai",
        "emitter": "N",
        "initial_state": "1s",
        "positions": {
          "e": "np.arange(10, 400, 5)",
          "t": "0",
          "p": "0",
          "a": "np.linspace(-30, 30, 31)"
        }
      },
      {
        "__class__": "pmsco.project.ScanLoader",
        "filename": "{project}/twoatom_hemi_250e.etpi",
        "emitter": "N",
        "initial_state": "1s",
        "is_modf": false
      },
      {
        "__class__": "pmsco_project.ScanKey",
        "key": "Ge3s113tp"
      }
    ]
  }
}
~~~~~~

The class name must be specified as it would be called in the custom project module.
`pmsco.project` must, thus, be imported in the custom project module.

The *ScanCreator* object creates a scan using Numpy array constructors in `positions`.
In the example above, a two-dimensional rectangular energy-alpha scan grid is created.
The values of the positions axes are passed to Python's `eval` function
and must return a one-dimensional Numpy `ndarray`.

The `emitter` and `initial_state` keys define the probed core level.

The *ScanLoader* object loads a data file, specified under `filename`.
The filename can include a placeholder which is replaced by the corresponding item from Project.directories.
Note that some of the directories (including `project`) are pre-set by PMSCO.
It is recommended to add a `data` key under `directories` in the run-file
if the data files are outside of the PMSCO directory tree.
The `is_modf` key indicates whether the file contains a modulation function (`true`) or intensity (`false`).
In the latter case, the modulation function is calculated after loading.

The *ScanKey* is the shortest scan specification in the run-file.
It is a shortcut to a complete scan description in `scan_dict` dictionary in the project object.
The `scan_dict` must be set up in the `__init__` method of the project class.
The `key` item specifies which key of `scan_dict` should be used to create the Scan object.

Each item of `scan_dict` holds a dictionary
that in turn holds the attributes for either a `ScanCreator` or a `ScanLoader`.
If it contains a `positions` key, it represents a `ScanCreator`, else a `ScanLoader`.


\subsection sec_runfile_optimizer Optimizer Parameters

The `optimizer_params` is a dictionary holding one or more of the following items.

| Key | Values | Description |
| --- | --- | --- |
| pop-size | integer<br>The default value is the greater of 4 or the number of parallel calculation processes. | Population size (number of particles) in swarm and genetic optimization mode. |
| seed-file | file system path | Name of the population seed file. Population data of previous optimizations can be used to seed a new optimization. The file must have the same structure as the .pop or .dat files. See @ref pmsco.project.Project.seed_file. |
| table-file | file system path | Name of the model table file in table scan mode. |


\subsubsection sec_runfile_files File Categories

The following category names can be used with the `keep_files` option.
Multiple names can be specified as a list.

| Category | Description | Default Action |
| --- | --- | --- |
| all | shortcut to include all categories | |
| input |      raw input files for calculator, including cluster and phase files in custom format | delete |
| output |     raw output files from calculator | delete |
| atomic |     atomic scattering and emission files in portable format | delete |
| cluster |    cluster files in portable XYZ format for report | keep |
| debug |      debug files |  delete |
| model |       output files in ETPAI format: complete simulation  (a_-1_-1_-1_-1) | keep |
| scan |       output files in ETPAI format: scan (a_b_-1_-1_-1) |  keep |
| domain |     output files in ETPAI format: domain (a_b_c_-1_-1) |  delete |
| emitter |    output files in ETPAI format: emitter (a_b_c_d_-1) |  delete |
| region |     output files in ETPAI format: region (a_b_c_d_e) |  delete |
| report|      final report of results | keep always |
| population |  final state of particle population | keep |
| rfac |        files related to models which give bad r-factors, see warning below | delete |

\note
The `report` category is always kept and cannot be turned off.
The `model` category is always kept in single calculation mode.

\warning
If you want to specify `rfac` with the `keep_files` option,
you have to add the file categories that you want to keep, e.g.,
`"keep_files": ["rfac", "cluster", "model", "scan", "population"]`
(to return the default categories for all calculated models).
Do not specify `rfac` alone as this will effectively not return any file.


\subsection sec_runfile_schedule Job Scheduling

To submit a job to a resource manager such as Slurm, add a `schedule` section to the run file
(section ordering is not important):

~~~~~~{.py}
{
  "schedule": {
    "__module__": "pmsco.schedule",
    "__class__": "PsiRaSchedule",
    "nodes": 1,
    "tasks_per_node": 24,
    "walltime": "2:00",
    "manual_run": true,
    "enabled": true
  },
  "project": {
    "__module__": "projects.twoatom.twoatom",
    "__class__": "TwoatomProject",
    "mode": "single",
    "output_file": "{home}/pmsco/twoatom0001",
    ...
  }
}
~~~~~~

In the same way as for the project, the `__module__` and `__class__` keys select the class that handles the job submission.
In this example, it is pmsco.schedule.PsiRaSchedule which is tied to the Ra cluster at PSI.
For other machines, you can sub-class one of the classes in the pmsco.schedule module and include it in your project module.

The parameters of pmsco.schedule.PsiRaSchedule are as follows.
Some of them are also used in other schedule classes or may have different types or ranges.

| Key | Values | Description |
| --- | --- | --- |
| nodes | integer: 1..2 | Number of compute nodes (main boards on Ra). The maximum number available for PEARL is 2. |
| tasks_per_node | integer: 1..24, 32 | Number of tasks (CPU cores on Ra) per node. Jobs with less than 24 tasks are assigned to the shared partition. |
| wall_time | string: [days-]hours[:minutes[:seconds]] <br> dict: with any combination of days, hours, minutes, seconds | Maximum run time (wall time) of the job. |
| manual | bool | Manual submission (true) or automatic submission (false). Manual submission allows you to inspect the job files before submission. |
| enabled | bool | Enable scheduling (true). Otherwise, the calculation is started directly (false).

@note The calculation job may run in a different working directory than the current one.
It is important to specify absolute data and output directories in the run file (project/directories section).

*/
