runme performs the preliminary steps needed to run a model program — create a
run directory, copy the executable and parameter files, link input data — and
then runs it, either in the background or by submitting to a SLURM queue. It can
run a single simulation or a whole ensemble, and can generate ensembles by
factorial combination or by sampling parameter distributions.
runme is an installable Python package providing the runme command. It is
self-contained: the ensemble functionality that used to require the separate
runner library (via jobrun) is now built in.
runme builds on the runner package by
Mahé Perrette, a flexible framework for sampling parameters and for running and
analyzing model ensembles. runme reimplements a focused subset of that
functionality — factorial combination, Latin-hypercube and Monte-Carlo sampling,
and ensemble execution — while stripping away the more complex methods (Bayesian
analysis, iterative importance sampling, weighted resampling). Here the ensemble
"run" step is handled simply by calling runme itself, rather than by wrapping a
separate per-simulation script.
If you need those more advanced methods, use the original
perrette/runner package directly.
pip install git+https://github.com/fesmc/runmeThis puts the runme command on your path. To upgrade later, add --upgrade (or
--force-reinstall) to the same command. The only dependencies are numpy and
scipy (used by the ensemble sampling), installed automatically.
Once installed, runme --init scaffolds a project (see below), runme --config
manages your local settings, and runme --list shows the available HPC queues.
The quickest way is runme --init in your model directory. It creates a
.runme/ directory with templates to edit:
cd $MODEL_PATH
runme --init # creates .runme/{info.json, runme_config, queues.json}
# edit .runme/info.json for your model; set hpc/account in .runme/runme_config
runme --config # creates your local .runme_config from the templateRun runme --init again at any time to validate the configuration: it reports
missing files or keys, flags unset placeholders, checks that your hpc exists in
the queues file, and fills in any genuinely-missing template files (without
overwriting existing ones).
The rest of this section explains the files --init scaffolds.
.runme/info.json describes the executables, the parameter files to copy, and
the input folders to link. It is the one genuinely project-specific file:
{
"exe_default" : "libmodel/bin/model.x",
"par_path_as_argument" : true,
"exe_aliases" : { "main" : "libmodel/bin/model.x" },
"grp_aliases" : {},
"par_paths" : { "alias" : "None" },
"files" : ["None"],
"dir-special" : { "None" : "target_dir_name" },
"links" : ["input", "ice_data", "maps"],
"par_defaults" : [],
"par_library" : []
}par_defaults and par_library are optional; see Running a single simulation.
Then create a local .runme_config with your per-host settings (the HPC name,
account, email, OpenMP threads, and the paths to your info and queues files):
{
"hpc" : "dkrz_levante",
"account" : "ba1442",
"jobname" : "model",
"omp" : 16,
"mem" : -1,
"queues_file" : ".runme/queues.json",
"info_file" : ".runme/info.json",
"email" : "",
"mail_type" : ["FAIL", "REQUEUE"]
}That's it. The queues definition and SLURM submit templates are shipped with the
package, so you don't need to copy them into every project. runme resolves each
configuration file through a fallback chain:
- the path given in
.runme_config(your project's.runme/), ~/.config/runme/<name>(a user-wide override),- the default bundled in the package.
So a project usually carries only .runme/info.json and .runme_config. Provide
your own .runme/queues.json or .runme/submit_slurm[_omp] only when you need to
override the packaged defaults.
- Inspect available queues with
runme --list(orrunme --list HPC). - Inspect the active config with
runme --config. - If you use a new HPC not in the packaged
queues.json, add it (and please open an issue/PR so the package stays up to date). On the new cluster's login node,runme check queues [NAME]introspects SLURM (viascontrol/sacctmgr), prints a ready-made block of the(partition, qos, wall)triplets you can actually submit to, and offers to merge it into your queues file. Aliases are guessed from whichever of partition/qos varies, falling back toqueue1, queue2, ...(rename them afterwards) when both vary.
runme -o output/run -n par/model.nml # stage only (create rundir, copy files)
runme -r -o output/run -n par/model.nml # stage and run in the background
runme -s -o output/run -n par/model.nml -q short # stage and write a SLURM submit script
runme -r -s -o output/run -n par/model.nml -q short # stage, write submit script, and sbatch itThe steps carried out are:
- create the run directory,
- copy the executable into it,
- copy the relevant parameter files,
- link the input-data folders,
- run in the background or submit to the queue (or, with neither
-rnor-s, just stage everything).
Parameters can be modified inline with -p KEY=VALUE [KEY=VALUE ...]; the changes
are written to the parameter file copied into the run directory. A -p key must
already exist in one of the parameter files being staged — runme will not create
new parameters, so a typo'd name is reported rather than silently added.
The exception is a model whose parameter files list only overrides of a full
defaults file. Name that file in info.json:
"par_defaults" : ["input/model_defaults.nml"]and a -p key absent from the staged files is accepted if the defaults declare
it, and inserted into its group (or a new group) in the copied parameter file.
Group renames are followed: a defaults parameter whose value names another
defaults group (e.g. nml_dyn = "dyn") acts as a pointer, so with
nml_dyn = "dyn_north" in the parameter file, -p dyn_north.x=1 is checked
against dyn.x, while -p dyn.x=1 is an error.
A model may also read some namelist groups from a shared library file when the
parameter file lacks them. Name such files in info.json:
"par_library" : ["input/forcing_db.nml"]and a -p key whose group is in none of the staged files but is in a library
file (the first one holding it) copies the whole group, all its parameters,
into the parameter file before the override is applied, so the staged group is
complete. The key must exist in that group. Library groups are looked up after
the staged files and before par_defaults. With several staged files, a new
group (from the library or par_defaults) goes into the -n file.
A -p value takes the type of the parameter's current value (in the staged file,
or else the defaults): True, .true. or T set a logical, an integer sets a
real as 10.0, and a number sets a string parameter as a quoted string.
A value may be a vector, written as a bracket literal: -p 'ctl.years=[10,20,30]'
sets a single array-valued parameter (serialized as [10, 20, 30] in TOML/.jl,
space-separated in a namelist). Commas inside the brackets belong to the vector,
not to an ensemble dimension. Quote the entry — [ is a glob character in most
shells.
Parameter files are read and written in the format implied by their extension:
Fortran namelist (.nml, .par), TOML (.toml), JSON (.json), and a flat
line format (.jl) of group.name = value assignments — one per line, which
also happens to be valid Julia (the file includes cleanly when the matching
structs already exist). All use the same group.name keys, so a TOML
[control] table (or a JSON {"control": {...}} object) with tau = 1.0 is
addressed as control.tau, exactly like the namelist group.
For projects whose executable takes the parameter file as an argument
(par_path_as_argument=true), -n names that file: it is copied into the run
directory and passed to the model.
For projects that read their own fixed-name parameter files
(par_path_as_argument=false, e.g. climber-x), -n is optional and acts as
an overlay: the parameters in the named file are merged into the project's
default parameter files. Precedence is
default parameter files → -n overlay → -p overrides
so a -p flag wins over the same parameter set in the overlay file.
A case is just a normal (partial or complete) parameter file (namelist, TOML,
JSON, or .jl) kept in a cases/ folder, capturing a configuration worth
reusing. Use one via -n:
pass its path, or pass a bare name and runme looks it up under cases/
(cases/NAME, or cases/NAME.* when a single file matches, so the extension can
be omitted):
runme -r -o output/run -n spinup # uses cases/spinup(.nml)
runme -r -o output/run -n spinup -p ctl.year=10 # case, then tweak on topBecause a case is loaded through the overlay path above, -p overrides layer on
top of it — handy for running permutations from a saved baseline.
Save the parameters applied in any run directory as a case with:
runme case save NAME -o RUNDIRThis reads RUNDIR/runme.json and writes its applied parameters as a partial
parameter file to cases/NAME — cases/NAME.nml by default, or the format
given by an explicit extension (e.g. NAME.toml). It works for an ensemble
member directory too.
Every run directory — for a single simulation or for each ensemble member — is self-describing and contains:
- the executable, the (parameter-edited) parameter files, and the input-data links;
runme.json— a machine-readable record of the run: the fullrunmecommand, the model command line, the applied parameters, the git revision, and a status (staged/prepared/running/submitted);params.txt— a one-row table of the parameter names and values for this run;info.txt— the same row prefixed with therunidand suffixed with therundir, so a directory can be located and loaded uniformly.
Because single simulations and ensemble members write the same files, downstream tooling can load any run directory the same way.
Any -p value that is a comma list, a range, or a distribution turns the run into
an ensemble. The output directory -o OUTDIR then holds one run directory per
ensemble member:
runme -r -o OUTDIR -n par/model.nml -p ctl.n_accel=1,5,10This runs three simulations in OUTDIR/0, OUTDIR/1, OUTDIR/2. Single-valued
-p entries given alongside ensemble dimensions are fixed overrides applied to
every member:
runme -r -o OUTDIR -n par/model.nml -p ctl.n_accel=1,5,10 ctl.year=5000 smb.alb_ice=0.3,0.4A list of vector values is an ensemble over those vectors — -p ctl.years=[10,20],[30,40]
runs two members, one per vector (whereas -p ctl.years=[10,20] is a single fixed
vector on every run).
Use -a for run directories named from the parameter values instead of the run id:
runme -r -a -o OUTDIR -n par/model.nml -p ctl.n_accel=1,5,10In OUTDIR:
params.txt/info.txt— the full parameter table (ensemble dimensions and fixed overrides);info.txtadds therunidandrundircolumns.params_ensemble.txt/info_ensemble.txt— only the permuted ensemble dimensions.
runid ctl.n_accel rundir
0 1 0
1 5 1
2 10 2
Each member run directory additionally gets its own single-row params.txt /
info.txt and a runme.json record, so any run directory — single or ensemble
member — loads the same way.
Run only some members with -j (slurm --array syntax):
runme -r -o OUTDIR -n par/model.nml -p ctl.n_accel=1,5,10 -j 0,2 # members 0 and 2
runme -r -o OUTDIR -n par/model.nml -i lhs.txt -j 0-9 # first ten membersFor complex ensembles, a two-step approach is often clearer: generate the
parameter set first (so you can check it and keep it for reproducibility), then
run it with -i.
Factorial combination:
runme product ctl.n_accel=1,5,10 smb.alb_ice=0.3,0.4 -o grid.txt
runme -r -o OUTDIR -n par/model.nml -i grid.txtLatin-hypercube (or Monte-Carlo) sampling of distributions:
runme sample atm.c_trop=U?0.8,1.2 smb.alb_ice=N?0.35,0.05 -N 100 --seed 4 -o lhs.txt
runme -r -o OUTDIR -n par/model.nml -i lhs.txtDistribution specs: U?MIN,MAX (uniform), N?MEAN,SD (normal), or any
scipy.stats distribution as TYPE?[SHP,]LOC,SCALE. Discrete values (a=1,2,3)
and ranges (a=0:10:5) are also accepted.
Add --dry-run to print what would be staged and run for each member without
creating or running anything.
Some models (e.g. climber-x, and sometimes yelmox) generate interpolation maps between grids on their first run and cache them in a shared, linked input folder. Launching a whole ensemble before those maps exist makes every member try to build the same maps at once. To avoid that, do one map-generating run first.
Add --gen-maps to your normal command. It runs a single simulation into a
mapgen/ subfolder of -o OUTDIR — the single sim itself, or member 0 of an
ensemble (plus any fixed -p overrides) — and then exits:
runme -r -o OUTDIR -n par/model.nml -p ctl.n_accel=1,5,10 --gen-maps # warms the maps
runme -r -o OUTDIR -n par/model.nml -p ctl.n_accel=1,5,10 # the real ensembleThe first command stages and runs OUTDIR/mapgen/; once it finishes, drop
--gen-maps and re-run the same command to launch the real single run or
ensemble, which reuses the cached maps. mapgen/ sits alongside the member
directories (0, 1, …) and is left in place.
Run/submit flags are honoured as given: with -r the mapgen run runs in the
background, and with -s -r it is submitted to the queue (wait for that job to
finish before launching the ensemble).