Skip to main content

qccodec

image image image CI

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 data object will be a qcdata object, either SinglePointData, OptimizationData, ConformerSearchData or other *Data structure depending on the calctype. Run dir(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 qccodec from 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 decode and optionally the input data used to generate the calculation (usually done from qccompute which 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
  • target is 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.
  • calctypes restricts when the parser runs; omit it to run for every calculation type.
  • file is "stdout" (default), "directory", a file name or glob relative to the calculation directory, or a function of the Context returning such a name.
  • required=False tolerates a missing file or MatchNotFoundError.
  • 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)

Source distribution for qccodec 0.13.1
File Size Uploaded
qccodec-0.13.1.tar.gz 658.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for qccodec 0.13.1
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.13.1 This release

2 release files

0.13.0

2 release files

0.12.0

2 release files

0.11.2

2 release files

0.11.1

2 release files

0.11.0

2 release files

0.10.2

2 release files

0.10.0

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.8

2 release files

0.7.7

2 release files

0.7.6

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page