# Packaged VASP runners *For campaigns that want an ordinary VASP calculation without writing a runner at all.* *httk-workflow* ships complete VASP runners. A campaign that wants the ordinary thing — relax a structure, run a single point, or both in sequence — writes no runner at all: it submits jobs that name one of the installed files in `httk.workflow.vasp.runners`. Their result hooks live in `httk.workflow.vasp.collect` as `collect_vasp_relax`, `collect_vasp_static`, and `collect_vasp_relax_static`. | Runner | Workflow | Steps | | --- | --- | --- | | `vasp_relax.py` | `httk.vasp.relax` | publish, prepare, run | | `vasp_relax.sh` | `httk.vasp.relax` | publish, prepare, run | | `vasp_static.py` | `httk.vasp.static` | publish, prepare, run | | `vasp_relax_static.py` | `httk.vasp.relax-static` | publish, prepare, promote, run, static | `vasp_relax.sh` is the Bash authoring of exactly the same workflow as `vasp_relax.py`: the same steps, the same job parameters, the same job state, the same failure codes, and the same files in the workdir and in the published data. It is both a working runner and the proof that the two authoring SDKs are one protocol. ## Submitting a job Reference a packaged runner as an installed runner, which needs no publication — the manager resolves the reserved `pkg:` form inside its own module allowlist, and `httk.workflow` is in that allowlist by default: ```python from httk.workflow import JobSpec, Workspace, prepare_job_payload from httk.workflow.runners import runner_reference reference = runner_reference("vasp_relax.py") job = prepare_job_payload( "prepared-job", JobSpec( name="Relax silicon", workflow="httk.vasp.relax", runner_path=str(reference["path"]), # pkg:httk.workflow.vasp.runners/vasp_relax.py runner_source="installed", runner_sha256=str(reference["sha256"]), initial_step="prepare", data_mode="transactional", parameters={"kpoint_density": 30.0, "incar_tags": {"ENCUT": 520}}, ), ) ``` The payload carries the structure and the INCAR it starts from: ```console mkdir -p prepared-job/files cp POSCAR INCAR POTCAR prepared-job/files/ httk project init --name workflow . httk job submit --placement project/si prepared-job httk workflow manager run ``` Every `httk workflow` command names the *registered* workspace, never a path; `workspace init` both creates the workspace and registers the name — see {doc}`workflow_cli`. The same file can be published to the workspace runner store instead, which pins it by digest for a whole campaign: ```python workspace = Workspace.initialize("workflow-workspace") published = workspace.publish_runner("/path/to/vasp_relax.py", name="vasp/relax.py") ``` ## The command to run The VASP command is the `vasp.command` application setting, which the runners resolve through `a.setting("vasp.command")` — most specific first: a job's own `vasp.command` parameter, then `HTTK_VASP_COMMAND` in the environment, then the workspace's `vasp.command` setting, and finally the job's legacy `vasp_command` parameter. The chosen string is split the way a shell would. Two spellings therefore both work, and mean different things. A machine that invokes VASP its own way still exports the environment variable, and it wins over everything a workspace configures — deployment state a job submitted elsewhere cannot know: Configure the command once on the workspace, so no one has to export it for every job — and a workspace bound to a remote is even seeded with it from the remote definition when it is created: ```console httk workspace settings set --key vasp.command --value "srun -n 32 vasp_std" default httk workflow manager run --pool vasp ``` The pseudopotential library resolves the same way, as `vasp.pseudo_library` (environment `HTTK_VASP_PSEUDO_LIBRARY`), falling back to the job's `pseudopotential_library` parameter. See {doc}`sdks/sdk_parity` for the resolution table and {doc}`workflow_cli` for `workspace settings`. ## Declared inputs and parameters The four workflows declare one creation input: `structure` → `files/POSCAR`. Paths are copied verbatim; other objects are serialized with `httk.core.save`, which requires the `httk-atomistic` package for POSCAR/CIF writers. Every implementation parameter is optional and every one is documented, with its default, in the API reference of {py:mod}`httk.workflow.vasp.runners`. The ones a campaign normally sets are `poscar`, `incar_tags`, `kpoint_density`, `pseudopotential_library`, `timeout`, and `collect`. Two job members are not parameters but requirements: `workdir.mode` must be `persistent`, because the inputs a remedy rewrites have to be the inputs the next attempt reads, and `data.mode` must be `transactional` for the collected files to be published. With `data.mode` `none` the results simply stay in the workdir. ## What a run does `prepare` stages the payload files into the workdir, then derives what the job did not give: the k-point grid from `kpoint_density`, `EDIFF` and `EDIFFG` from `accuracy_per_atom`, `MAGMOM` from the structure, `NBANDS` from the POTCAR, and a POTCAR from `pseudopotential_library` when none was staged. Explicit `incar_tags` are applied first and win over every derived value. `run` executes VASP under supervision, writes `vasp-run-report.json`, and acts on the classification: | Classification | What the runner does | | --- | --- | | `completed` | records the energy and advances | | anything else with a remedy left | applies exactly one remedy and asks for another attempt | | anything else without one | fails with `vasp.failed`, carrying the decision that ran out | Remedies are the bounded ladder of the policy named by `remedy_policy`, which defaults to `reviewed-v1`; they are planned and applied explicitly and never invented: see {doc}`runtime_helpers`. A group with its own reviewed practice registers a policy and names it in the job parameters rather than editing a runner. The ladder position is recorded in the job state directory, so it survives every attempt of the job, and `maximum_remedies` bounds how many a single job may apply. The `publish` step publishes the files named by the `collect` parameter into the job's transactional data, under `data_prefix`. `vasp_relax_static.py` archives the relaxation inside the workdir before the single point overwrites it, and publishes the two stages side by side as `relax/` and `static/`. ## relaxation-report The packaged vasp-relax, vasp-relax-bash, and vasp-relax-static workflows declare the relaxation-report postprocess script. Run it after collection: ~~~console httk workflow postprocess --workspace WS --script relaxation-report ~~~ It reads HTTK_WORKFLOW_DATA_DIR (or HTTK_WORKFLOW_WORKDIR when no transactional data directory exists), finds the published OUTCAR and CONTCAR files, and writes relaxation_report.txt and relaxation_report.json into workdir/postprocess/relaxation-report/. Missing files are reported as unavailable and do not make the script fail. ## relaxation-plot The same three packaged workflows also declare the relaxation-plot postprocess script. Run it after collection: ~~~console httk workflow postprocess --workspace WS --script relaxation-plot ~~~ It reads the preferred published OUTCAR from HTTK_WORKFLOW_DATA_DIR (or HTTK_WORKFLOW_WORKDIR when no transactional data directory exists), plots every ionic-step energy, and writes workdir/postprocess/relaxation-plot/relaxation_energies.svg. Missing OUTCAR data or energies are reported and do not make the script fail. ## Failure codes | Code | Meaning | | --- | --- | | `vasp.command_missing` | no `vasp.command` resolved — set it with `httk workspace settings set --key vasp.command --value '...' default`, or use `HTTK_VASP_COMMAND` as a deployment override; no `vasp_command` parameter was provided | | `vasp.input_missing` | the structure named by `poscar` is not in the payload | | `vasp.no_relaxed_structure` | the chained runner's relaxation left no CONTCAR | | `vasp.failed` | VASP did not complete and no remedy remained | ## Formation energies `vasp_relax_static.py` is the *httk* v1 formation-energy template without its database half. A formation energy is assembled from the total energies of several finished jobs and the elemental references they are compared against, which is an analysis over jobs rather than a step inside one. What the runner guarantees is the pair of numbers that assembly needs: one relaxed structure and one total energy of it, each with the evidence of how it was produced. ## Copying one A group whose practice differs from the packaged one should copy the file and edit it. Each runner is one self-contained file that imports nothing but an installed *httk-workflow*, so a copy is a complete runner, publishable as it stands. A group with its own reviewed remedy practice can also keep the packaged runners and register its own policy with {py:func}`httk.workflow.vasp.register_remedy_policy`.