qccodec
Translates qcdata inputs into native program inputs and parses program outputs back into qcdata objects, one codec per program.
The QC Suite
These packages work together to make quantum chemistry fast, structured, and interoperable.
- qcref - Reference data for computational chemistry: CODATA physical constants, conversion factors, a periodic table, and solvent properties, each value with its source.
- qcdata - Data structures for quantum chemistry inputs and results, with Jupyter visualizations. Documentation
- qcinf - Cheminformatics algorithms and structure utilities for qcdata structures.
- qccodec - Translates qcdata inputs into native program inputs and parses program outputs back into qcdata objects, one codec per program.
- qccompute - Runs quantum chemistry programs on qcdata inputs using qccodec codecs and returns structured results. Documentation
- BigChem - Distributed execution of qccompute calculations across clusters of computers or the cloud.
- ChemCloud - A web application and Python client that expose a BigChem cluster securely over the internet.
✨ Basic Usage
-
Installation:
python -m pip install qccodec
-
Parse QC program outputs into structured data files with a single line of code.
from pathlib import Path from qcdata import CalcType from qccodec import decode stdout = Path("tc.out").read_text() data = decode("terachem", CalcType.gradient, stdout=stdout)
-
The
dataobject will be aqcdataobject, eitherSinglePointData,OptimizationData,ConformerSearchDataor other*Datastructure depending on thecalctype. Rundir(data)inside a Python interpreter to see the various values you can access. A few prominent values are shown here as an example:from pathlib import Path from qcdata import CalcType from qccodec import decode stdout = Path("tc.out").read_text() data = decode("terachem", CalcType.hessian, stdout=stdout) data.provenance.program # Producer identity data.provenance.program_version # Parsed version, or None when unavailable data.energy data.gradient # If a gradient calc data.hessian # If a hessian calc data.calcinfo_nmo # Number of molecular orbitals
-
Parsed values can be written to disk like this:
with open("data.json", "w") as f: f.write(data.model_dump_json())
-
And read from disk like this:
from qcdata import SinglePointData data = SinglePointData.open("data.json")
-
You can also run
qccodecfrom the command line like this:qccodec -h # Get help message for cli qccodec terachem hessian tests/data/terachem/water.frequencies.out > data.json # Parse TeraChem stdout to json
-
More complex parsing can be accomplished by passing the directory containing the scratch files to
decodeand optionally the input data used to generate the calculation (usually done fromqccomputewhich uses structure data):from pathlib import Path from qcdata import CalcType, ProgramInput from qccodec import decode stdout = Path("tc.out").read_text() directory = Path(".") / "scr.geom" input_data = ProgramInput.open("prog_inp.json") data = decode("terachem", CalcType.hessian, stdout=stdout, directory=directory, input_data=input_data)
Contributing
See CONTRIBUTING.md for development setup, the pre-commit workflow, running tests, and the release process. Adding support for a new program is covered there step by step.
If there's data you'd like parsed from output files or a program you'd like supported, open an issue describing the data and include an example output file, like this.
Encoding inputs
from qcdata import ProgramInput, Structure
from qccodec import encode
input_data = ProgramInput(
program="terachem",
calctype="energy",
structure=Structure.open("molecule.xyz"),
model={"method": "hf", "basis": "sto-3g"},
)
native = encode(input_data) # SubprocessInput(command=("terachem", "tc.in"), files={...})
encode() returns a SubprocessInput (a command plus the files it needs) or a
PythonInput (a callable plus its arguments). Nothing is executed; qccompute
runs these.
Recovering failed calculations
partial = decode("terachem", "gradient", stdout=logs, directory=scratch_dir, failed=True)
With failed=True, missing files and values are tolerated and whatever was parsed
is returned. Without it, a missing required value raises ParserError (or
MatchNotFoundError) with the recovered data attached as .data. The CLI supports
the same behavior with --failed.
One Codec per program
Everything qccodec knows about a program lives in one Codec object: its name,
the calculation types it supports, an encoder, and either a set of small parsers
(file-based programs) or a decode function (Python APIs). Built-ins live in
qccodec/programs/<name>.py, one file per program, and you can build your own in a
script the same way:
from qcdata import CalcType, ProgramInput
from qccodec import Codec, SubprocessInput, re_search
codec = Codec("myprog", calctypes={CalcType.energy})
@codec.encoder
def encode(inp: ProgramInput) -> SubprocessInput:
return SubprocessInput(
command=("myprog", "run.inp"),
files={"run.inp": f"{inp.model.method}\n{inp.structure.to_xyz()}"},
)
@codec.parser(target="energy", calctypes=[CalcType.energy])
def parse_energy(stdout: str) -> float:
return float(re_search(r"Total energy:\s+(-?\d+\.\d+)", stdout).group(1))
Then run it with qccompute, either explicitly or by name after registering it:
import qccodec, qccompute
output = qccompute.compute(input_data, codec=codec)
qccodec.register(codec) # now resolvable as program="myprog",
output = qccompute.compute(input_data) # including as a geomeTRIC gradient child
register() also lets you override a built-in codec by name for the current process.
Parsers
A parser is a plain function from file contents to a value. The decorator records where the value goes and when the parser runs, and returns the function unchanged so it stays easy to test directly.
@codec.parser(target="energy", calctypes=[CalcType.energy, CalcType.gradient])
def parse_energy(stdout: str) -> float: ... # reads stdout (default)
@codec.parser(file="crest.engrad", target="gradient", calctypes=[CalcType.gradient])
def parse_gradient(contents: str) -> list[list[float]]: ... # reads a named file or glob
@codec.parser(file="directory", target="trajectory", calctypes=[CalcType.optimization])
def parse_trajectory(ctx: Context) -> list[ProgramOutput]: ... # needs several files
targetis the result field to fill, or a tuple for nested fields such as("provenance", "program_version"). Directory parsers may omit it and return a mapping that is merged into the result.calctypesrestricts when the parser runs; omit it to run for every calculation type.fileis"stdout"(default),"directory", a file name or glob relative to the calculation directory, or a function of theContextreturning such a name.required=Falsetolerates a missing file orMatchNotFoundError.versions=">=6.0"limits a parser to some program versions (a PEP 440 specifier); omit it to run on every version. See "Program versions" below.
Directory parsers receive a Context with stdout, directory, input_data,
failed, options, path(pattern), read(pattern), and decode(calctype, stdout=...)
for nested decoding with the same codec.
Failure detection and Python-native programs
@codec.failure
def failure(ctx: Context) -> str | None:
return "CREST reported FAILED." if "FAILED" in (ctx.stdout or "") else None
Many programs exit 0 after failing (TeraChem prints Job finished: only on
success). When the hook returns a message, decoding recovers what it can and raises
CalculationError with that data attached.
Programs driven through a Python API skip parsers entirely:
codec = Codec("xtb", calctypes={CalcType.energy, CalcType.gradient}, encode=encode, decode=decode)
where decode(input_data, native_output) maps native_output.value into a qcdata
object.
Options and wavefunction propagation
A codec declares the decode options it accepts, with defaults. Callers override them per calculation and parsers read them from the context:
codec = Codec("crest", calctypes={...}, options={"collect_rotamers": False})
@codec.parser(file="directory", calctypes=[CalcType.conformer_search])
def parse_rotamers(ctx: Context) -> dict:
if not ctx.options["collect_rotamers"]:
return {}
...
decode("crest", "conformer_search", directory=d, input_data=inp, options={"collect_rotamers": True})
qccompute.compute(inp, options={"collect_rotamers": True})
Unknown option names raise EncoderError. For optimizers that chain calculations, a
codec may set codec.propagate_wfn = fn where fn(output, input_data) returns a new
input carrying the previous output's wavefunction as the initial guess; TeraChem copies
c0/ca0/cb0 and sets guess. qccompute calls it when propagate_wfn=True.
Program versions
When a program changes its output format, register a parser per version range on the same target; the parser functions themselves stay version-free:
@codec.parser(target="gradient", calctypes=[CalcType.gradient], versions="<6.0")
def parse_gradient_v5(stdout: str) -> list[list[float]]: ...
@codec.parser(target="gradient", calctypes=[CalcType.gradient], versions=">=6.0")
def parse_gradient(stdout: str) -> list[list[float]]: ...
The parser targeting ("provenance", "program_version") runs first and its value
selects which parsers apply. Programs whose version strings are not PEP 440 give the
codec a version_key (TeraChem compares builds by release date, so v1.9-2023.08-dev
matches versions=">=2023.8"). When no version can be read, alternatives for a target
are tried in registration order and the first match wins. Pass version= to
decode/parse for logs that never print one.
Codecs from other packages
A package can ship a codec by publishing it as an entry point; get_codec and
qccompute.compute then find it by program name with no registration call:
[project.entry-points."qccodec.codecs"]
myprog = "mypackage.codec:codec"
Testing a codec
qccodec.testing gives every codec the same test harness. A recorded program run is a
directory holding the inputs, the outputs, and the data the codec must produce:
tests/data/myprog/water_energy/
input.json ProgramInput used for the run
expected.json the qcdata *Data object the codec must produce
stdout captured program output
<files> output files at their real relative paths, e.g. scr.geom/optim.xyz
case.json optional: {"options": {...}, "failed": true, "raises": "ParserError"}
encoded/ optional: command.txt plus the native input files encode() must write
import pytest
from qccodec import get_codec
from qccodec.testing import cases, check_codec, run_case
@pytest.mark.parametrize("case", cases("tests/data"), ids=str)
def test_case(case):
run_case(get_codec(case.input_data.program), case)
def test_contract():
check_codec(codec, inputs=[some_program_input])
run_case checks the encoded files, the full decode (or the expected exception and its
partial data), and every registered parser applied directly to its own file against the
matching slice of expected.json. check_codec verifies the structural contract: parser
targets name real result fields, decoding nothing yields typed empty data in recovery
mode and typed partial data on error, and encoded files stay inside the directory.
See CONTRIBUTING.md for the full walkthrough of adding a program.
Optional dependencies
Optional integrations install with qccodec[geometric], qccodec[xtb], or qccodec[tcpb]; their libraries are imported only when a calculation is invoked.
Metadata
Release files for qccodec 0.13.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| qccodec-0.13.1.tar.gz | 658.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| qccodec-0.13.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 702.9 kB
Release files / qccodec-0.13.1.tar.gz
| Download URL | qccodec-0.13.1.tar.gz |
|---|---|
| Size | 658.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3842884439e2c9298712a84834fe628da3cdc15d7e34065772f6e1ba3f9910c3
|
|
BLAKE2b-256 checksum How to use checksums |
97f87059b8904b415791b40bab6bb7f15bff94e8ada34a9e5a59fea4c798fb0e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.
Transparency logRelease files / qccodec-0.13.1-py3-none-any.whl
| Download URL | qccodec-0.13.1-py3-none-any.whl |
|---|---|
| Size | 44.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8dc341766e426e77a0bf407d9b1db0b1828dfbada66401315071975bcc96c522
|
|
BLAKE2b-256 checksum How to use checksums |
fc749e073c4c29697a0663801957fed4702d37dd7b17f097f556935d69e68b61
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.
Transparency log