Default Templates¶
This page describes the structure and design of the default task
templates. These templates are used to write task and calculation
scripts when writing task inputs (i.e., with Task.write_inputs()).
All templates are written in Jinja.
run.sh.j2¶
run.sh.j2 is the default task script template for all autojob-defined
TaskBase implementations. With this template, scheduler inputs are
written, configuration files (/etc/profile and $HOME/.bash_profile) are sourced,
and the task is executed. The resulting script is intended to be executed with
Bash.
1#!/bin/bash
2{% if scheduler_inputs %}
3{% for key, value in scheduler_inputs.items() %}
4{% if value %}
5#SBATCH {{ key }}={{ value }}
6{% endif %}
7{% endfor %}
8{% endif %}
9
10echo "\n... Setting up shell environment ...\n"
11shopt -s expand_aliases
12
13if test -e "/etc/profile"; then source "/etc/profile"; fi
14if test -e "$HOME/.bash_profile"; then source "$HOME/.bash_profile"; fi
15
16# run task
17{% if calculation_inputs %}
18python {{ calculation_inputs.calculation_script }}
19{% else %}
20cp {{ task_inputs.atoms_filename }} {{ settings.OUTPUT_ATOMS_FILE }}
21{% endif %}
The first block (i.e., the block starting with {% if scheduler_inputs %})
prints out the scheduler inputs for the task. The block writes the scheduler inputs
(if defined) in a format supported by the SLURM workload manager. Next, the
expand_aliases shell option is enabled. (This is useful since aliases are often
used to condense useful commands for use in task scripts. For example, it is common
to define an alias for activating Python virtual environments.) This is followed
by sourcing common Bash configuration files, /etc/profile and
$HOME/.bash_profile. Finally, depending on whether the task has calculation inputs
or not, the last block will either execute the calculation script with Python or
copy the inputs file to the outputs file.
calc.sh.j2¶
calc.sh.j2 is a built-in task script template that leverages advanced
features of autojob. This template extends run.sh.j2 be loading modules,
calling custom commands (activate_env, debug-slurm, log-job), and then calling
autojob CLI commands (autojob run, autojob harvest,
autojob clean, autojob restart, and autojob advance).
1#!/bin/bash
2{% if scheduler_inputs %}
3{% for key, value in scheduler_inputs.items() %}
4{% if value %}
5#SBATCH {{ key }}={{ value }}
6{% endif %}
7{% endfor %}
8{% endif %}
9
10echo "\n... Setting up shell environment ...\n"
11shopt -s expand_aliases
12module purge
13
14if test -e "/etc/profile"; then source "/etc/profile"; fi
15if test -e "$HOME/.bash_profile"; then source "$HOME/.bash_profile"; fi
16
17unset LANG; ulimit -s unlimited; ulimit -a -S; ulimit -a -H
18export LC_ALL="C"; export MKL_NUM_THREADS=1; export OMP_NUM_THREADS=1
19
20module load python {{ calculator|lower }}
21activate_env
22debug-slurm
23
24# run task
25{% if calculation_inputs %}
26autojob run --buffer=0.1 --time-source={{ task_inputs.task_script }} 'python {{ calculation_inputs.calculation_script }}'
27exit_code=$?
28autojob -vv harvest
29{% endif %}
30autojob clean
31log-job
32
33if [ "$exit_code" -eq 124 ]; then
34autojob -vv restart -S; else
35autojob -vv advance; fi
Lines 17-18 define common environment variables for running computational codes.
run.py.j2¶
run.sh.j2 is the default calculation script template for all autojob-defined
Calculation subclasses. This Python script template loads
the input Atoms, configures the ASE Calculator and Optimizer,
calls the ccu function run_calculation(), and then runs
any analyses.
1from importlib import import_module
2import json
3import logging
4from pathlib import Path
5
6import ase.io
7from ase.calculators.calculator import get_calculator_class
8from ccu.workflows.calculation import run_calculation
9
10from autojob.plugins import get_analysis
11from autojob.tasks.calculation import CalculationInputs
12
13logging.basicConfig(level=logging.DEBUG)
14
15with Path("{{ settings.INPUTS_FILE }}").open(mode="r", encoding="utf-8") as file:
16 inputs = json.load(file)
17
18atoms = ase.io.read(inputs["task_inputs"]["atoms_filename"])
19
20# Configure calculator
21calc_inputs = CalculationInputs(**inputs["calculation_inputs"])
22calculator = get_calculator_class(calc_inputs.calculator)
23calc = calculator(**calc_inputs.calc_params)
24atoms.calc = calc
25
26# Configure optimizer
27if calc_inputs.optimizer:
28 *opt_mod_parts, opt_cls_name = calc_inputs.optimizer.split(".")
29 opt_cls = getattr(import_module(".".join(opt_mod_parts)), opt_cls_name)
30 opt = opt_cls(atoms, **calc_inputs.opt_params["init"])
31 opt_run_params = calc_inputs.opt_params["run"]
32else:
33 opt = None
34 opt_run_params = {}
35
36run_calculation(atoms, opt=opt, **opt_run_params, output={{ settings.OUTPUT_ATOMS_FILE }})
37
38# Run analyses
39if calc_inputs.analyses:
40 for name, (args, kwargs) in calc_inputs.analyses.items():
41 analysis = get_analysis(name)
42 _ = analysis(*args, **kwargs)
The run_calculation() function called on line 36 will
result in the output trajectory being written.
vib.py.j2¶
vib.sh.j2 is the a calculation script template intended for use by
Vibration tasks. This Python script template loads
the input Atoms, configures the ASE Calculator and calls the
ccu function run_vibration(), and then runs
any analyses.
1import json
2import logging
3from pathlib import Path
4
5from ase.calculators.calculator import get_calculator_class
6import ase.io
7from ccu.workflows.vibration import run_vibration
8
9from autojob.plugins import get_analysis
10from autojob.tasks.calculation import CalculationInputs
11
12logging.basicConfig(level=logging.DEBUG)
13
14with Path("{{ settings.INPUTS_FILE }}").open(
15 mode="r", encoding="utf-8"
16) as file:
17 inputs = json.load(file)
18
19atoms = ase.io.read(inputs["task_inputs"]["atoms_filename"])
20
21# Configure calculator
22calc_inputs = CalculationInputs(**inputs["calculation_inputs"])
23calculator = get_calculator_class(calc_inputs.calculator)
24calc = calculator(**calc_inputs.calc_params)
25atoms.calc = calc
26
27run_vibration(atoms, **inputs["vibration_inputs"])
28
29# Run analyses
30if calc_inputs.analyses:
31 for name, (args, kwargs) in calc_inputs.analyses.items():
32 analysis = get_analysis(name)
33 _ = analysis(*args, **kwargs)
The run_vibration() function called on line 27 will
result in the output trajectory being written along with a JSON file containing
the information needed to instantiate a VibrationsData object representing
the results of the vibrational analysis.
See also