MolFrame

Structures

Read a molecular structure and navigate its models, chains, residues, atoms, and coordinates.

Reading

Use molframe.read() with a path, bytes, or a Reader. Format detection uses the supplied name or path extension when available.

import molframe

structure = molframe.read("model.cif")
# The same API reads a PDB file:
structure = molframe.read("model.pdb")

The Python build reads mmCIF, BinaryCIF, PDB, PQR, PDBQT, MMTF, PDBML, SDF, MOL2 and core CIF (format="smallcif"). Reading local paths and caller-supplied bytes is the verified loading boundary. Identifier acquisition is not silently mapped to a guessed archive URL: the current adapters expose a typed unavailable-provider diagnostic until a provider manifest supplies a canonical URL and authoritative checksum. For a direct remote resource, use the Rust adapter's verified-download boundary with an expected SHA-256 digest, byte limit, timeout, and redirect policy.

Stating how a read is done

format= names the format when neither the content nor the file name settles it (a file suffix such as "cif" or "mol" is accepted as a name). Every other decision lives in a ReadOptions, which is reusable across reads:

options = molframe.ReadOptions(
    mode="strict",                       # "strict", "permissive" (default) or "recover"
    first_model_only=True,
    discard_hydrogens=True,
    skip_categories=["struct_conf"],     # or only_categories=[...]; required ones are always read
    max_nesting_depth=8,                 # bounds on hostile or oversized input
)
structure = molframe.read("model.cif", options=options)
small_molecule = molframe.read(sdf_text.encode(), format="sdf")

A word outside a decision's vocabulary raises PolicyError (code MOLFRAME-E6104) and lists what is accepted. Stating the format twice, in format= and in ReadOptions, is refused.

What was wrong with the file

read() returns the structure and discards the findings; a file that parses is not the same as a file that is right. read_with_diagnostics() keeps them:

structure, findings = molframe.read_with_diagnostics("model.pdb")
for finding in findings:
    print(finding.code, finding.message, finding.remedy, finding.span)

Bounding and cancelling a read

context= governs the part of a read that is parallel and large, bond and secondary-structure perception: its worker count, its memory budget and its cancellation. A cancelled context stops the read with Cancelled and an exhausted budget with MemoryBudgetError, instead of returning a structure that skipped the step.

Files too large to hold at once

open_structure_batches() yields bounded runs of atom rows, so memory stays bounded by rows, bytes and the context budget whatever the file's size:

for batch in molframe.open_structure_batches("1aon.cif", rows=8192):
    xyz = batch.positions          # (rows, 3) float32
    names = batch.atom_names       # list of text
    occupancy = batch.occupancies  # NaN where the file records none

Formats without a bounded reader are refused rather than read whole.

What a structure carries

Beyond the hierarchy, a structure exposes the facts its file states. Anything the file does not say is None or NaN, never a guess:

structure.metadata.id, structure.metadata.method, structure.metadata.resolution
structure.cell                 # a crystal.UnitCell, or None (a placeholder unit cube is not reported)
structure.entities[0].sequence # the canonical sequence; chains[i].entity says which chains copy it
structure.anisotropy           # a Table of U11..U23 per atom, None when the file did not say
structure.annotations["plddt"] # .values and .present: which atoms record a value
structure.elements, structure.occupancies, structure.b_factors   # bulk per-atom arrays
structure.secondary_source     # SecondarySource.File / Dssp / CaOnly / Unassigned per residue

Atoms expose element, atomic_number, occupancy, b_factor, formal_charge, altloc, serial, auth_name and anisotropy; residues number, auth_number, insertion_code, is_hetero, chain, secondary_structure and secondary_source; chains auth_label, entity and polymer_kind.

The hierarchy

The collections are indexable and report their length. Individual handles expose identifiers and their immediate children.

structure = molframe.read("model.pdb")

print(structure.atom_count)
print(structure.residue_count)
print(structure.chain_count)
print(structure.model_count)

first_chain = structure.chains[0]
first_residue = first_chain.residues[0]
first_atom = first_residue.atoms[0]
print(first_atom.name, first_atom.element, first_atom.coordinate)

An atom coordinate can be None when the input does not provide coordinates for that atom. Do not assume every topology atom has a position.

Coordinates

structure.coordinates is a NumPy-compatible float32 array with one row per atom and three Cartesian coordinates per row. Coordinates are in Ångström. Keep the structure alive while using a coordinate view.

coordinates = structure.coordinates
assert coordinates.shape == (structure.atom_count, 3)

For an analysis over a subset, use selection.to_coordinates() rather than manually aligning hierarchy indices.

Ambiguity in input

PDB and mmCIF files can contain multiple models, alternate locations, missing coordinates, or ambiguous residue boundaries. MolFrame records input findings as diagnostics. Choose an explicit parsing policy when your scientific interpretation depends on these cases; do not silently merge conformers or models.

Verified acquisition

molframe read does not resolve identifiers or access the network. For reproducible acquisition, use the CLI fetch command with an explicit provider URL, pinned SHA-256 digest, and resource limits:

molframe fetch 1HHO --url-template 'https://example.org/structures/{id}.cif' --sha256 <64-hex-digest> --max-bytes 50000000 --timeout-seconds 30 --redirect-limit 0 --output 1HHO.cif

The URL must contain the literal {id} placeholder. The command validates response size and SHA-256 before writing; a failed transfer, response-limit violation, malformed digest, or checksum mismatch leaves the destination untouched. The destination is created atomically, and an existing file is not replaced by fetch. HTTPS identifies the transport origin, but a locally checked digest does not establish publisher authenticity when the provider does not publish an authoritative checksum. Record the provider URL, digest, format, and source/version alongside a downloaded resource.

CCD resources use the same verified boundary:

molframe ccd update --url <provider-url> --sha256 <64-hex-digest> --max-bytes 500000000 --timeout-seconds 120 --redirect-limit 0 --version <ccd-version> --output components.cif.gz

ccd update atomically replaces an existing destination only when --replace is supplied. Keep downloaded bytes in an application-managed cache and verify the digest whenever an entry is reused; do not treat a path’s existence as proof that its contents are valid. The Python read API currently accepts paths, bytes, and readers; identifier acquisition is intentionally separate.

Identity, numbering, and canonical gaps

Hierarchy handles retain source identity rather than rewriting it: atom, residue, and chain accessors distinguish author and label namespaces, preserve insertion codes, and report an absent author number as absent. Namespace-aware lookups must reject an ambiguous match instead of choosing the first chain. Selections remain owned by their source structure; an equal row index from a different topology is not the same atom.

Canonical sequence projections distinguish an observed residue from a canonical gap. A gap has no fabricated residue or atom index, while an observed residue without a reliable reference mapping remains explicitly unmapped. Alignment intervals are checked and may be disjoint or ambiguous; ordinal 1..N extrapolation is not a valid mapping.

Verification status

The local-path/bytes loading and verified-download contracts documented above are implemented boundaries. No test, formatter, or linter command was run for this documentation-only change. Identifier acquisition through a provider manifest, cache corruption/concurrency/offline cases, CIF/BCIF/PDB namespace round trips, canonical-gap fixtures, and Python hierarchy scenarios remain unverified here. HTTPS origin plus a locally computed digest does not establish publisher authenticity when the provider supplies no authoritative checksum.

On this page