Custom codec¶
Run a program qccodec doesn't support by defining its codec in your own script. The
codec turns a ProgramInput into the program's command and files, and parses the
output back into qcdata. Pass it as codec=, or register it to use the program by name,
including as a geomeTRIC gradient child. qccodec's
Writing a codec
guide covers parsers, failure detection, and testing.
"""Define a codec for a program qccodec does not know about, and run it.
The "program" here is a Python one-liner so the example runs anywhere; swap the
command and regex for the real thing.
"""
import sys
import qccodec
from qccodec import Codec, SubprocessInput, re_search
from qcdata import CalcType, ProgramInput, SinglePointData, Structure
from qccompute import compute
codec = Codec("myprog", calctypes={CalcType.energy})
@codec.encoder
def encode(inp: ProgramInput) -> SubprocessInput:
"""Write the program's input file and build its command line."""
assert inp.model is not None
run_inp = f"method {inp.model.method}\n{inp.structure.to_xyz()}"
# Stand-in for ("myprog", "run.inp")
command = (sys.executable, "-c", "print('Total energy: -1.5')")
return SubprocessInput(command, files={"run.inp": run_inp})
@codec.parser(target="energy", calctypes=[CalcType.energy])
def parse_energy(stdout: str) -> float:
"""Pull the final energy out of the program's stdout."""
return float(re_search(r"Total energy:\s+(-?\d+\.\d+)", stdout).group(1))
@codec.parser(target=("provenance", "program_version"), required=False)
def parse_version(stdout: str) -> str:
"""Record the version when the program prints one."""
return re_search(r"myprog version (\S+)", stdout).group(1)
prog_input = ProgramInput(
program="myprog",
calctype="energy", # type: ignore
structure=Structure(symbols=["H", "H"], geometry=[[0, 0, 0], [0, 0, 1.4]]), # type: ignore
model={"method": "wb97x-d"}, # type: ignore
)
# Pass the codec explicitly...
output = compute(prog_input, codec=codec)
assert isinstance(output.results, SinglePointData)
print("energy:", output.results.energy)
# ...or register it once and run by program name, e.g. as a geomeTRIC gradient child.
qccodec.register(codec)
output = compute(prog_input)
print("provenance:", output.results.provenance)