SLURM Job Scripts¶
SlurmJob generates complete SLURM submission scripts for Q-Chem or ORCA jobs. Core counts, memory limits, and program-specific launch commands are derived from the CalculationInput spec automatically.
The SlurmJob Object¶
SlurmJob is a frozen dataclass with a fluent setter API, just like CalculationInput.
from calcflow.slurm import SlurmJob
job = SlurmJob(
job_name="h2o_tddft",
time="04:00:00", # wall time — HH:MM:SS
n_cores=16,
memory_mb=64000, # total node memory in MB
partition="cpu",
account="mygroup",
)
Required fields: job_name, time, n_cores, memory_mb, partition.
Optional fields: constraint, account, queue, modules.
Adding Environment Modules¶
Most HPC clusters use environment modules to load software. Specify them as a list:
.add_modules() appends to the existing module list — useful for building up from a base job:
base_job = SlurmJob(
job_name="base",
time="02:00:00",
n_cores=8,
memory_mb=32000,
partition="cpu",
modules=["intel/2023", "openmpi/4.1"],
)
qchem_job = base_job.add_modules(["qchem/6.2"])
orca_job = base_job.add_modules(["orca/5.0"])
Generating a Script¶
Pass a CalculationInput and the target program to .export():
from calcflow import CalculationInput, Geometry
calc = CalculationInput(
charge=0,
spin_multiplicity=1,
task="energy",
level_of_theory="wB97X-D3",
basis_set="def2-TZVP",
).set_tddft(nroots=10, singlets=True, triplets=False).set_cores(16)
geom = Geometry.from_xyz_file("molecule.xyz")
script = job.export(
calculation=calc,
program="qchem",
input_filename="molecule.inp",
output_filename="molecule.out",
)
print(script)
The generated script includes:
#SBATCHdirectives derived from theSlurmJobfieldsmodule loadcommands for each entry inmodules- The program-specific launch command (e.g.
qchem -np 16 molecule.inp molecule.out) - Any program-specific directives the builder exposes (e.g. OpenMP thread settings)
Full Example: Q-Chem TDDFT Workflow¶
from pathlib import Path
from calcflow import CalculationInput, Geometry
from calcflow.slurm import SlurmJob
geom = Geometry.from_xyz_file("meoq.xyz")
# Calculation spec
calc = (
CalculationInput(
charge=0,
spin_multiplicity=1,
task="energy",
level_of_theory="wB97X-D3",
basis_set="def2-TZVP",
)
.set_tddft(nroots=15, singlets=True, triplets=False)
.set_solvation(model="smd", solvent="water")
.set_charges(mulliken=True, hirshfeld=True, cm5=True)
.set_cores(32)
.set_memory_per_core(3000)
)
# SLURM job
job = SlurmJob(
job_name="meoq_tddft",
time="08:00:00",
n_cores=32,
memory_mb=128000,
partition="cpu",
account="chemistry",
modules=["qchem/6.2"],
)
# Generate input and submission script
input_text = calc.export("qchem", geom)
script = job.export(calc, "qchem", "meoq.inp", "meoq.out")
Path("meoq.inp").write_text(input_text)
Path("submit.sh").write_text(script)
ORCA Example¶
orca_calc = (
CalculationInput(
charge=0,
spin_multiplicity=1,
task="energy",
level_of_theory="wB97X-D3",
basis_set="def2-TZVP",
)
.set_tddft(nroots=10, singlets=True, triplets=False)
.set_cores(8)
)
orca_job = SlurmJob(
job_name="meoq_orca",
time="04:00:00",
n_cores=8,
memory_mb=32000,
partition="cpu",
modules=["orca/5.0.4", "openmpi/4.1"],
)
input_text = orca_calc.export("orca", geom)
script = orca_job.export(orca_calc, "orca", "meoq.inp", "meoq.out")
Saving the Spec for Reproducibility¶
Reload later with CalculationInput.from_json(Path("calc_spec.json").read_text()) — so you always know exactly what was run.
Partition and Constraint¶
For clusters with heterogeneous partitions: