seamm_thermochemistry package#
Submodules#
seamm_thermochemistry.atct module#
A local copy of the Active Thermochemical Tables (ATcT) and the rules for choosing an entry.
ATcT (https://atct.anl.gov) publishes enthalpies of formation at 0 K and 298.15 K for a few thousand species, derived from one thermochemical network (TN), so the values are mutually consistent and correlated. This module keeps a complete copy of one version of the tables on disk, builds an index from canonical SMILES to the ATcT entries, chooses the right entry for a molecule, and propagates the network covariance through a reaction.
Layout of the cache, one directory per TN version:
<base>/ATcT/<version>/
atct_all.json the /api/v1/all/ download, unmodified
metadata.json version, source URL, download date, SHA-256
index.json canonical SMILES -> ATcT IDs, built locally
covariance.json covariance blocks fetched so far, by version
where <base> is the installation’s Parameters/thermochemistry directory
by default. Nothing here is distributed with SEAMM: each installation downloads
its own copy, and the copy is never modified, so results can be reproduced.
Conventions that matter (Ruscic 2004, 2005; Ruscic & Bross 2025):
The listed uncertainties, and the covariance matrix, are 95 % confidence limits, valid at 298.15 K only. There is no 0 K uncertainty.
A plain
(g)entry is the species in its ground state: at 0 K the lowest level of the lowest conformer; at 298.15 K a thermal average over conformers. Conformers, stereoisomers, spin states and nuclear-spin isomers are separate entries, labelled in the formula’s state, e.g.(g, trans),(g, triplet),(g, para).Ions use the stationary-electron convention.
Never mix TN versions in one reaction.
RDKit is needed only to build the index and to canonicalize query SMILES; it is imported lazily, so the rest of the package does not depend on it.
- class seamm_thermochemistry.atct.ATcT(version=None, base=None)[source]#
Bases:
objectOne version of the ATcT tables, from the local cache.
- Parameters:
version (str, optional) – The TN version, e.g. “1.220”. The newest cached version by default.
base (path-like, optional) – The cache directory; see atct_directory.
- candidates(smiles, *, charge=None, gas_only=True)[source]#
All entries whose canonical SMILES matches, as ATcTEntry objects.
If smiles carries no double-bond or tetrahedral stereo but matching entries do, those entries are returned too (they come from the stereo-free index), so that the caller can see them.
- property citation#
- covariance(ids, *, fetch=True, user_agent='SEAMM-thermochemistry/2026.11.13.3 (+https://molssi-seamm.github.io; contact seamm@molssi.org)')[source]#
The covariance matrix (kJ²/mol², 95 % scale, 298.15 K) for the given ATcT IDs, in the order given.
A cached block that contains every ID is used; otherwise the block is fetched from
/covariance/species/(one request for all IDs), checked against the listed uncertainties, and cached.Returns None if the block is not cached and fetch is False or the request fails; callers then fall back to independent uncertainties.
- property covariance_cache#
- fetch_covariance(ids, *, user_agent='SEAMM-thermochemistry/2026.11.13.3 (+https://molssi-seamm.github.io; contact seamm@molssi.org)')[source]#
Fetch and cache the covariance block for ids.
- import_covariance(source)[source]#
Add a covariance block saved from a browser, e.g. the response of
/api/v1/covariance/species/?atctids=...saved to a file. The block is checked like a fetched one; a block already cached with exactly the same IDs is replaced.Returns the block.
- property index#
[ids]}, “flat”: {SMILES without stereo: [ids]}, “inchikey”: {key: [ids]}, “unparsed”: [ids]}, built with RDKit on first use and kept in
index.json.- Type:
{“stereo”
- Type:
{canonical SMILES
- lookup(smiles=None, *, atct_id=None, charge=0, multiplicity=None, conformer=None)[source]#
The ATcT entry for a molecule, by the selection rules.
- Parameters:
smiles (str) – The molecule, with double-bond stereo if it has any.
atct_id (str, optional) – An explicit ATcT ID, which overrides every rule.
charge (int) – The net charge; ATcT lists ions separately.
multiplicity (int, optional) – The spin multiplicity. A labelled spin-state entry is used when it matches; otherwise the plain entry, assumed to be the ground state.
conformer (str, optional) – A conformer or isomer label to select (
syn,anti,cis,trans, …) instead of the plain entry.
- Return type:
- Raises:
- parse_covariance(data)[source]#
The API’s covariance response as {“ids”: […], “matrix”: [[…]]}.
The rows come back in the API’s own order (
species_atctids), not the request order. The square root of each diagonal element must agree with the listed 298.15 K uncertainty, which confirms that the matrix is on the 95 % scale; a mismatch is an error. ATcT lists the uncertainties rounded up to 0.01 kJ/mol (0.1118 is listed as 0.12), so the listed value may exceed the diagonal by up to 0.01.
- class seamm_thermochemistry.atct.ATcTEntry[source]#
Bases:
dictA normalized ATcT entry (a dict) with a few conveniences.
- property casrn_group#
conformers and states of one species share it (
74-82-8*0,74-82-8*1…).- Type:
The CAS number part of the ID
- property heavy_atoms#
The number of atoms other than hydrogen, from the formula.
- property index#
0 is the plain entry.
- Type:
The
*nindex of the ID
- property is_gas#
- property is_nuclear_spin_isomer#
ortho/para entries of small species, also when combined with a spin state (“para singlet” formaldehyde). On a bigger species “ortho” and “para” name positional isomers (o-tolualdehyde), which are wanted.
- property is_plain#
A plain entry, no conformer, state or isomer label.
- property multiplicity#
The spin multiplicity named in the descriptor, or None.
- exception seamm_thermochemistry.atct.ATcTError[source]#
Bases:
ExceptionBase class for errors from the ATcT cache.
- exception seamm_thermochemistry.atct.AmbiguousEntry(message, candidates)[source]#
Bases:
ATcTErrorMore than one ATcT entry matches; the candidates are listed.
- exception seamm_thermochemistry.atct.MissingEntry[source]#
Bases:
ATcTError,KeyErrorNo ATcT entry matches.
- seamm_thermochemistry.atct.atct_directory(base=None)[source]#
The directory holding the ATcT versions.
base overrides everything. Otherwise the installation’s
Parameters/thermochemistry/ATcT, found asdb.pyfinds the reference database: the current root first, then the directory of the database registered in seamm.ini, then~/SEAMM/Parameters/thermochemistry.
- seamm_thermochemistry.atct.canonical_smiles(smiles, *, stereo=True, keep_single_center=False)[source]#
RDKit canonical SMILES for matching.
Double-bond stereochemistry is kept (ATcT lists cis and trans isomers as separate species). Tetrahedral stereocentres are dropped when the molecule has only one, since enantiomers have the same enthalpy of formation and ATcT has no enantiomer entries; with two or more, the relative configuration is kept. Returns None if RDKit cannot parse the SMILES.
- seamm_thermochemistry.atct.download_all(base=None, *, user_agent='SEAMM-thermochemistry/2026.11.13.3 (+https://molssi-seamm.github.io; contact seamm@molssi.org)', source=None, exist_ok=False)[source]#
Download
/all/into<base>/<version>/atct_all.jsonand write its metadata. An existing version is never overwritten: with exist_ok that is not an error (e.g. an installer’s update, or two jobs fetching the same missing version at once), otherwise it raises ATcTError. source may be a local file (a copy saved from a browser) instead of the API.Returns the version directory.
- seamm_thermochemistry.atct.normalize_entry(raw)[source]#
One ATcT record, from
/all/or a per-species route, as a dict with fixed keys:version, id, name, formula, phase, descriptor, charge, dfH0_0K, dfH0_298K, uncertainty_298K (None if exact), exact, units, smiles, casrn, inchi, inchikey, mass.The formula column carries the state, e.g.
CH4 (g)orC2H2Cl2 (g, trans). It is split intoformula,phase(g,cr,l,cr,l,aq, …) anddescriptor(trans,triplet,ortho,syn, … or"").
- seamm_thermochemistry.atct.reaction_uncertainty(coefficients, uncertainties, covariance=None)[source]#
The 95 % uncertainty of Σ νᵢ ΔfHᵢ at 298.15 K.
- Parameters:
coefficients ([float]) – The stoichiometric coefficients ν of the reference species.
uncertainties ([float]) – Their listed 95 % uncertainties, same order.
covariance ([[float]], optional) – Their covariance matrix, same order. If None the uncertainties are combined in quadrature, which ignores the correlations.
- Returns:
The uncertainty, and whether the covariance was used.
- Return type:
(float, bool)
seamm_thermochemistry.atct_cli module#
seamm-thermochemistry-atct: manage the local copy of the Active
Thermochemical Tables and look entries up from the command line:
seamm-thermochemistry-atct download [--from FILE] [--base DIR]
seamm-thermochemistry-atct list [--base DIR]
seamm-thermochemistry-atct lookup SMILES [--charge Q] [--multiplicity M]
[--conformer LABEL] [--version V]
seamm-thermochemistry-atct index [--version V] (rebuild the index)
seamm-thermochemistry-atct covariance [--from FILE] [--version V]
[SMILES-or-ATcT-ID ...]
seamm_thermochemistry.cbh module#
Connectivity-based hierarchy (CBH) reactions for a molecule.
The CBH of Ramabhadran and Raghavachari (J. Chem. Theory Comput. 7, 2094 (2011); Raghavachari et al., J. Chem. Theory Comput. 19, 3763 (2023)) builds, from the hydrogen-suppressed molecular graph alone, one error-cancelling reaction per rung:
Rung |
Products (capped with H to the parent’s valence) |
Reactants |
|---|---|---|
CBH-0 |
the hydride of every heavy atom |
H2, to balance H |
CBH-1 |
every heavy-atom bond |
N0(atom) x (deg-1) |
CBH-2 |
every heavy atom with its heavy neighbours |
N0(bond) per bond |
CBH-3 |
every bond with the neighbours of both atoms |
N1(atom) x (deg-1) |
CBH-4 |
every atom with two shells of neighbours |
N1(bond) per bond |
Here N_k(atom) is the atom with all heavy atoms within k bonds, N_k(bond) is the bond’s two atoms with all heavy atoms within k bonds of either, and deg is the number of heavy neighbours. Odd rungs (2k+1) have N_k(bond) products and N_k(atom) reactants with coefficient (deg - 1); even rungs (2k >= 2) have N_k(atom) products and N_{k-1}(bond) reactants with coefficient 1. Identical fragments are merged and species on both sides cancel. The target itself has coefficient -1. This reproduces every published reaction we checked (see tests/test_cbh.py).
Every fragment is the induced subgraph: it keeps the bonds, bond orders,
formal charges and unpaired electrons its atoms have in the parent, and every
cut bond is replaced by hydrogens that restore the atom’s valence in the
parent. Double-bond stereochemistry and the relative configuration of
stereocentres are kept, so cis and trans fragments are different species –
except labels on the fusion atoms of tiny rings that leave a fragment that
cannot be built in 3D (see _feasible_stereo()).
Groups that should not be broken (aromatic ring systems, three- and four-membered rings, charge-separated or hypervalent groups such as nitro and sulfonyl, and whole inorganic ions) are contracted into single nodes of the graph first (“coarse-grained” or “ring-preserved” CBH). The hierarchy on the contracted graph is shallower: for 4-chlorotoluene with the ring protected, CBH-1 is Cl-C6H4-CH3 + C6H6 -> C6H5Cl + C6H5CH3 and CBH-2 is already trivial.
Limitations: the simple overlap formula is valid only when every unprotected ring of size r satisfies rung <= r - 2; such a rung is reported as unavailable. Aromatic rings that are not protected are Kekulized, which makes CBH-3 and higher depend on the Kekule structure chosen. A delocalized charge or spin must be localized on one atom by the input SMILES.
- exception seamm_thermochemistry.cbh.CBHError[source]#
Bases:
ExceptionA problem generating a CBH reaction.
- class seamm_thermochemistry.cbh.Reaction(rung, target)[source]#
Bases:
objectOne CBH reaction.
- rung#
- Type:
int
- coefficients#
Stoichiometric coefficients by canonical SMILES (products positive, reactants negative, the target -1). Empty if the rung is trivial.
- Type:
{str: int}
- fragments#
The fragment SMILES as generated (with stereo) by canonical SMILES.
- Type:
{str: str}
- trivial#
The net reaction is empty: the rung reproduces the target.
- Type:
bool
- balanced#
- Type:
bool
- balance_errors#
- Type:
[str]
- protected#
Descriptions of the groups contracted into single nodes.
- Type:
[str]
- property products#
- property reactants#
- property references#
The species other than the target, with their coefficients.
Bases:
CBHErrorThe rung cannot be built for this molecule (small unprotected rings or parallel edges in the contracted graph).
- seamm_thermochemistry.cbh.SINGLET_GROUND_STATES = ('[SiH2]', 'Cl[Si]Cl', 'F[Si]F', '[GeH2]', 'F[C]F', 'Cl[C]Cl', 'F[CH]', 'Cl[CH]', '[BH]', '[AlH]', 'F[B]', 'Cl[B]')#
Species whose SMILES puts two unpaired electrons on one atom but whose ground state is a singlet: the silylenes and germylene, the dihalocarbenes and halocarbenes, and BH, AlH, BF and BCl. Methylene (CH2) and imidogen (NH) are ground-state triplets and are not listed.
- seamm_thermochemistry.cbh.TRIPLET_GROUND_STATES = ('O=O', 'S=S', 'O=S', 'O=C=C=O', 'C1=C=C=1')#
Species that a SMILES writes closed-shell but whose ground state is a triplet: O2 and its valence isoelectronic S2 and SO, ethylenedione (OCCO, whose singlet is a saddle point) and cyclic C3 (whose singlet opens to linear C3; ATcT’s plain entry is the triplet).
- seamm_thermochemistry.cbh.cbh_reactions(smiles, rungs=(0, 1, 2, 3, 4), protect='default')[source]#
The CBH reactions of a molecule.
- Parameters:
smiles (str) – The target, with its double-bond stereochemistry, formal charges and radical centres. Aromatic rings may be written aromatic.
rungs (iterable of int) – The rungs wanted.
protect ("default" | "none" | sequence) – Which groups to contract into single nodes: “aromatic” (aromatic ring systems, fused rings merged), “small rings” (3- and 4-membered), “groups” (the charge-separated and hypervalent groups in PROTECT_SMARTS), “all rings”, or SMARTS patterns. “default” is aromatic + small rings + groups.
- Returns:
{int – A Reaction for each rung. A rung that cannot be built on this graph raises RungUnavailable unless it is above a trivial rung, in which case it is trivial too.
- Return type:
Reaction}
- seamm_thermochemistry.cbh.ground_state_multiplicity(smiles)[source]#
The spin multiplicity of a species’ ground state: 1 for those in SINGLET_GROUND_STATES, 3 for those in TRIPLET_GROUND_STATES, otherwise its unpaired electrons + 1, as the SMILES implies (a radical centre
[CH3]is a doublet,[CH2]a triplet).
- seamm_thermochemistry.cbh.unique_fragments(reactions)[source]#
The union of the reference species over several reactions, with the generated (stereo) SMILES, so that a batch computes each fragment once.
- Parameters:
reactions (iterable of Reaction)
- Returns:
{str – canonical SMILES -> generated SMILES
- Return type:
str}
seamm_thermochemistry.curated module#
Reading hand-curated reference values into a reference library.
The curated files (one CSV per source: printed JANAF, CODATA key values, the NBS tables of 1982, spectroscopic D0, W4-17, derived values) share common columns:
smiles, formula, charge, multiplicity, name, phase, dfH0_kJmol,
dfH298_kJmol, unc_kJmol, unc_level, status, citation, notes
plus columns of their own. This is a maintainer tool: it builds a library file, which is then published; users install the published file.
Each row is added as a measurement of its source, then the value of each species and source is chosen, the overrides applied and the derivations checked. Rows the rules refuse are reported, not imported.
- seamm_thermochemistry.curated.ATOM_MULTIPLICITY = {'Al': 2, 'As': 4, 'B': 2, 'Be': 1, 'Br': 2, 'C': 3, 'Ca': 1, 'Cl': 2, 'F': 2, 'Ga': 2, 'Ge': 3, 'H': 2, 'K': 2, 'Li': 2, 'Mg': 1, 'N': 4, 'Na': 2, 'O': 3, 'P': 4, 'S': 3, 'Se': 3, 'Si': 3}#
Ground-state spin multiplicities of the atoms, for rows that leave it blank
- seamm_thermochemistry.curated.CURATED_SOURCES = {'codata.csv': ('CODATA-1989', 'evaluated', 20, 1989, 'J. D. Cox, D. D. Wagman and V. A. Medvedev, CODATA Key Values for Thermodynamics (Hemisphere, 1989)'), 'd0.csv': ('D0', 'measured', 30, None, 'Spectroscopic dissociation energies'), 'derived.csv': ('derived', 'derived', 11, None, 'Values derived from evaluations'), 'janaf_printed.csv': ('JANAF-1998', 'evaluated', 10, 1998, 'NIST-JANAF Thermochemical Tables, 4th ed., M. W. Chase, J. Phys. Chem. Ref. Data Monograph 9 (1998)'), 'nbs82.csv': ('NBS-82', 'evaluated', 25, 1982, 'D. D. Wagman et al., The NBS Tables of Chemical Thermodynamic Properties, J. Phys. Chem. Ref. Data 11, Suppl. 2 (1982)'), 'w4_17.csv': ('W4-17', 'literature', 40, 2017, 'A. Karton, N. Sylvetsky and J. M. L. Martin, J. Comput. Chem. 38, 2063 (2017)')}#
The files, their source and the source’s kind, rank and year
- seamm_thermochemistry.curated.import_curated(library, directory, *, files=None)[source]#
Read the curated CSVs in directory into library.
Returns a list of (file, smiles, outcome, detail) for every row, where outcome is “added”, “refused” or “held”, and the problems found by
ReferenceLibrary.check().
seamm_thermochemistry.db module#
SQLite-backed store for atomic reference energies.
Every SEAMM code that reports a formation-referenced energy (Gaussian, Psi4, ORCA, VASP, …) needs the same two pieces of data per element: an experimental reference (heat of formation, entropy, standard-state description) and one or more computed atomic reference energies (one per code/method/settings combination). Today each plugin carries its own copy of both as a wide, mostly-empty CSV. This module replaces that with a single, shared, relational store – one element row per element, one atom_energy row per (element, code, method, ref_type, settings) combination – with room for the provenance a shared reference dataset needs (who computed it, when, with what).
Two reference conventions are both first-class (ref_type):
"atom": energy of the isolated, gas-phase atom. This is the Gaussian/Psi4/ORCA convention, and the one to use for a physically anchored, cross-code-comparable energy of formation."element_phase": energy per atom of the element’s standard-state phase (bulk metal, graphite, O2(g), …). This is VASP’s existing convention (element_energies.csv) – no experimental anchor needed, but the result is referenced to the computed elemental phases, not real formation energies. It also serves as a fallback for elements (e.g. Mn) where the free atom is a poor DFT reference.
See formation.py for the arithmetic that consumes this store.
- class seamm_thermochemistry.db.ThermoDB(path=None, read_only=False)[source]#
Bases:
objectHelper around the shared atomic reference-energy SQLite database.
- Parameters:
path (str or Path, optional) – Database file. Defaults to
DEFAULT_DB_PATH: the installer- managed location registered in~/.seamm.d/seamm.ini’s[thermochemistry]section (database-path, normally~/SEAMM/Parameters/thermochemistry/thermochemistry.db– seeinstaller.py) if the installer has been run, else the bundleddata/thermochemistry.dbshipped with the package (a stale prototype snapshot, useful only before the installer has run).read_only (bool) – Open without creating/migrating the schema, and without permission to write. Use for a package-installed, already-built database.
- add_atom_energy(symbol, code, method, energy, *, ref_type='atom', settings='', units='kJ/mol', correction=None, spin_multiplicity=None, source=None, computed_date=None, code_version=None, notes=None)[source]#
Insert or update one computed atomic reference energy.
- Parameters:
symbol (str) – Element symbol, e.g. “O”. Must already exist via add_element.
code (str) – Originating code: “gaussian”, “psi4”, “orca”, “vasp”, …
method (str) – Method/functional label, e.g. “CBS-QB3”, “PBE-D3BJ”. Where the basis is folded into the label (as in the legacy Gaussian/Psi4 tables, e.g. “CCD-FC/6-31++G(2d,2p)”), pass the full label.
energy (float) – The atom’s energy, in units (converted to kJ/mol on storage).
ref_type ({"atom", "element_phase"}) – “atom” = isolated gas-phase atom. “element_phase” = energy per atom of the standard-state phase (VASP’s existing convention).
settings (str) – Free-form disambiguator that is part of the identity key, e.g. “encut=700eV”. Defaults to “” (not NULL, so the UNIQUE constraint dedups consistently).
units (str) – Units of energy and correction: one of “kJ/mol”, “kcal/mol”, “eV”, “hartree”/”E_h”.
correction (float, optional) – Additive correction (e.g. Gaussian’s “<method> correction” column), same units as energy.
- add_element(atomic_number, symbol, *, multiplicity=None, term_symbol=None, standard_state=None, dfH0_0K=None, dfH0_298K=None, dfH0_298K_stderr=None, h298_minus_h0_atom=None, h298_minus_h0_std_state=None, s298_gas=None, s298_gas_stderr=None, s298_std_state=None, s298_std_state_reference=None, reference=None, reference_note=None)[source]#
Insert or update the experimental reference row for one element.
- batch()[source]#
Defer commits until the end of the block, for bulk imports.
add_element/add_atom_energy normally commit immediately – the right default for interactive use, but one fsync per row makes a bulk import of hundreds of thousands of rows (e.g. the full Gaussian/Psi4 composite-method grids) prohibitively slow. Wrap those calls in
with db.batch():to commit once at the end instead.
- coverage(code, method, *, ref_type='atom', settings='')[source]#
Return (n_elements_present, max_atomic_number) for one (code, method).
- dfH0(symbol, *, at_0K=True)[source]#
Experimental atomic heat of formation, kJ/mol.
- Parameters:
at_0K (bool) – If True (default, and the recommended anchor for an “energy of formation” – see the design doc), return DfH(X, 0K). If False, return the 298 K gas-phase value used by the existing enthalpy-of-formation code paths.
- doi()[source]#
The Zenodo DOI of the published snapshot this database file IS, or None for a working copy that has not been published yet.
Unlike the schema version (which just says “the table layout this code understands”), the DOI identifies the actual data – the citable, permanent reference for reproducibility. Set once, right before the file is uploaded to Zenodo – see
scripts/publish_to_zenodo.pyand set_doi.
- dump_atom_energies_csv(path)[source]#
Write the atom_energy table (joined to element symbols) to CSV.
- get_atom_energy(symbol, code, method, *, ref_type='atom', settings='', units='kJ/mol')[source]#
Return one atomic reference energy (including its correction), or None.
- get_atom_energy_row(symbol, code, method, *, ref_type='atom', settings='')[source]#
Return the full atom_energy row (energy, correction, source, computed_date, code_version, notes, …) as a dict, or None.
For provenance/reporting – get_atom_energy returns just the combined number get_reference_energies/atomization_energy need.
- get_element(symbol=None, atomic_number=None)[source]#
Return the experimental reference row for one element as a dict, or None.
- get_reference_energies(code, method, *, ref_type='atom', settings='', units='kJ/mol')[source]#
Return
{symbol: energy}for every element tabulated for this (code, method, ref_type, settings) combination.This is the lookup table formation.formation_energy needs.
- missing(code, method, symbols, *, ref_type='atom', settings='')[source]#
Return the subset of symbols with no tabulated (code, method) energy.
- s298_gas(symbol)[source]#
Experimental standard molar entropy of the isolated gas atom, S°(X, g, 298.15 K), J/(mol K), or None.
- s298_std_state(symbol)[source]#
Experimental standard molar entropy of the element’s standard-state phase, PER ATOM of X (e.g. S°(H2,g,298.15K)/2 for H), J/(mol K), or None. Needed (together with s298_gas) for a Gibbs-energy-of- formation anchor – see formation.formation_gibbs_energy.
- schema_version()[source]#
The schema version stamped in this database (an int), for reports/provenance – see _SCHEMA_VERSION.
- set_doi(doi)[source]#
Stamp this database file with the Zenodo DOI of the snapshot it is about to become. Call this right before uploading the file – Zenodo prereserves a DOI as soon as a deposit/version is created, before any file is attached, so the DOI can be baked into the file itself rather than only living in the Zenodo record around it.
seamm_thermochemistry.derive module#
Derive DfH(X, 0K) from the 298 K experimental data already in the store.
The master reference sheet only has ΔfH°(0K) filled in for H and He; every other element has ΔfH°(298K) plus the two H(298K)-H(0K) corrections needed to convert it, via the standard thermochemical identity:
DfH(X, 0K) = DfH(X, 298K) - [H298-H0]_atom + [H298-H0]_standard_state
h298_minus_h0_std_state is tabulated per formula unit of the standard state, not per atom – for a diatomic standard state (H2, N2, O2, F2, Cl2, Br2) it must be divided by 2 to get a per-atom contribution; for the monatomic/solid standard states used by every other element in range, no division is needed. Verified against the two elements the master sheet already gives directly: H (218 - 6.197 + 8.468/2 = 216.03, matches 216.034) and He (0 - 6.197 + 6.197 = 0, matches 0), both to within the sheet’s own rounding.
- seamm_thermochemistry.derive.derive_dfH0_0K(db, *, overwrite=False)[source]#
Fill in element.dfH0_0K for every element where it’s derivable.
- Parameters:
db (ThermoDB)
overwrite (bool) – If False (default), skip elements that already have dfH0_0K – the master sheet’s own H/He values are treated as authoritative over a re-derivation (though the derivation reproduces them to within rounding; see module docstring).
- Returns:
Symbols of the elements filled in.
- Return type:
list of str
seamm_thermochemistry.formation module#
The shared arithmetic: atomization and formation energies from a ThermoDB.
Every code (Gaussian, Psi4, ORCA, VASP, …) that reports a formation energy is doing the same subtraction:
atomization = sum(n_X * E_ref(X)) - E(system)
and, when an experimental anchor is available,
formation = sum(n_X * DfH(X)) - atomization
They differ only in the choice of E_ref (an isolated gas atom, or an atom of
the element’s standard-state phase – ref_type on ThermoDB) and whether an
experimental anchor is added at all. See
~/Sites/reference-energy/2026-07-24_reference-energy/ for the full
design rationale, including why the arbitrary code-dependent zero of a raw
total energy is the problem this solves, and the “no thermochemistry ->
energy of formation, not enthalpy” case this module is aimed at.
Sign convention, checked against the existing gaussian_step / psi4_step / vasp_step implementations:
atomization_energymatches Gaussian/Psi4’s “E atomization” (>0 for a bound system: separated atoms sit higher in energy than the molecule).formation_energy(..., anchor=True)matches the existingDfH_at - H_atomizationconstruction (Gaussian/Psi4’s enthalpy of formation), generalized to also give a ZPE-free “energy of formation” when the 0 K anchor is used and system_energy excludes ZPE.formation_energy(..., anchor=False)matches VASP’s existingDfE0 = E(system) - sum(n_X * mu_X)(materials formation-energy convention, no experimental data required) – note this is-atomization_energy, not atomization_energy itself; the two conventions report the quantity with opposite sign, which is why they are two functions rather than one flag.
- exception seamm_thermochemistry.formation.MissingReferenceData[source]#
Bases:
KeyErrorRaised when a composition needs reference data the ThermoDB doesn’t have.
- seamm_thermochemistry.formation.atomization_energy(composition, system_energy, db, code, method, *, ref_type='atom', settings='', units='kJ/mol')[source]#
sum(n_X * E_ref(X)) - E(system): the energy to separate all atoms.
This is exactly Gaussian/Psi4’s “Atomization Energy” (electronic-only, no ZPE, if system_energy is itself ZPE-free) and VASP’s “cohesive energy”. Positive for a bound system.
- Parameters:
composition (dict or collections.Counter) – {element_symbol: count}, e.g. {“C”: 2, “H”: 6}.
system_energy (float) – The system’s electronic energy, in units.
db (ThermoDB) – Open reference-energy database.
code (str) – Identify which tabulated atom energies to use.
method (str) – Identify which tabulated atom energies to use.
ref_type ({"atom", "element_phase"}) – See
ThermoDB.add_atom_energy.settings (str) – Must match the settings used when the atom energies were stored.
units (str) – A pint-parseable unit string for system_energy and the return value, e.g. “kJ/mol”, “eV”, “E_h”.
- seamm_thermochemistry.formation.formation_energy(composition, system_energy, db, code, method, *, ref_type='atom', settings='', anchor=True, anchor_at_0K=True, units='kJ/mol')[source]#
The formation-referenced energy of a system.
- Parameters:
composition – As in atomization_energy.
system_energy – As in atomization_energy.
db – As in atomization_energy.
code – As in atomization_energy.
method – As in atomization_energy.
ref_type – As in atomization_energy.
settings – As in atomization_energy.
units – As in atomization_energy.
anchor (bool) – If True (default), add the experimental atomic heat of formation so the result is a true energy/enthalpy of formation – the physically meaningful, cross-code-comparable quantity this package exists for. If False, return the bare, no-experimental-data-needed quantity: VASP’s existing
DfE0convention (ref_type="element_phase"is the natural pairing, but any ref_type is accepted).anchor_at_0K (bool) – Use the 0 K experimental anchor (recommended: pairs with a ZPE-free system_energy to give an “energy of formation”, degrades gracefully to DfH(0K) if ZPE is added back in, and to DfH(298K) if the molecule’s thermal correction is added) or the 298 K anchor (matches the existing enthalpy-of-formation code paths directly). Ignored if anchor is False.
- Returns:
Energy/enthalpy of formation (anchor=True) or the bare atomization-referenced energy in VASP’s DfE0 sign convention (anchor=False), in units.
- Return type:
float
- seamm_thermochemistry.formation.formation_enthalpy(composition, H_system, temperature, db, code, method, *, ref_type='atom', settings='', units='kJ/mol')[source]#
DfH(T): enthalpy of formation from the elements, at temperature T.
Generalizes
formation_energy(..., anchor=True, anchor_at_0K=True)from the electronic-only, 0 K quantity to a real temperature. The Hess cycle is elements (standard states, T) -> gas atoms (T) -> molecule (T). The gas atoms’ own H(T)-H(0) appears in both halves of the cycle and cancels exactly, leaving the standard-state elements’ thermal enthalpy (the form used by G4 and similar composite methods):DfH(T) = sum(n_X * [DfH0(X, 0 K) - (H_std(X, T) - H_std(X, 0))]) - [sum(n_X * E_ref(X)) - H_system]
where H_std is the enthalpy of the element’s standard state, per atom (ThermoDB.element.h298_minus_h0_std_state, halved for the diatomic gases). Equivalently, DfH(T) = sum(n_X * DfH0(X, T)) - [atomization enthalpy at T]; at 298.15 K the two forms use the same tabulated data and agree to rounding.
Approximation: the standard-state H(T)-H(0) is tabulated at 298.15 K only and is scaled linearly in T elsewhere – see _std_state_thermal_enthalpy for the size of the error. At 298.15 K the result is exact.
History: until 2026.10.28 this function subtracted (5/2)RT per atom, the gas atom’s own thermal enthalpy, instead of the standard-state term. That made DfH(298 K) too negative by 1.96 kJ/mol per H, 5.15 per C and 1.86 per O (5.8 kJ/mol for water).
- Parameters:
composition – As in atomization_energy.
db – As in atomization_energy.
code – As in atomization_energy.
method – As in atomization_energy.
ref_type – As in atomization_energy.
settings – As in atomization_energy.
units – As in atomization_energy.
H_system (float) – The system’s total enthalpy H(T) – electronic + ZPE + thermal correction – on the SAME absolute energy scale as the electronic energy stored for the reference atoms (not a thermal correction alone), in units.
temperature (float) – Temperature, in K.
- Returns:
DfH(T), in units.
- Return type:
float
- seamm_thermochemistry.formation.formation_gibbs_energy(composition, G_system, temperature, db, code, method, *, ref_type='atom', settings='', units='kJ/mol')[source]#
DfG(T): Gibbs energy of formation from the elements, at T.
Same Hess-cycle construction as formation_enthalpy, extended to G. Requires each element’s s298_std_state (the standard-state phase’s molar entropy, per atom – see ThermoDB.s298_std_state) in addition to the usual 0 K atom energies, heats of formation and standard-state H(298 K)-H(0 K); raises MissingReferenceData for any element missing any of them.
Derivation: on the computed energy scale the standard-state element’s Gibbs energy per atom is:
G_std(X, T) = E_ref(X) - DfH0(X, 0K) + [H_std(X, T) - H_std(X, 0)] - T * S_std(X, T)
(the gas atom at 0 K sits DfH0(X, 0K) above the standard state, and has no ZPE), so:
DfG(T) = G_system - sum(n_X * G_std(X, T)) = sum(n_X * [DfH0(X, 0K) - (H_std(X, T) - H_std(X, 0))]) + T * sum(n_X * S298_std_state(X)) / 1000 - [sum(n_X * E_ref(X)) - G_system]
No atomic entropy is needed: the gas atom is only an intermediate.
Approximations: S298_std_state is used at its tabulated 298.15 K value regardless of T, and the standard-state H(T)-H(0) is scaled linearly from its 298.15 K value (see _std_state_thermal_enthalpy). At T=0 this reduces exactly to
formation_energy(..., anchor=True, anchor_at_0K=True)evaluated with G_system in place of the electronic energy.History: until 2026.10.28 this function subtracted (5/2)RT per atom instead of the standard-state thermal enthalpy; see formation_enthalpy.
- Parameters:
composition – As in atomization_energy.
db – As in atomization_energy.
code – As in atomization_energy.
method – As in atomization_energy.
ref_type – As in atomization_energy.
settings – As in atomization_energy.
units – As in atomization_energy.
G_system (float) – The system’s total Gibbs free energy G(T), on the same absolute scale as the electronic energy, in units.
temperature (float) – Temperature, in K.
- Returns:
DfG(T), in units.
- Return type:
float
seamm_thermochemistry.fragment_cli module#
seamm-thermochemistry-fragments: the fragment-energy store from the
command line:
seamm-thermochemistry-fragments harvest JOB_DIR [JOB_DIR ...] [--store FILE]
seamm-thermochemistry-fragments list [SMILES] [--store FILE]
seamm-thermochemistry-fragments lookup SMILES [--charge Q] [--multiplicity M]
[--model-chemistry MC] [--fingerprint FP]
seamm-thermochemistry-fragments duplicates [--store FILE]
seamm-thermochemistry-fragments harvest JOB_DIR ... --supersede "REASON"
seamm-thermochemistry-fragments superseded [SMILES] [--store FILE]
seamm-thermochemistry-fragments writeback-log [--job JOB] [--species SMILES]
seamm-thermochemistry-fragments duplicates --layers [--official FILE]
seamm-thermochemistry-fragments migrate [--store FILE]
seamm-thermochemistry-fragments merge SOURCE.db ... [--store FILE]
seamm-thermochemistry-fragments export --out FILE [--label TEXT]
seamm-thermochemistry-fragments official build --version V --out FILE
[--previous FILE] SOURCE.db ...
--store is the local store (fragments.db), the one every command
writes; --official is the official store (fragments-official.db, read
only), which lookup and geometries read after the local one. Run
harvest on the host after a job has finished, never on a compute node.
seamm_thermochemistry.fragment_store module#
The fragment-energy store: energies of CBH fragments (and targets) computed earlier, so that a species is computed once per level of theory.
The store is one SQLite file, fragments.db next to the ATcT cache
(Parameters/thermochemistry/), and must live on a local disk: SQLite
locking is unreliable on GPFS/NFS, so the connection uses no memory mapping,
a busy timeout and the rollback journal, and nothing on a compute node ever
writes to it. Jobs store their species’ energies as properties on their own
configurations (the systems named CBH:<SMILES>); afterwards the single
writer, seamm-thermochemistry-fragments harvest <job> on the host,
promotes them with insert-or-ignore and full provenance.
A record is keyed by the species identity (canonical SMILES, charge and spin multiplicity) and two opaque fingerprints: the one of the calculation that gave the energy and the one of the calculation that gave the frequencies (ZPE and thermal enthalpy), which can be at different levels. The QC step owns the fingerprint (a hash of the generated input without the lines that do not affect the energy, plus program and version); this module never interprets it. The model chemistry string is kept for people to read. A record without a fingerprint is keyed on the model chemistry and code version instead and flagged unverified: it is used only when nothing verified exists, and the caller is told.
E, ZPE and H(T) are separate fields in kJ/mol. A record whose key already
exists is ignored (first value wins); if its values differ from the stored
ones by more than 0.1 kJ/mol the attempt is logged in duplicate_log, never
overwritten, which catches a different conformer or SCF solution. A value
known to be wrong (a wrong conformer, say) is replaced only explicitly, with
supersede: the old values move to the superseded table with the date
and the reason, and the record keeps its key and id, so nothing is lost and
the replacement is visible.
Reads are best effort: a missing, locked or corrupt store gives None and a log message, and the caller computes the species.
- class seamm_thermochemistry.fragment_store.EnergyRecord[source]#
Bases:
dictA row of the
energytable.- property key#
- static make(geometry_hash, E, *, energy_fingerprint=None, calculation_geometry=None, model_chemistry=None, program=None, version=None, byproducts=None, job=None, configuration=None, source=None, date=None)[source]#
Build an energy row for the geometry with hash geometry_hash.
calculation_geometry is the geometry fingerprint the calculation recorded, if any; it must equal geometry_hash (the pairing guard). Without an energy fingerprint the row is keyed on the model chemistry and code version and flagged unverified.
- class seamm_thermochemistry.fragment_store.FragmentRecord[source]#
Bases:
dictOne record of the store, a dict with the columns of
fragmentandverifiedas a bool.- property key#
- static make(species, *, charge=0, multiplicity=1, E=None, ZPE=None, H=None, T=298.15, energy_fingerprint=None, frequency_fingerprint=None, model_chemistry=None, frequency_model_chemistry=None, program=None, version=None, geometry_hash=None, job=None, configuration=None, source=None, date=None)[source]#
Build a record. Without an energy fingerprint the record is keyed on the model chemistry and code version and flagged unverified. The model chemistries are stored bare, with the program in
program(split_model_chemistry()).
- class seamm_thermochemistry.fragment_store.FragmentStore(path=None, *, create=False, immutable=False, layer='local')[source]#
Bases:
GeometryEnergyMixinThe fragment-energy store (see the module docstring), with the geometry and energy tables of
geometry_store.- Parameters:
path (str or Path, optional) – The SQLite file; default next to the ATcT cache.
create (bool) – Create the file and schema if missing (the writer); a reader passes False and gets an empty store when there is no file.
immutable (bool) – Open a file that nothing will change while it is open (the official store) read-only without locking (SQLite’s
immutable=1), which is safe on network filesystems such as GPFS.layer (str) – For a new file (with create): its layer, “local” (the default), “official” or “contribution”, recorded in its
metatable.
- get(species, *, charge=0, multiplicity=1, energy_fingerprint=None, frequency_fingerprint=None, model_chemistry=None, version=None)[source]#
The best record for a species at a level, or None.
With fingerprints, the verified record with those fingerprints (the frequency fingerprint may be None to accept any, including none; each may be one or several, see
geometries()). Without, or when there is no verified record, an unverified record of the same model chemistry (either spelling, seesplit_model_chemistry(); and code version if given); the caller seesrecord["verified"]and reports it.A record with thermal data (H) comes before one with the energy alone (stored by a run without thermal corrections, e.g. MOPAC’s enthalpy of formation taken as the enthalpy), so either kind of run finds what it needs when both are stored for a level.
Any error reading the store gives None and a log message.
- property layer#
“local”, “official” or “contribution”; a store from before the meta table is a local one.
- put(record)[source]#
Insert a record unless its key exists.
Returns “inserted”, “duplicate” (same key, same values), “logged” (same key, values differing by more than 0.1 kJ/mol: the attempt went to
duplicate_log, the stored values stay) or “quarantined” (the key is held by a record quarantined by hand,UNUSABLE: <reason>: it stays, and the attempt goes torow_history; onlysupersede(), a person’s decision with a reason, replaces it).
- release(candidate, reason)[source]#
Release a quarantined row: replace it with the values of a release candidate (an id from
release_candidates()), keeping the quarantined values as superseded, with reason (a person’s).Returns “superseded”; raises ValueError if candidate is not a candidate (not logged as one, or its row no longer quarantined).
- release_candidates(species=None)[source]#
New values that met a row quarantined by hand and were kept out.
A harvest or write-back that brings a row for a key held by a quarantined row logs it in
row_history(kind “quarantined”) after it passed every check of the harvest. Each such attempt whose row is still quarantined is a candidate forrelease(). Returns, oldest first, [{id, table, row, species, charge, multiplicity, conformer, date, job, quarantine, old, new}], old and new each with E (and ZPE, H) and the model chemistry; species keeps only its rows.
- supersede(record, reason, force=False)[source]#
Replace the stored values for the record’s key, keeping the old ones.
The old values go to the
supersededtable with today’s date and reason; the record keeps its key and id. A key not yet in the store is simply inserted. A value the new record lacks (None) keeps the stored one, so a corrected energy does not erase a stored ZPE or H.With force, values that differ by less than the duplicate tolerance (0.1 kJ/mol) are replaced too, so that a correction applied to a whole level of theory reaches every record, however small; identical values are still left alone.
Returns “inserted”, “unchanged” (same values within 0.1 kJ/mol, or identical with force: nothing to replace) or “superseded”.
- class seamm_thermochemistry.fragment_store.GeometryRecord[source]#
Bases:
dictA row of the
geometrytable (a dict; JSON columns as Python values).- property key#
- static make(species, symbols, coordinates, *, charge=0, multiplicity=1, conformer='', frequency_fingerprint=None, optimization_fingerprint=None, model_chemistry=None, optimization_model_chemistry=None, program=None, version=None, E=None, ZPE=None, H=None, T=298.15, frequencies=None, symmetry_number=None, moments=None, conformer_provenance=None, atomic_numbers=None, geometry_hash=None, job=None, configuration=None, source=None, date=None)[source]#
Build a geometry row.
Without a frequency fingerprint the row is keyed on the model chemistry and code version and flagged unverified. The geometry hash is computed from the atomic numbers (or symbols) and coordinates unless given.
optimization_model_chemistry, when given, must be the frequency level’s (model_chemistry). The model chemistry is stored bare, with its program inprogram(split_model_chemistry()).
- exception seamm_thermochemistry.fragment_store.PairingError[source]#
Bases:
ValueErrorAn energy that is not on the geometry it would be attached to, or a geometry optimized at one level with frequencies from another.
- seamm_thermochemistry.fragment_store.check_structure(symbols, coordinates, species, charge=None, multiplicity=None)[source]#
Why a structure is not one of its species, or None if it is.
The bonds are perceived again from the coordinates, not taken from the structure’s own (which came from the SMILES it was built from), so a species that rearranged while it was optimized is caught. The graph (elements and connections) must be that of species, whose formal charge must be charge and whose radical electrons must have the parity of the unpaired electrons of multiplicity; and no atoms may be too close together (
molsystem.vsepr.check_contacts(), which also catches ligands of a hypervalent centre on top of each other). Without RDKit nothing is checked.
- seamm_thermochemistry.fragment_store.fragment_store_path(path=None)[source]#
The store file: path if given, else
fragments.dbnext to the ATcT cache (<...>/Parameters/thermochemistry/fragments.db).
- seamm_thermochemistry.fragment_store.harvest_configuration(store, configuration, species, *, charge=None, multiplicity=None, conformer='', job=None, mapped=None, label=None, energy='total energy#*#*', zpe='ZPE#*#*', enthalpy='H(298.15 K)#*#*', fingerprint='settings fingerprint#*#*', frequency_fingerprint='frequency fingerprint#*#*', supersede=None, force=False, tables='all', structure_check=True, write_back=False, counts=None)[source]#
Put one configuration’s species into the store: what
harvest_job()does for each system of a job, and what the Thermochemistry step does for each species it computes (write-back).Every energy property matching energy gives a fragment row (unless tables is “geometry”) and an energy row on the configuration’s geometry, and, with the ZPE or enthalpy of the same level, a geometry row. See
harvest_job()for the properties read and the guards.A geometry with an imaginary mode beyond
IMAGINARY_TOLERANCEis refused, with every energy on it, in a harvest as in a write-back. A key held by a row quarantined by hand (UNUSABLE: <reason>) keeps it: the attempt is logged and reported, “quarantined” in the tallies, and only an explicit supersede (a person’s decision, with a reason) replaces it.write_back adds the rule for results written by a running job:
a geometry lower in E + ZPE by more than 0.1 kJ/mol than every stored conformer of the species at its level, and structurally distinct from each (RMSD above
CONFORMER_RMSDÅ), is added as a new conformerauto:<geometry hash>instead of being only logged; lookups use the lowest.
Returns the tallies, counts updated in place when given.
- seamm_thermochemistry.fragment_store.harvest_job(job_directory, store, *, energy='total energy#*#*', zpe='ZPE#*#*', enthalpy='H(298.15 K)#*#*', fingerprint='settings fingerprint#*#*', frequency_fingerprint='frequency fingerprint#*#*', name_prefix='CBH:', all_systems=False, database='seamm.db', supersede=None, force=False, species_map=None, tables='all', structure_check=True)[source]#
Promote the species energies of one job into the store.
Every system whose name starts with name_prefix (
CBH:<SMILES>: the SMILES is the species identity), or every system when all_systems (the name, less the prefix if present, canonicalized, is the species, as for systems that Read Structure named by SMILES), contributes one record per energy property found on its current configuration, with the ZPE and enthalpy of the same level when present, the fingerprints if the QC step stored them, and the provenance (job directory, configuration id, geometry hash, date). The model chemistry is the third#field of the energy property’s name.With a species_map ({(job, system name): row}, see
load_species_map()) the systems are found by their names in the map instead of by the prefix: the map gives each one’s species (the store key, with the stereo of its structure), and optionally its configuration, charge, multiplicity and conformer label. Systems the map does not list are skipped. tables is “all” (the fragment table too) or “geometry” (only the geometry and energy tables, e.g. for several conformers of a species, which the fragment table cannot hold).The species map may also carry the thermal corrections of the structures whose jobs did not store them as properties:
E (kJ/mol),ZPE (kJ/mol)(unscaled),H298 (kJ/mol)(H at 298.15 K) andsymmetry number. They are used only for an energy with no ZPE or enthalpy of its own level, as H - E added to the job’s energy, and only when the map’s E agrees with the job’s within 0.1 kJ/mol (otherwise the system is reported and gets no geometry row).With supersede (the reason, e.g. “lowest conformer from B2PLYP geometries”), a record whose key is already stored with different values replaces them, the old values kept in the
supersededtable, instead of being logged as a differing duplicate. With force as well, values within the duplicate tolerance are replaced too (seeFragmentStore.supersede()).The same properties also fill the geometry and energy tables (
geometry_store): an energy property with a ZPE or enthalpy of the same level makes a geometry row at that level (the configuration’s coordinates, its energy, ZPE and H, and the frequencies, symmetry number and moments of inertia when stored asfrequencies,symmetry numberandmoments of inertiaproperties of the same level), and every energy property makes an energy row on the configuration’s geometry. An energy whosecalculationrecord names a different geometry is refused (the pairing guard) and reported in “skipped”.With structure_check (the default) a structure whose bonds, perceived again from its coordinates, are not those of its species, or that has atoms too close together, is refused (see
check_structure()).Returns {“inserted”: n, “duplicate”: n, “logged”: n, “superseded”: n, “skipped”: [reasons], “geometry”: {outcome: n}, “energy”: {outcome: n}}, the first four counting the fragment rows.
- seamm_thermochemistry.fragment_store.load_species_map(path)[source]#
A species map from a CSV file: {(job, system name): row}.
The columns are
job(the job id, or empty for any job),system nameandspecies(the store key: the canonical SMILES, with the stereo of the structure), and optionallyconfiguration name(the configuration to use, default the system’s current one),charge,multiplicityandconformer label, and the thermal correctionsE (kJ/mol),ZPE (kJ/mol),H298 (kJ/mol)andsymmetry number(seeharvest_job()). Other columns are ignored.
- seamm_thermochemistry.fragment_store.split_model_chemistry(text, program=None)[source]#
The bare model chemistry and the program of a model chemistry string.
ORCA:CMP@R2SCAN-3Cgives (”CMP@R2SCAN-3C”, “ORCA”); a bareCMP@R2SCAN-3Cgives (”CMP@R2SCAN-3C”, program). Only a prefix before the first@is a program, soORCA:DFT@B3LYP/bse:def2-SVPgives (”DFT@B3LYP/bse:def2-SVP”, “ORCA”), and a string without@(e.g. a fingerprint,sha256:...) is returned as it is. A program given explicitly wins over a different prefix, which is logged.- Parameters:
text (str or None) – The model chemistry.
program (str, optional) – The program, if known otherwise.
- Return type:
(str or None, str or None)
- seamm_thermochemistry.fragment_store.store_is_writable(path=None)[source]#
(bool, reason): whether a running job may write the fragment store.
Only on a local filesystem: SQLite’s locking is unreliable on network and parallel filesystems (NFS, GPFS, Lustre). seamm.ini’s
[thermochemistry]fragment-store-writable = auto | yes | nooverrides the check.
seamm_thermochemistry.frequency_corrections module#
Corrections of harmonic frequencies, one frequency at a time, with their sources.
Some methods get their frequencies wrong in a way a single scale factor cannot fix. PM7, for one, puts the X-H stretches near a quarter of their real spread, so its frequencies above 2500 cm^-1 need a steep linear map (Rozanska et al. 2014, eq 1). Such a correction is a piecewise-linear function of the frequency, fitted for a method (and basis), and is applied to each real frequency before the zero-point energy and the thermal functions (H(T) - H(0), S, Cp) are computed.
A correction replaces the ZPE scale factor for its method: the two are never
applied together. Each entry carries its citation, the elements its fit set
contained, and the frequency window where the map is discontinuous or was not
validated, so the thermochemistry step can warn about frequencies there. Its
tag (id, version and the range used) goes into the frequency fingerprint of the
corrected thermal data, so corrected and uncorrected data are never mixed.
The functions are looked up like the ZPE scale factors, by the (method, basis) of
the frequency level’s model chemistry (seamm_thermochemistry.zpe.split_level()).
- seamm_thermochemistry.frequency_corrections.FREQUENCY_CORRECTIONS = {('PM7', ''): FrequencyCorrection(id='PM7-Rozanska2014', version=1, method='PM7', basis='', branches=(Branch(low=0.0, high=125.0, slope=1.0, intercept=0.0, optional=False), Branch(low=125.0, high=2500.0, slope=1.006, intercept=-27.7, optional=True), Branch(low=2500.0, high=inf, slope=3.99, intercept=-7849.2, optional=False)), citation='Rozanska, Stewart, Ungerer, Leblanc, Freeman, Saxe and Wimmer, J. Chem. Eng. Data 59, 3136 (2014), eq 1', bibtex='@article{Rozanska2014,\n author = {Rozanska, Xavier and Stewart, James J. P. and Ungerer, Philippe and\n Leblanc, Benoit and Freeman, Chris and Saxe, Paul and Wimmer, Erich},\n title = {High-Throughput Calculations of Molecular Properties in the MedeA\n Environment: Accuracy of PM7 in Predicting Vibrational Frequencies,\n Ideal Gas Entropies, Heat Capacities, and Gibbs Free Energies of\n Organic Molecules},\n journal = {Journal of Chemical \\& Engineering Data},\n volume = {59},\n number = {10},\n pages = {3136--3143},\n year = {2014},\n doi = {10.1021/je500201y}\n}', fitted_elements=('C', 'H', 'N', 'O'), unvalidated=((98.0, 125.0), (2400.0, 2600.0)), valid_max=2900.0, note='Fitted to the experimental frequencies of 52 organic molecules (C, H, N, O; experimental frequencies up to about 3700 cm^-1, raw PM7 up to about 2850). The map is discontinuous at 125 cm^-1 (124 -> 124, 126 -> 99.1) and at 2500 cm^-1 (2499 -> 2486.3, 2501 -> 2129.8). The source calls the 125-2500 cm^-1 branch optional (0.6 %). For PM7 only: other MOPAC methods (PM6, PM6-ORG, ...) need their own fit.', aliases=())}#
The corrections, by (method, basis), upper case
- class seamm_thermochemistry.frequency_corrections.FrequencyCorrection(id: str, version: int, method: str, basis: str, branches: tuple, citation: str, bibtex: str = '', fitted_elements: tuple = (), unvalidated: tuple = (), valid_max: float = None, note: str = '', aliases: tuple = ())[source]#
Bases:
objectA piecewise-linear correction of the harmonic frequencies of one method.
Its fields:
- id, versionstr, int
Its name and version; both go into
tag.- method, basisstr
The level it is for, upper case, as
split_level()gives it.- branchestuple of Branch
Covering 0 to infinity in order; frequencies on a boundary use the higher branch.
- citationstr
The source, for printing.
- bibtexstr
The source as a BibTeX entry, for the references.
- fitted_elementstuple of str
The elements of the fit set. Stretches of hydrogen bonded to other elements were not validated.
- unvalidatedtuple of (float, float)
The windows (cm^-1) where the map is discontinuous or was not validated, as raw frequencies.
- valid_maxfloat or None
The largest raw frequency (cm^-1) the fit covers. Higher frequencies are left as they are (a steep branch would blow them up), and the step warns.
- notestr
Anything else to say when it is used.
- aliases: tuple = ()#
- basis: str#
- bibtex: str = ''#
- branches: tuple#
- citation: str#
- fitted_elements: tuple = ()#
- id: str#
- in_unvalidated_windows(frequencies)[source]#
The positive frequencies (cm^-1) in an unvalidated window.
- method: str#
- not_applicable(frequencies)[source]#
The raw frequencies (cm^-1) above the fit’s range, left uncorrected.
- note: str = ''#
- unvalidated: tuple = ()#
- valid_max: float = None#
- version: int#
- seamm_thermochemistry.frequency_corrections.RANGES = ('full', 'high-frequency only')#
all of it, or only the branches that are needed (a branch marked
optionalis then left out)- Type:
How much of a correction to apply
seamm_thermochemistry.geometry_store module#
Geometries and energies in the fragment store: a species’ energy at one level of theory on a geometry from another.
The fragment table holds E, ZPE and H of a species at one level of theory,
or at an energy level and a frequency level, with no geometry. Thermochemistry
often wants the energy at a costlier level (e.g. revDSD-PBEP86 or
CCSD(T)-F12) on the geometry and frequencies of a cheaper one (e.g.
r2SCAN-3c), and the same geometry serves several energy levels. Two tables hold
that:
geometryOne row per conformer of a species at a geometry/frequency level. The key is (species, charge, multiplicity, frequency fingerprint, conformer label). The row holds the coordinates and their hash (
seamm_util.fingerprint.geometry_fingerprint()), the energy, the unscaled ZPE and H(T) at that level, and when known the harmonic frequencies, the number of imaginary modes, the rotational symmetry number and the moments of inertia, so that H(T) at other temperatures (and later S and G) can be recomputed. ZPEs are stored unscaled; factors are applied at use.energyOne row per single-point energy on a geometry, keyed by the geometry’s hash and the calculation’s settings fingerprint. The hash, not the row id, is the key, so copies of the store on other hosts keep working; the row id of the geometry is kept as a convenience. Other results of the same calculation (e.g. the MP2 energy of a CCSD(T) run, or the (T) correction) go in
byproducts.
A composite energy (a recipe of several components) is never stored, only its components; it is combined at use, as ZPE factors are applied at use, so a corrected recipe or factor needs no recomputation.
Guards, applied when rows are written:
An energy whose calculation recorded the geometry it ran on (its geometry fingerprint) must be on the geometry of the row it is attached to; otherwise it is refused.
A geometry row is optimized and has its frequencies at one level: when the optimization’s model chemistry is given it must be the frequency level’s. (The settings fingerprints of the two may differ legitimately, e.g. a Hessian by finite differences converges the SCF more tightly, so the model chemistry and program are compared, and both fingerprints are kept.)
A harvest or write-back refuses a geometry with an imaginary mode beyond
IMAGINARY_TOLERANCE(cm⁻¹; smaller ones are taken as numerical noise). Rows with imaginary modes stored earlier stay, andgeometries()does not return them for reuse unless asked.
Writes follow the fragment table’s rules: insert-or-ignore, a differing
duplicate (by more than 0.1 kJ/mol) logged in row_history and never
overwritten, and an explicit supersede with a reason that keeps the old
values in row_history. A row quarantined by hand (UNUSABLE: <reason>)
is never replaced by a put: the attempt is logged in row_history (kind
“quarantined”), and only an explicit supersede releases it.
Model chemistries are stored as the bare model and the program, e.g.
CMP@R2SCAN-3C and ORCA, however they are given
(split_model_chemistry()): the ORCA sub-steps name a level
CMP@R2SCAN-3C and the Model Chemistry step ORCA:CMP@R2SCAN-3C. Lookups by
model chemistry accept either spelling and also match rows written before the
names were normalized.
The fingerprint arguments of the readers take one fingerprint or several (any
iterable): a code may give a species one of a few fingerprints at the same level
(e.g. ORCA with or without NoCOSX, chosen by the species’ elements), and the
caller passes those it expects.
- class seamm_thermochemistry.geometry_store.EnergyRecord[source]#
Bases:
dictA row of the
energytable.- property key#
- static make(geometry_hash, E, *, energy_fingerprint=None, calculation_geometry=None, model_chemistry=None, program=None, version=None, byproducts=None, job=None, configuration=None, source=None, date=None)[source]#
Build an energy row for the geometry with hash geometry_hash.
calculation_geometry is the geometry fingerprint the calculation recorded, if any; it must equal geometry_hash (the pairing guard). Without an energy fingerprint the row is keyed on the model chemistry and code version and flagged unverified.
- class seamm_thermochemistry.geometry_store.GeometryEnergyMixin[source]#
Bases:
objectThe geometry and energy tables of
FragmentStore(see the module docstring). The store provides_connect,_readonlyandpath.- energy(geometry_hash, *, energy_fingerprint=None, model_chemistry=None, version=None)[source]#
The energy row on a geometry at a level, or None.
As
geometries(): the verified row with the fingerprint (or with any of several, the first in the order given), else an unverified row of the model chemistry (and code version if given).
- geometries(species, *, charge=0, multiplicity=1, frequency_fingerprint=None, model_chemistry=None, version=None, include_imaginary=False)[source]#
The geometry rows of a species at a frequency level, by conformer.
With frequency fingerprints (one, or several the caller expects), the verified rows with any of them; without, or when there are none, the unverified rows of the model chemistry (either spelling, see
split_model_chemistry(); and the code version if given). Rows with imaginary modes beyond the tolerance are left out unless include_imaginary. Any error reading the store gives [] and a log message.
- log_writes(entries, kind='write-back')[source]#
Record what a harvest or a job’s write-back did with each row: entries are dicts with job, species, charge, multiplicity, conformer, table_name, row, outcome and reason (any may be missing).
- put_energy(record)[source]#
Insert an energy row unless its key exists.
The row is linked to the geometry row with the same hash when there is one. A duplicate (the same key and energy within the tolerance) that brings the energy’s parts (
byproducts) when the stored row has none adds them to it: a calculation recomputed because its row lacked the parts a recipe needs then need not be recomputed again. Returns (“inserted” | “duplicate” | “parts added” | “logged”, row id).
- put_geometry(record)[source]#
Insert a geometry row unless its key exists.
Returns (“inserted” | “duplicate” | “logged”, row id), as
FragmentStore.put()does for the fragment table.
- put_geometry_conformer(record, rmsd=None)[source]#
Insert a geometry row, as a new conformer when it is one.
When the species already has geometries at this frequency level, a row whose E + ZPE is lower than all of theirs by more than 0.1 kJ/mol, and whose structure differs from each of theirs (symmetry-aware RMSD above rmsd Å, by default
fragment_store.CONFORMER_RMSD), is stored as the conformerauto:<geometry hash>instead of being only logged against the existing row. Lookups take the lowest conformer. Anything else – the same minimum again, a higher conformer – is put as byput_geometry()(inserted, a duplicate, or logged).Returns (outcome, row id) as
put_geometry().
- species_at(species, *, charge=0, multiplicity=1, frequency_fingerprint=None, frequency_model_chemistry=None, energy_fingerprint=None, energy_model_chemistry=None)[source]#
The species at an energy level on a frequency level’s geometry.
Returns a list, one entry per conformer that has both, of {“geometry”: GeometryRecord, “energy”: EnergyRecord, “E”: kJ/mol, “ZPE”: unscaled kJ/mol, “H - E”: kJ/mol at the geometry’s T}. The energy level may be the frequency level itself (its E is then in the geometry row, and an energy row is used when there is one). Each fingerprint may be one or several (see
geometries()).
- supersede_energy(record, reason, force=False)[source]#
Replace an energy row’s value, keeping the old one in
row_history. Returns (outcome, row id).
- supersede_geometry(record, reason, force=False)[source]#
Replace a geometry row’s values, keeping the old ones in
row_history. A geometry with new coordinates gets a new hash, so its energy rows (keyed on the old hash) no longer attach to it; they stay in the store for the old geometry. Returns (outcome, row id).
- superseded_rows(species=None)[source]#
The superseded values of geometry and energy rows (
row_history), newest first: [{table, row, species, conformer, date, reason, old, new}], old and new each with E (and ZPE, H, model chemistry, job, geometry hash). With species, only its rows; an energy row belongs to the species of the geometry with its geometry hash, before or after the change.
- class seamm_thermochemistry.geometry_store.GeometryRecord[source]#
Bases:
dictA row of the
geometrytable (a dict; JSON columns as Python values).- property key#
- static make(species, symbols, coordinates, *, charge=0, multiplicity=1, conformer='', frequency_fingerprint=None, optimization_fingerprint=None, model_chemistry=None, optimization_model_chemistry=None, program=None, version=None, E=None, ZPE=None, H=None, T=298.15, frequencies=None, symmetry_number=None, moments=None, conformer_provenance=None, atomic_numbers=None, geometry_hash=None, job=None, configuration=None, source=None, date=None)[source]#
Build a geometry row.
Without a frequency fingerprint the row is keyed on the model chemistry and code version and flagged unverified. The geometry hash is computed from the atomic numbers (or symbols) and coordinates unless given.
optimization_model_chemistry, when given, must be the frequency level’s (model_chemistry). The model chemistry is stored bare, with its program inprogram(split_model_chemistry()).
- seamm_thermochemistry.geometry_store.IMAGINARY_TOLERANCE = 20.0#
Imaginary modes at or below this (cm⁻¹, magnitude) are numerical noise
- exception seamm_thermochemistry.geometry_store.PairingError[source]#
Bases:
ValueErrorAn energy that is not on the geometry it would be attached to, or a geometry optimized at one level with frequencies from another.
- seamm_thermochemistry.geometry_store.SCHEMA_VERSION = 4#
4 adds the
writeback_logtable (what each job added or refused), 3 themetatable (the layer: local, official or contribution; an official store’s version), 2 the geometry, energy and row_history tables; 0 or 1 is the fragment table alone.- Type:
The store’s schema version (
PRAGMA user_version)
- seamm_thermochemistry.geometry_store.is_unusable(model_chemistry)[source]#
Whether a row’s model chemistry marks it quarantined (“UNUSABLE: …”).
- seamm_thermochemistry.geometry_store.quarantine_reason(model_chemistry)[source]#
The reason of a quarantined row: its model chemistry after “UNUSABLE:”.
- seamm_thermochemistry.geometry_store.same_model_chemistry(a, b, program_a=None, program_b=None)[source]#
Whether two model chemistries are the same level: the same bare model, and the same program when both are known (case-insensitive).
- seamm_thermochemistry.geometry_store.split_model_chemistry(text, program=None)[source]#
The bare model chemistry and the program of a model chemistry string.
ORCA:CMP@R2SCAN-3Cgives (”CMP@R2SCAN-3C”, “ORCA”); a bareCMP@R2SCAN-3Cgives (”CMP@R2SCAN-3C”, program). Only a prefix before the first@is a program, soORCA:DFT@B3LYP/bse:def2-SVPgives (”DFT@B3LYP/bse:def2-SVP”, “ORCA”), and a string without@(e.g. a fingerprint,sha256:...) is returned as it is. A program given explicitly wins over a different prefix, which is logged.- Parameters:
text (str or None) – The model chemistry.
program (str, optional) – The program, if known otherwise.
- Return type:
(str or None, str or None)
seamm_thermochemistry.hooks module#
The JobServer’s org.molssi.seamm.jobserver.job_finished hook: harvest a
finished job’s CBH species into the host’s fragment store.
The Thermochemistry step writes the species it computes into the fragment store
itself when the store is on a local disk of the machine running the job
(write-back). When it cannot – a job that ran on a cluster, or a store on a
network filesystem – it leaves the marker harvest_fragments.json in the
job’s directory. When the job comes back and the JobServer finalizes it, this
hook harvests such a job into the store on the JobServer’s host, once: the
marker is then renamed harvest_fragments.done.json, with the tallies.
The hook never changes the job or its status, and any error is logged, not raised.
- seamm_thermochemistry.hooks.MARKER = 'harvest_fragments.json'#
The marker the step leaves in the job’s directory
- seamm_thermochemistry.hooks.harvest_finished_job(job_id=None, wdir=None, state=None, logger=<Logger seamm_thermochemistry.hooks (WARNING)>, **kwargs)[source]#
Harvest a finished job’s species into this host’s fragment store if the job asked for it (the marker), whatever its final state: the species of a job’s finished Loop iterations are valid even if a later step failed.
Returns the tallies, or None when there was nothing to do or it failed.
seamm_thermochemistry.import_orca_cli module#
Import one or more ORCA atom-energy results CSVs into thermochemistry.db.
Installed as the seamm-thermochemistry-import-orca console script.
Usage#
seamm-thermochemistry-import-orca results1.csv results2.csv … seamm-thermochemistry-import-orca –dry-run results.csv seamm-thermochemistry-import-orca –force results.csv # overwrite every conflict seamm-thermochemistry-import-orca –prefer-lower-energy results.csv # keep lower
See seamm_thermochemistry.importers.import_orca_atom_results for the CSV shape expected and the vetting/duplicate-handling rules – this module is just the CLI + pretty-printing around that function.
seamm_thermochemistry.importers module#
Importers from the legacy master files into a ThermoDB.
Requires the import extra (pandas, openpyxl) – the core
db/formation modules do not need either.
Three legacy sources exist today, all sharing the same 12-column experimental-reference block (by position; header text drifts slightly between them – “ΔfH°(0)” vs “ΔfH°gas(0)” vs “DfH0(0)” for the same column):
Paul’s master workbook, “Atom Reference Energies and States.xlsx” – the clean experimental block only, one row per element.
“VASP element_energies.xlsx” (sheet “element_energies”) – the same experimental block, plus paired
"<method>@<encut> atom energy"(isolated atom, ref_type=”atom”) and"<method>@<encut>"(per-atom standard-state energy, ref_type=”element_phase”) columns.Each of gaussian_step / psi4_step’s
data/atom_energies.csv– the same experimental block, plus one column per method (isolated atom, kJ/mol) and an optional"<method> correction"column.
A fourth, ongoing source needs no xlsx/openpyxl at all:
import_orca_atom_resultsreads a per-job ORCA atom-energy results CSV (one row per element, oneE DFT@<method>/<basis> (kJ/mol)/S^2 DFT@<method>/<basis>column pair per method+basis run) and vets each cell against its expected spin before adding it – see that function’s docstring.
- seamm_thermochemistry.importers.import_orca_atom_results(db, csv_path, *, code='orca', ref_type='atom', warn_threshold=0.02, reject_threshold=0.2, energy_rel_tol=1e-06, energy_abs_tol=0.01, force=False, prefer_lower_energy=False, dry_run=False)[source]#
Import ORCA atom-energy results from a per-job results CSV.
Expects the shape produced by the atom-energy scan flowchart: one row per element (
Atomic Number,Element,Multiplicity), and one"E DFT@<method>/<basis> (kJ/mol)"/"S^2 DFT@<method>/<basis>"column pair per method+basis combination that has been run so far (blank cells are jobs not finished/reached yet, not errors).methodandbasisare stored separately –methodas the ThermoDBmethod,basisassettings– since ORCA (unlike Gaussian’s composite-method columns) always keeps them as independent axes.Vetting, since a bad SCF root (wrong occupation, not just unconverged) is the actual failure mode seen for hard atoms (lanthanides etc.), not just a missing value:
A computed S^2 is compared to the multiplicity’s expected S(S+1). Within warn_threshold (relative) it’s added silently; beyond that but within reject_threshold it’s added but flagged in “spin_warnings” for manual review; beyond reject_threshold it’s not added at all (“rejected”).
A missing S^2 is fine for a singlet (trivially 0, commonly not reported) but rejected for any open-shell multiplicity – no way to vet it.
If the CSV’s own multiplicity for an element disagrees with what’s already in the element table (when known), that’s noted in “multiplicity_mismatches” but does not block the import – it may just mean the reference table’s own value needs a look.
Duplicate handling, since re-running elements/methods is expected across “hundreds of these”: an existing (element, code, method, ref_type, settings) value that’s already in the database is left alone if the new value is close (“unchanged”); if it differs by more than energy_rel_tol/energy_abs_tol it is a conflict, resolved one of three ways (force and prefer_lower_energy are mutually exclusive – pick one):
Neither set (default): reported in “conflicts”, not overwritten. Safest, but needs a human to look at every conflict.
force=True: always overwritten with the new value, listed in “updated”. Only right when you’re confident the new run is simply better across the board – a blanket “latest wins” that can silently make things worse (see prefer_lower_energy).
prefer_lower_energy=True: for hard atoms (partially-filled d/f shells – lanthanides, some transition metals) two independently converged runs of the same nominal state can land on genuinely different self-consistent solutions, invisible to the S^2 gate since both have the right total spin. The tell is a conflict whose delta is nearly constant across several basis sets for the same element – not a basis-incompleteness effect, a different orbital occupation. There’s no rigorous variational principle for DFT, but “prefer whichever energy is lower” is the standard practical criterion for picking between competing self-consistent solutions of the same state. If the new value is lower it’s written and listed in “updated”; if the existing value is already lower it is kept and listed in “kept_existing” – so a blanket rerun can’t silently make a good stored value worse. Still reported either way, never silent.
- Parameters:
db (ThermoDB)
csv_path (str or Path)
code (str) – Defaults to “orca”; parameterized in case this CSV shape is ever reused for another code.
ref_type (str) – Defaults to “atom” (isolated gas-phase atom energies – the only thing this scan computes).
warn_threshold (float) – Relative S^2 deviation thresholds (see above). Tune per how strict you want the gate; there’s no universally “right” value, the deviations here are your own to judge (e.g. an ~20%-high S^2 on some lanthanides is real but was still accepted on review in this project).
reject_threshold (float) – Relative S^2 deviation thresholds (see above). Tune per how strict you want the gate; there’s no universally “right” value, the deviations here are your own to judge (e.g. an ~20%-high S^2 on some lanthanides is real but was still accepted on review in this project).
energy_rel_tol (float) – math.isclose tolerances for treating a duplicate energy value as “the same result” rather than a conflict.
energy_abs_tol (float) – math.isclose tolerances for treating a duplicate energy value as “the same result” rather than a conflict.
force (bool) – Overwrite every conflicting existing value unconditionally.
prefer_lower_energy (bool) – Resolve conflicts by keeping whichever energy is lower (see above). Mutually exclusive with force.
dry_run (bool) – Classify and report everything, but never call db.add_atom_energy – preview a CSV before committing it.
- Returns:
dict with keys “added”, “updated”, “unchanged”, “conflicts”,
”kept_existing”, “rejected”, “spin_warnings”,
”multiplicity_mismatches” – each a list of small dicts describing
what happened, for the caller to report.
- seamm_thermochemistry.importers.import_reference_xlsx(db, path, sheet='Reference Data', reference_note='JANAF')[source]#
Load the experimental block from Paul’s master workbook.
Returns the list of element symbols imported.
- seamm_thermochemistry.importers.import_vasp_workbook(db, path, sheet='element_energies', reference_note='JANAF', import_elements=True)[source]#
Load the VASP element/atom energy workbook.
Imports the shared experimental block (optional, import_elements) plus every
"<method>@<encut> atom energy"column as ref_type=”atom” and every matching"<method>@<encut>"column as ref_type=”element_phase”, withsettings=f"encut={encut}eV". Both column families are already in kJ/mol in this workbook (checked against its own “Testing” sheet, which carries the raw VASP eV values alongside their kJ/mol conversion) – not eV, despite the encut being in eV.Returns a dict {“elements”: […], “atom_energy”: n, “element_phase”: n}.
- seamm_thermochemistry.importers.import_wide_method_csv(db, path, code, *, methods=None, ref_type='atom', reference_note=None, import_elements=True)[source]#
Load one of the wide per-code CSVs (gaussian_step/psi4_step style).
- Parameters:
code (str) – “gaussian” or “psi4” (or whatever this file belongs to).
methods (list of str, optional) – Restrict to these method columns (by exact header text). If None, import every non-empty method column found – can be thousands of columns for the full Gaussian composite-method grid; pass an explicit list for anything but a one-off full import.
{"elements" (Returns a dict)
seamm_thermochemistry.installer module#
seamm_thermochemistry.ladder module#
Laddered reference values for CBH reactions (CBH-ANL, Elliott et al. JPCA 127, 1512 (2023), eqs. 8-11).
Only the CBH-0 species (H2, CH4, H2O, NH3, HF, …, and the radical CBH-0 species such as CH3 and OH) take their values from ATcT. Every other reference species gets its value from its own CBH reaction one rung below its size, computed at a high level of theory, anchored to the laddered values of that reaction’s species:
L(M) = H(M) - Σ_i c_i H(i) + Σ_i c_i L(i)
where H are the high-level enthalpies (E + scaled ZPE at 0 K, or with the thermal correction at 298.15 K), c_i the coefficients of M’s CBH-(r-1) reaction (r = M’s own rung, the lowest rung at which M is its own fragment) and L(i) the laddered values of the reaction’s other species, recursively down to the ATcT anchors. Because every laddered value is a linear combination of ATcT anchors plus high-level reaction enthalpies, each one carries its coefficients on the ATcT species, so a reaction’s ATcT uncertainty is cᵀΣc with the ATcT covariance exactly, and a method term for the high-level reaction enthalpies is measured by comparing the laddered values with ATcT for every ladder species that ATcT has.
A ladder is built from the fragment-energy store (the high-level energies and
the frequency-level ZPE and thermal corrections, harvested from finished
jobs) and saved as a named reference set, a JSON file in ladders/ next to
the store, with the provenance of every value.
- class seamm_thermochemistry.ladder.Ladder(data)[source]#
Bases:
objectA named set of laddered reference values.
ladder[species]is a dict with the keysrung,H0andH298(kJ/mol, the laddered ΔfH° at 0 K and 298.15 K; one may be None),atct_coefficients({ATcT ID: coefficient}),uncertainty(the 95 % ATcT part),used_covariance,reaction,provenanceand, for species that ATcT has,atct_difference_0K/atct_difference_298K.
- seamm_thermochemistry.ladder.build_ladder(species, *, name, energy_level, frequency_level=None, zpe_scale=0.9863, atct_version=None, atct_base=None, store_path=None, protect='default', fetch_covariance=False, anchors_from_atct=True)[source]#
Build a ladder for species (SMILES; their lower-rung fragments are added as needed) from the fragment store.
- Parameters:
energy_level (str) – The high-level energies: a settings fingerprint, or a model chemistry string (then unverified records are accepted, and reported).
frequency_level (str, optional) – The level of the ZPE and thermal corrections (default: the energy level’s own record).
zpe_scale (float) – Scale factor for the harmonic ZPE (0.9863 for B2PLYP-D3/cc-pVTZ).
seamm_thermochemistry.ladder_cli module#
seamm-thermochemistry-ladder: laddered reference values for CBH
reactions from the fragment store (CBH-ANL):
seamm-thermochemistry-ladder build NAME --energy LEVEL [--frequencies LEVEL]
[--zpe-scale 0.9863] [--atct-version V] [--store FILE] SMILES [SMILES ...]
seamm-thermochemistry-ladder show NAME [--store FILE]
seamm-thermochemistry-ladder list [--store FILE]
LEVEL is a settings fingerprint or a model chemistry string, as stored by
seamm-thermochemistry-fragments harvest.
seamm_thermochemistry.layered_store module#
The fragment store in two layers: the user’s local store and the official one, looked up local first.
fragments.db (the local layer) is the store write-back, harvest, merge and
supersede write to. fragments-official.db (the official layer) is published
on Zenodo, installed and replaced whole by the installer, and only ever read,
without locking (it may sit on a network filesystem). See the P5 design note,
docs/developer_guide/campaigns/2026-11-10, §4.
The rules, for rows of the same key in both layers:
- R1
The local row is used, unless R2 applies.
- R2
The official row has been superseded and the local one equals a value it superseded (within 0.1 kJ/mol): the official row is used, and noted.
- R3
Otherwise, values differing by more than 0.1 kJ/mol are noted as a conflict, and the local row is used.
- R4
Conformers come from both layers; the lowest E + ZPE wins, a tie within 0.1 kJ/mol going to the local layer (
LayeredStore.lowest()). Verified rows in either layer come before unverified ones in either.- R5
Energies pair with geometries only on the same geometry hash: looked up in the geometry’s own layer first, then the other. Every row returned carries its
layer, so a caller can say when a geometry and its energy come from different layers.
Notes from R2 and R3 accumulate in LayeredStore.notes for the caller to
report.
- class seamm_thermochemistry.layered_store.LayeredStore(local_path=None, official_path=None, *, create_local=False)[source]#
Bases:
objectThe local and official fragment stores, read as one (see the module docstring).
- Parameters:
local_path (str or Path, optional) – The local store; by default
fragments.dbnext to the ATcT cache.official_path (str or Path, optional) – The official store; by default
fragments-official.dbbeside it. A missing file means no official layer.create_local (bool) – Open the local store for writing (creating it if missing).
- energy(geometry_hash, *, prefer='local', **kwargs)[source]#
The energy row on a geometry at a level (see
FragmentStore.energy()), from the layer prefer (the geometry’s) first, then the other (R5); with itslayer.
- geometries(species, **kwargs)[source]#
The geometry rows of a species at a frequency level from both layers (see
FragmentStore.geometries()for the arguments), one per key, each with itslayer.
- get(species, **kwargs)[source]#
The best fragment-table record of a species at a level (see
FragmentStore.get()) over both layers, with itslayer.
- static lowest(rows)[source]#
The lowest conformer (E + ZPE) of geometry rows, a tie within 0.1 kJ/mol going to the local layer (R4); None if none has E and ZPE.
- property path#
seamm_thermochemistry.references module#
The supplementary reference library: enthalpies of formation that ATcT lacks, from evaluations, measurements, literature and computed values.
references-official.db is published on Zenodo, installed by the installer
and only ever read. references.db is the local layer: user references and
computed references not yet published. Both sit beside the ATcT cache in
<...>/Parameters/thermochemistry/. The importers that build a library are
maintainer tools; users only install the published file.
The file keeps every value a source gives (measurement), the one chosen
per species and source (reference), and how derived values were derived
(derivation), so every choice can be audited and redone. Uncertainties are
stored as the source states them, with their level; the expanded (95 %)
uncertainty is computed from them by expanded_uncertainty(), so the
policy can change without re-importing anything.
Rules built in (the reference-data pilot, 2026-11-12):
Burcat’s database is never a source: a source named Burcat, or a value citing it, is refused.
No P species from the online JANAF tables (their PH3 and PH2 sit ~17.5 kJ/mol off the printed edition); P species come from the printed edition.
Estimates are stored but never chosen unless accepted, which is recorded with who and when.
A later source replaces an earlier one only when its uncertainty is below a third of the earlier one’s and they agree within the earlier one’s (
later_source_wins()); both values are kept.Every derived value has its derivation, and
ReferenceLibrary.check()recomputes it from the current values of its partners.Ions are stored in ATcT’s stationary-electron convention, converted on entry from the thermal-electron one.
- seamm_thermochemistry.references.ELECTRON_H298 = 6.19739257389175#
the thermal enthalpy of a free electron, the difference between the two ion conventions at 298.15 K per unit of charge
- Type:
(5/2) R T at 298.15 K, kJ/mol
- seamm_thermochemistry.references.EXCLUDED_ELEMENTS = {'JANAF-online': {'P'}}#
Sources whose values for species containing these elements are refused
- class seamm_thermochemistry.references.LayeredReferences(local_path=None, official_path=None)[source]#
Bases:
objectThe local and official reference libraries, read as one.
For each species the values of both layers are merged, best rank first; within a rank the local layer comes first. Every value carries its
layer.- candidates(smiles, charge=0, multiplicity=None, *, include_held=False)[source]#
Every usable value for a species, from both layers (and the held ones too, if asked, never preferred).
- lookup(smiles, charge=0, multiplicity=None)[source]#
The value to use: each layer’s preferred value (overrides applied within the layer), the best-ranked of them, the local layer winning a tie. None if neither layer has the species.
- property versions#
(version, path)} of the libraries read.
- Type:
{layer
- exception seamm_thermochemistry.references.NewerReferenceSchemaError[source]#
Bases:
PermissionErrorThe library’s schema is newer than this code’s, so it is not written.
- seamm_thermochemistry.references.OFFSET_MIN_SHARED = 5#
The fewest species shared with ATcT for a source’s offset to be used
- exception seamm_thermochemistry.references.ReferenceError[source]#
Bases:
ValueErrorA value refused by the library’s rules.
- class seamm_thermochemistry.references.ReferenceLibrary(path, *, create=False, layer='local', immutable=False)[source]#
Bases:
objectOne reference-library file (a layer).
- Parameters:
path (str or Path) – The SQLite file.
create (bool) – Create the file and schema if missing (a writer).
layer (str) – For a new file: “official” or “local”, recorded in
meta.immutable (bool) – Open read-only without locking (the installed official file).
- accept(measurement_id, *, by, reason)[source]#
A person accepts a value the rules would not choose: an estimate, or a species held for its spread. Recorded with who, when and why.
- accept_file(path)[source]#
Apply the decisions kept in a CSV: smiles, charge, source, dfh_298k, by, reason, and optionally action (“accept”, the default, or “hold”, which needs no value). Returns the reference ids of the accepts.
- accept_value(smiles, charge, source, dfh_298k, *, by, reason)[source]#
Accept a source’s value for a species, found by its value at 298.15 K (within 0.01 kJ/mol), so that an accept can be kept in a file and reapplied whenever the library is rebuilt.
- add_calibration(path, *, source_names=None)[source]#
Read calibration values: a source’s values for species ATcT has, used only to measure the source’s offset, never as references.
The CSV has smiles, source, dfH298_kJmol, ATcT_version, ATcT_id, ATcT_dfH298_kJmol and citation. source_names maps the file’s source names to the library’s. Returns the number of rows read.
- add_derivation(measurement_id, reaction, drh_298k, *, drh_uncertainty=None, citation=None, notes=None)[source]#
Record how a value was derived from others: every derived value has one, and so may a value a source derived (e.g. ΔfH from a D0 and the atoms), so that its offset is inherited and it can be checked.
reaction is a list of dicts, one per species, each with its
coefficient(products positive): the derived species has"target": True; a partner givessmiles(withchargeandmultiplicity) and thesourceits value comes from, or"element": Truefor an element in its reference state (ΔfH = 0).
- add_measurement(species_id, source, *, citation, dfh_298k=None, dfh_0k=None, uncertainty=None, unc_level='unstated', status='measured', year=None, h298_h0=None, standard_state=None, ion_convention=None, source_ref=None, method=None, comment=None, retrieved=None, notes=None)[source]#
Add one value a source gives for a species. Returns its id.
- add_source(name, kind, rank, *, citation=None, licence=None, licence_checked=None, retrieved=None, notes=None)[source]#
Add a source, or return the id of the one with this name.
- add_species(smiles, charge=0, multiplicity=1, *, formula=None, name=None, cas=None, inchi=None, inchi_retry=None, notes=None)[source]#
Add a species, or return the id of the existing one.
The SMILES is canonicalized as the CBH code keys species. A given formula must match the SMILES’, and a CAS number already used by a species of another constitution is refused.
- apply_overrides()[source]#
Mark, for every species, which source’s value wins by
later_source_wins(), going down the ranks. Both values stay; the winner records the one it overrides and why.
- best(species_id)[source]#
The value to use for a species: the best-ranked chosen value, unless a later source overrides it. A value at 0 K only (W4-17) is used only when no source gives one at 298.15 K. None if there is none.
- check()[source]#
Check every value with a derivation against its partners’ current values, and that every derived value has a derivation.
Returns a list of problems (dicts with
measurement,smilesandproblem); empty when all is well.
- choose(species_id, source)[source]#
Choose the value of a source for a species by
select_measurement(), and record it. Returns the reference id, or None if nothing can be chosen.
- hold(smiles, charge, source, *, by, reason)[source]#
A person holds a source’s value for a species (not used), with why: e.g. two modern measurements in conflict, awaiting a computed arbitration. Recorded with who and when.
source may name several sources separated by “/” (e.g. “JANAF-1998 / NBS-82”: the two disagree, so neither is used). Each must be a source in the library; a source with no value for the species, or whose values its own file already holds, is passed over. Returns the number of values held here.
- in_atct#
whether ATcT has a species, so a derivation partner it has adds no offset
- Type:
optional callable(smiles, charge, multiplicity) -> bool
- inherited_offset(measurement_id)[source]#
The offset a value derived from others inherits (science, 2026-11-12): the partners’ source offsets propagated through the derivation, sqrt(Σ (ν_i offset_i)²). A partner ATcT has (see
in_atct) is resolved from ATcT and adds nothing. Returns (offset, “from …”) or None when there is no derivation or no partner source has an offset.
- property layer#
- property meta#
- references(species_id, *, include_held=False)[source]#
The chosen values of every source for a species, best rank first, with their expanded uncertainty.
- set_offset(source, offset_kj, n, atct_version, *, full=None, excluded=None)[source]#
Record a source’s offset against ATcT: the rms of (source - ATcT) over the n species both have. Fewer than OFFSET_MIN_SHARED species record no offset (None), only the count. full is (rms, n) over every shared species when some were excluded (a recorded reason).
- species(smiles, charge=0, multiplicity=None)[source]#
The species rows matching a SMILES (any multiplicity if None).
- property version#
- seamm_thermochemistry.references.SCHEMA_VERSION = 1#
The schema version (
PRAGMA user_version) of this code
- seamm_thermochemistry.references.UNSTATED_FLOOR = 4.0#
The floor (95 %) for a measured value given without any uncertainty, kJ/mol
- seamm_thermochemistry.references.compute_offsets(library, atct=None, *, exclude=None)[source]#
Measure each source’s offset against ATcT and record it.
The differences source - ATcT come from two places: the library’s own values for species ATcT also has (with atct), and the calibration values (
ReferenceLibrary.add_calibration()), which are used for nothing else. A source’s offset is the rms of its differences over at least OFFSET_MIN_SHARED species (science, 2026-11-12), else none. It is added in quadrature to the source’s uncertainties.Caveat (science): the calibration species are well studied, so the offset is a lower bound for the obscure species the library serves. It is not a measure of their reliability; the stated uncertainties and the spread rule are.
exclude is {source: (set of SMILES, reason)}: species left out of the offset for a recorded reason (e.g. CODATA key values known to be superseded); the offset over every species is kept beside it.
Returns {source: (rms, n, [(smiles, diff)])}, over the species used.
- seamm_thermochemistry.references.elements(smiles)[source]#
The set of element symbols in a SMILES (with implicit H).
- seamm_thermochemistry.references.expanded_uncertainty(uncertainty, unc_level, status='measured')[source]#
The 95 % (k = 2) uncertainty from a stated one and its level.
Science, 2026-11-12: 95 % and 2σ as stated; 1σ and unstated levels doubled; a measured value with no uncertainty gets a 4 kJ/mol floor; an estimate without one has none (None). “mixed” (W4-17: atoms at 2σ plus twice the method’s 1σ) is already at 95 %.
- seamm_thermochemistry.references.hill_formula(smiles)[source]#
The Hill formula of a SMILES, without its charge.
- seamm_thermochemistry.references.later_source_wins(earlier, later)[source]#
Whether a later source’s value replaces an earlier one’s.
Each argument is (value, expanded uncertainty). The later one wins only when its uncertainty is below a third of the earlier one’s and the two agree within the earlier one’s uncertainty.
- seamm_thermochemistry.references.local_references_path(path=None)[source]#
The local layer,
references.db.
- seamm_thermochemistry.references.official_references_path(path=None)[source]#
The published library,
references-official.db.
- seamm_thermochemistry.references.references_directory(base=None)[source]#
The directory of the reference libraries: beside the ATcT cache.
- seamm_thermochemistry.references.select_measurement(measurements, choosable=('measured', 'review', 'derived', 'user'))[source]#
Choose one value among a source’s measurements of one species.
measurements are dicts with at least
dfh_298k(ordfh_0kwhen that is all a source gives),uncertainty,status,yearandid. The rule (thermo’s pilot, science 2026-11-12): a review value; else the most recent with a stated uncertainty (ties to the smaller uncertainty); else the most recent. Values whosemethodis “N/A” (derived from another value) come last. Only values whose status is in choosable are chosen: never estimates or held values, and computed ones only from a computed source. The spread flag is raised when another measurement with an uncertainty differs from the chosen one by more than their combined uncertainties; the species is held when that exceeds three times the combined uncertainties and there is no review value.Returns (chosen, rule, spread, flag, state), or None if nothing can be chosen. state is “chosen” or “held”.
seamm_thermochemistry.references_cli module#
seamm-thermochemistry-references: the supplementary reference library
from the command line:
seamm-thermochemistry-references sources [--library FILE]
seamm-thermochemistry-references list [--library FILE]
seamm-thermochemistry-references show SMILES [--charge Q] [--multiplicity M]
[--local FILE] [--official FILE]
seamm-thermochemistry-references check [--library FILE]
seamm-thermochemistry-references accept MEASUREMENT --by WHO --reason TEXT
[--library FILE]
seamm-thermochemistry-references accept-file ACCEPTS.csv --library FILE
seamm-thermochemistry-references add-curated DIR --library FILE
seamm-thermochemistry-references webbook fetch SPECIES.csv --cache DIR
seamm-thermochemistry-references webbook import --cache DIR --library FILE
seamm-thermochemistry-references offsets --library FILE [--atct-version V]
[--calibration FILE]
[--exclude SOURCE:SMILES,...:REASON]
add-curated (maintainers) reads hand-curated CSVs (printed JANAF, CODATA,
NBS-82, D0, W4-17, derived values) into a library file being built for
publication; it always names its file. webbook fetch saves NIST WebBook
pages for the species in a CSV (smiles, charge, multiplicity) into a cache, a
few seconds apart and logged; webbook import reads the cache into a
library, offline. offsets measures each source against ATcT (rms over
shared species).
--library is one library file, by default the local layer
(references.db); show reads both layers, local and official
(references-official.db). accept releases an estimate or a held
species, recording who and why.
seamm_thermochemistry.report module#
A detailed, citable text report of a formation-energy calculation.
Every code that reports a formation-referenced energy used to build (and
duplicate) its own ASCII table of “here is exactly what atom energies went
into this number” – see e.g. the ~200-line calculate_enthalpy_of_formation
this package’s formation.py replaced the arithmetic of. This module is
the shared replacement for the reporting half: one place that turns a
composition + a ThermoDB + a set of system energies into the explicit,
per-atom breakdown table plus references, so a chemist can see exactly which
database, which atomic energy numbers, and which experimental citations
produced the headline number – not just trust it.
- seamm_thermochemistry.report.format_report(composition, db, code, method, *, ref_type='atom', settings='', units='kJ/mol', name=None, level_label=None, system_energy=None, system_enthalpy=None, system_gibbs_energy=None, temperature=None)[source]#
Build the detailed formation-energy report text.
- Parameters:
composition – As in atomization_energy.
db – As in atomization_energy.
code – As in atomization_energy.
method – As in atomization_energy.
ref_type – As in atomization_energy.
settings – As in atomization_energy.
units – As in atomization_energy.
name (str, optional) – A human-readable name for the system (falls back to a generic label).
level_label (str, optional) – A human-readable level of theory (falls back to
f"{code}/{method}").system_energy (float, optional) – The system’s electronic energy (0 K, ZPE-free). If given, the Atomization Energy and Energy-of-Formation (DfE0) sections are included.
system_enthalpy (float, optional) – The system’s total enthalpy H(T). If given (together with temperature), the Enthalpy-of-Formation (DfH(T)) section is included.
system_gibbs_energy (float, optional) – The system’s total Gibbs free energy G(T). If given (together with temperature), the Gibbs-Energy-of-Formation (DfG(T)) section is included.
temperature (float, optional) – Temperature, in K, for the DfH(T)/DfG(T) sections.
- Returns:
The formatted report. Never raises: any missing reference data is reported as a plain-text note in the relevant section instead of stopping the whole report (a molecule that is missing, say, the standard-state entropy for one of its elements should still get an atomization-energy and DfE0 section).
- Return type:
str
seamm_thermochemistry.resolver module#
Resolving the reference species of CBH reactions: ATcT first, then the supplementary reference library.
The order (the reference-library design, §2, and the 2026-11-12 decisions):
ATcT, the version installed. If every reference is in ATcT, nothing else is used and the result is “ATcT-only”.
The user’s own references (the local layer’s
usersource), when the job enables them.The library’s sources by rank: printed JANAF, derived values, CODATA, the NBS tables of 1982, D0, W4-17 (0 K only, used only without a 298.15 K value), the WebBook measurements.
Computed references, when enabled.
One source per element. For each element that ATcT does not have (its gas-phase atom is not in ATcT), the references containing it are taken from the first source, in rank order, that has all of them, so that the source’s shared error cancels in the reaction. A later source still replaces that source’s value for a species where the replacement rule says so (science, 2026-11-12): the element then has several sources, the result is labelled “mixed: several sources” with the sources named, and a one-line note says so. It never blocks. When no single source has them all, each reference takes its own best value, with the same label.
The values come back in the layout of an ATcT entry (dfH0_0K,
dfH0_298K, uncertainty_298K at 95 %, id), with
atct_coefficients empty (they have no covariance with ATcT) and the
library’s details (source, layer, citation, …).
- seamm_thermochemistry.resolver.CODATA_H298_H0 = {'Al': (4.54, 1), 'B': (1.222, 1), 'Be': (1.95, 1), 'Br': (24.52, 2), 'C': (1.05, 1), 'Ca': (5.736, 1), 'Cl': (9.181, 2), 'F': (8.825, 2), 'Ge': (4.636, 1), 'H': (8.468, 2), 'K': (7.088, 1), 'Li': (4.632, 1), 'Mg': (4.998, 1), 'N': (8.67, 2), 'Na': (6.46, 1), 'O': (8.68, 2), 'P': (5.36, 1), 'S': (4.412, 1), 'Si': (3.217, 1)}#
H298 - H0 of the elements’ standard states, kJ/mol per formula unit of the standard state, from CODATA Key Values (Cox, Wagman & Medvedev 1989)
- seamm_thermochemistry.resolver.LABELS = ('ATcT-only', 'mixed', 'mixed: several sources', 'computed references', 'user references')#
The result labels, in increasing order of importance
- class seamm_thermochemistry.resolver.ReferenceResolver(atct, *, supplementary='compilations and computed', use_user=False, layers=None, multiplicity=None)[source]#
Bases:
objectResolve CBH reference species from ATcT and the reference library.
- Parameters:
atct (ATcT) – The ATcT tables.
supplementary (str) – “none” (ATcT only, today’s behaviour), “compilations”, or “compilations and computed” (the default, Paul’s decision 4).
use_user (bool) – Use the user’s own references (the local layer’s
userkind).layers (LayeredReferences, optional) – The libraries; by default the installed ones.
multiplicity (callable, optional) – multiplicity(smiles) for a reference species (the CBH convention).
- resolve(species)[source]#
Resolve a set of reference species (canonical SMILES).
Returns (entries, missing, label, notes): entries {smiles: entry}, ATcT entries carrying
atct_coefficients{id: 1} andsource“ATcT”; missing {smiles: reason}; the result label (LABELS); and one-line notes about mixed sources, flags and conversions.
- seamm_thermochemistry.resolver.SUPPLEMENTARY = ('none', 'compilations', 'compilations and computed')#
The settings of “Supplementary references”
- seamm_thermochemistry.resolver.dfh_0k_from_298k(dfh_298k, h298_h0_species, formula)[source]#
ΔfH(0 K) from ΔfH(298.15 K), the species’ own H298 - H0 and the elements’ (standard states, per atom):
ΔfH(0) = ΔfH(298) - [H298 - H0](species) + Σ n_E [H298 - H0](E)
seamm_thermochemistry.smiles module#
SMILES that RDKit refuses but chemistry needs: hypervalent atoms.
RDKit’s valence check allows a halogen one bond, so ClF3 (FCl(F)F) or BrF5
cannot be read with Chem.MolFromSmiles, bracketed (F[Cl](F)F) or not.
mol_from_smiles() reads them with every sanitization step except the
valence check, so such an atom keeps the bonds it is written with and gets no
implicit hydrogens beyond them; RDKit writes them back unbracketed, which this
function and Open Babel both read correctly.
- seamm_thermochemistry.smiles.NORMAL_VALENCE = {15: 3, 16: 2, 17: 1, 33: 3, 34: 2, 35: 1, 52: 2, 53: 1}#
The lowest (normal) valence of the main-group elements whose hypervalent compounds are common: sulfur, selenium and tellurium (2), phosphorus and arsenic (3), and the halogens chlorine, bromine and iodine (1).
- seamm_thermochemistry.smiles.is_hypervalent(atom)[source]#
Whether an RDKit atom is a neutral or anionic S, Se, Te, P, As, Cl, Br or I with more bonds than its normal valence (SF4, SF6, sulfonyl, phosphoryl, ClF3, PF6-, …).
- seamm_thermochemistry.smiles.mol_from_smiles(smiles)[source]#
An RDKit molecule from a SMILES, or None if it cannot be read.
A SMILES that RDKit reads as it is gives the same molecule as
Chem.MolFromSmiles. One that fails only RDKit’s valence check on an element that is hypervalent in real compounds (S, Se, Te, P, As, Cl, Br, I: ClF3, BrF5) is read without that check; aromaticity, conjugation and hybridization are still perceived. Any other over-valent atom (O=[N]=O) still makes the SMILES unreadable.
seamm_thermochemistry.store_tools module#
Tools for the two-layer fragment store (see layered_store):
migrate_store()brings an older store up to date (themetatable, the model chemistries stored bare with their program), after a dated backup;export_local()writes the local rows that the official store lacks, or has with other values, to a contribution file for the next official version;merge_stores()adds the rows of other stores (copies from other installations, contributions, campaign stores) to a store;build_official()builds a new official store from the previous one and contributions;layer_conflicts()lists the keys whose values differ between the local and the official store.
All writes are insert-or-ignore with provenance: a row whose key is already there with values differing by more than 0.1 kJ/mol is logged, never overwritten, and superseded values travel as history.
- seamm_thermochemistry.store_tools.build_official(out, version, sources, *, previous=None, source=None, frequency_fingerprints=None, energy_fingerprints=None, exclude=None, atct=None, check=True, job_ids_only=True)[source]#
Build an official store: the previous official one (if given) plus the contributions sources, marked official with version (and source, e.g. its Zenodo DOI), compacted, with a
.sha256file and a.report.jsonbeside it.frequency_fingerprints and energy_fingerprints, when given, keep only the rows reachable at those levels (e.g. the presets’ r2SCAN-3c geometries and the revDSD energies on them). exclude ({(species, charge, multiplicity): reason}, or with a fourth element, a conformer label, to leave out one conformer; see
load_exclusions()) leaves rows out, with the reason in the report.With job_ids_only (the default: an official store is published) the provenance keeps each job’s id and drops its directory (see
job_id_only()), in every row and in the history; the full paths stay in the stores the rows came from.Nothing is written when a contribution’s values differ from a row already there (resolve it with
supersedeand a reason first), or when the sanity pass (sanity_check(); atct for the multiplicities) finds a problem in a species state that is not excluded. Returns (path, sha256, counts).
- seamm_thermochemistry.store_tools.export_local(out, local=None, official=None, *, label=None)[source]#
Write the local store’s rows that the official store lacks, or has with values differing by more than 0.1 kJ/mol (flagged in the history as “differs from official”, with the official values), to the contribution file out, with their provenance and superseded values. label is free text recorded in the file; nothing about the host or user is added.
Returns {table: n rows exported}, plus “differing”: n.
- seamm_thermochemistry.store_tools.ground_state_multiplicity(species, charge=0, atct=None)[source]#
(multiplicity, source) of a species’ ground state where it is known, or (None, None).
Singlet for the species of
cbh.SINGLET_GROUND_STATES(silylenes, dihalocarbenes and BH, which a SMILES writes as triplets), triplet for those ofcbh.TRIPLET_GROUND_STATES(O2, S2, SO, OCCO, cyclic C3, which a SMILES writes closed-shell); else from ATcT when it lists the species in labelled spin states: the plain entry is the ground state, and the labelled entry with the same enthalpy of formation names its multiplicity (CH2: plain = triplet; SiH2: plain = singlet). Unlikecbh.ground_state_multiplicity()this does not fall back on the radical count, which is not knowledge of the ground state (O2 would be a singlet).
- seamm_thermochemistry.store_tools.job_id_only(job)[source]#
A job’s provenance without its directory: “5689:/D3/…/Job_005689” gives “5689”; a directory alone gives its last component (“Job_005689”); anything else is returned as it is.
- seamm_thermochemistry.store_tools.layer_conflicts(local=None, official=None)[source]#
The keys in both the local and the official store whose values differ by more than 0.1 kJ/mol: [{“table”, “key”, “local”, “official”, “difference”, “superseded”}], “superseded” when the local value is one the official store superseded (the official value is then used).
- seamm_thermochemistry.store_tools.load_exclusions(path)[source]#
Exclusions from a CSV file with the columns species, charge, multiplicity, reason and, optionally, conformer (to leave out one conformer instead of the whole species state): {(canonical species, charge, multiplicity, conformer or None): reason}.
- seamm_thermochemistry.store_tools.merge_stores(target, sources, *, label=None, allow_official=False)[source]#
Add the rows of sources (paths or stores) to target (a path or a writable store): insert-or-ignore, differing duplicates logged, superseded values copied as history. An official target is refused unless allow_official (for
build_official()).Returns {table: {outcome: n}} plus “history”: n.
- seamm_thermochemistry.store_tools.migrate_store(path=None, *, backup=True)[source]#
Bring a store up to date: the
metaandwriteback_logtables (schema 4), and the model chemistries stored bare with the program filled in (ORCA:CMP@R2SCAN-3C->CMP@R2SCAN-3C+ORCA). A dated copy of the file is kept first. Running it again changes nothing (and makes no copy).Keys do not change: verified rows are keyed by fingerprints, and the keys of unverified rows keep the prefixed spelling. Returns {“backup”: path or None, “changed”: {table: n}, “schema”: (old, new)}.
- seamm_thermochemistry.store_tools.sanity_check(store, *, atct=None)[source]#
The problems that keep rows out of an official store, by species state: {(species, charge, multiplicity): [reason, …]}.
a geometry with an imaginary mode beyond 20i (a saddle point);
a geometry whose bonds, perceived again from its coordinates, are not its species’, whose charge or multiplicity do not fit its SMILES, or with atoms too close together (
fragment_store.check_structure());a multiplicity that is not the species’ ground state, where that is known (
ground_state_multiplicity()).
Ions and open-shell species are checked like the rest, not excluded.
seamm_thermochemistry.webbook module#
The NIST Chemistry WebBook (SRD 69) as a source of the reference library.
A maintainer tool, in two stages so that the network part is separate and can be repeated:
fetchLooks each species up by InChI (gas-phase thermochemistry, SI units), saves the page in a cache directory, waits between requests and logs each one (URL, date, sha256). A stereo InChI that the WebBook does not find is retried without its stereo layers, and the retry is recorded.
importOffline and deterministic: parses every gas-phase enthalpy of formation on the cached pages into the library, with the original citation the WebBook gives, then chooses one value per species by
references.select_measurement().
Rows the WebBook marks as evaluations (class “cal”, e.g. JANAF reviews) are kept but held: the value is taken from the evaluation’s own source.
- seamm_thermochemistry.webbook.INDEX = 'index.csv'#
one row per page
- Type:
The index of a cache directory
- seamm_thermochemistry.webbook.fetch(species, directory, *, delay=3.0, user_agent='seamm-thermochemistry reference-library builder', opener=None)[source]#
Fetch WebBook pages into a cache directory.
species is a list of (smiles, charge, multiplicity). Pages already in the cache are not fetched again. Returns the index rows written.
- seamm_thermochemistry.webbook.import_webbook(library, directory)[source]#
Read the cached WebBook pages listed in a cache’s index into library.
Returns a list of (smiles, outcome, detail): outcome is “added” (with the number of values), “no page”, “no gas-phase value” or “refused”.
- seamm_thermochemistry.webbook.non_stereo_inchi(inchi)[source]#
An InChI without its stereo layers (/t, /m, /s).
- seamm_thermochemistry.webbook.parse_page(text)[source]#
The gas-phase enthalpies of formation on a WebBook page.
Returns {“title”, “found”, “cas”, “formula”, “rows”}, each row a dict with
class(exp or cal),value,uncertainty,units,method,reference(short),citation(full),yearandcomment.
seamm_thermochemistry.zpe module#
Scale factors for harmonic zero-point energies, with their sources.
A harmonic ZPE is too large; scaled by a factor fitted for the level of theory
it comes close to the anharmonic one. The factor belongs to the geometry and
frequency level (the method and basis of the Hessian), so it is looked up from
that level’s model chemistry, e.g. ORCA:CMP@R2SCAN-3C or
ORCA:DFT@PBE0-D4/def2-TZVP. Each entry carries its source, and a flag when
it is used outside what it was fitted for (another basis, or larger molecules
than the fit set); the thermochemistry step prints both and stores them with its
results.
The store keeps ZPEs unscaled; the factor is applied when the values are used, so a corrected factor needs no recomputation.
- class seamm_thermochemistry.zpe.ZPEFactor(factor: float, source: str, flag: str = '', method: str = '', basis: str = '')[source]#
Bases:
objectA ZPE scale factor for a level of theory: the factor, its source, a caution about its use (e.g. a basis set other than the fitted one) or “”, and the method and basis it is for (upper case; basis “” for a composite method).
- basis: str = ''#
- factor: float#
- flag: str = ''#
- method: str = ''#
- source: str#
- seamm_thermochemistry.zpe.ZPE_FACTORS = {('B2PLYP', 'CC-PVTZ'): ZPEFactor(factor=0.9863, source="the CBH-ANL protocol's factor for B2PLYP-D3/cc-pVTZ harmonic ZPEs", flag='', method='B2PLYP', basis='CC-PVTZ'), ('PBE0', 'DEF2-TZVP'): ZPEFactor(factor=0.975, source="Truhlar's database of frequency scale factors, version 5, PBE0/MG3S", flag='Truhlar PBE0/MG3S value, basis-mismatched (used for def2-TZVP)', method='PBE0', basis='DEF2-TZVP'), ('R2SCAN-3C', ''): ZPEFactor(factor=0.9899, source='fitted to the anharmonic ZPVEs of 22 W4-2006 molecules (SEAMM, 2026-11)', flag='fitted on molecules of up to 6 atoms; an extrapolation for larger ones', method='R2SCAN-3C', basis='')}#
The factors, by (method, basis), both upper case with the dispersion correction left out of the method (it hardly changes the frequencies).
- seamm_thermochemistry.zpe.split_level(model_chemistry)[source]#
(method, basis) of a model chemistry string, upper case, with the program and type prefix, a
bse:source and the dispersion suffix of the method removed; basis “” for a composite method.>>> split_level("ORCA:DFT@PBE0-D4/bse:def2-TZVP") ('PBE0', 'DEF2-TZVP') >>> split_level("ORCA:CMP@R2SCAN-3C") ('R2SCAN-3C', '')
Module contents#
Shared atomic reference-energy database and formation-energy arithmetic for SEAMM.
See ~/Sites/reference-energy/2026-07-24_reference-energy/ for the design
rationale. In short: a raw total energy from Gaussian/ORCA/Psi4/VASP has an
arbitrary, code-dependent zero that means nothing to a non-expert user and
cannot be compared across codes. This package holds the one shared table of
atomic reference energies, and the arithmetic to turn a system’s electronic
energy into a physically meaningful energy or enthalpy of formation.
- class seamm_thermochemistry.ATcT(version=None, base=None)[source]#
Bases:
objectOne version of the ATcT tables, from the local cache.
- Parameters:
version (str, optional) – The TN version, e.g. “1.220”. The newest cached version by default.
base (path-like, optional) – The cache directory; see atct_directory.
- candidates(smiles, *, charge=None, gas_only=True)[source]#
All entries whose canonical SMILES matches, as ATcTEntry objects.
If smiles carries no double-bond or tetrahedral stereo but matching entries do, those entries are returned too (they come from the stereo-free index), so that the caller can see them.
- property citation#
- covariance(ids, *, fetch=True, user_agent='SEAMM-thermochemistry/2026.11.13.3 (+https://molssi-seamm.github.io; contact seamm@molssi.org)')[source]#
The covariance matrix (kJ²/mol², 95 % scale, 298.15 K) for the given ATcT IDs, in the order given.
A cached block that contains every ID is used; otherwise the block is fetched from
/covariance/species/(one request for all IDs), checked against the listed uncertainties, and cached.Returns None if the block is not cached and fetch is False or the request fails; callers then fall back to independent uncertainties.
- property covariance_cache#
- fetch_covariance(ids, *, user_agent='SEAMM-thermochemistry/2026.11.13.3 (+https://molssi-seamm.github.io; contact seamm@molssi.org)')[source]#
Fetch and cache the covariance block for ids.
- import_covariance(source)[source]#
Add a covariance block saved from a browser, e.g. the response of
/api/v1/covariance/species/?atctids=...saved to a file. The block is checked like a fetched one; a block already cached with exactly the same IDs is replaced.Returns the block.
- property index#
[ids]}, “flat”: {SMILES without stereo: [ids]}, “inchikey”: {key: [ids]}, “unparsed”: [ids]}, built with RDKit on first use and kept in
index.json.- Type:
{“stereo”
- Type:
{canonical SMILES
- lookup(smiles=None, *, atct_id=None, charge=0, multiplicity=None, conformer=None)[source]#
The ATcT entry for a molecule, by the selection rules.
- Parameters:
smiles (str) – The molecule, with double-bond stereo if it has any.
atct_id (str, optional) – An explicit ATcT ID, which overrides every rule.
charge (int) – The net charge; ATcT lists ions separately.
multiplicity (int, optional) – The spin multiplicity. A labelled spin-state entry is used when it matches; otherwise the plain entry, assumed to be the ground state.
conformer (str, optional) – A conformer or isomer label to select (
syn,anti,cis,trans, …) instead of the plain entry.
- Return type:
- Raises:
- parse_covariance(data)[source]#
The API’s covariance response as {“ids”: […], “matrix”: [[…]]}.
The rows come back in the API’s own order (
species_atctids), not the request order. The square root of each diagonal element must agree with the listed 298.15 K uncertainty, which confirms that the matrix is on the 95 % scale; a mismatch is an error. ATcT lists the uncertainties rounded up to 0.01 kJ/mol (0.1118 is listed as 0.12), so the listed value may exceed the diagonal by up to 0.01.
- class seamm_thermochemistry.ATcTEntry[source]#
Bases:
dictA normalized ATcT entry (a dict) with a few conveniences.
- property casrn_group#
conformers and states of one species share it (
74-82-8*0,74-82-8*1…).- Type:
The CAS number part of the ID
- property heavy_atoms#
The number of atoms other than hydrogen, from the formula.
- property index#
0 is the plain entry.
- Type:
The
*nindex of the ID
- property is_gas#
- property is_nuclear_spin_isomer#
ortho/para entries of small species, also when combined with a spin state (“para singlet” formaldehyde). On a bigger species “ortho” and “para” name positional isomers (o-tolualdehyde), which are wanted.
- property is_plain#
A plain entry, no conformer, state or isomer label.
- property multiplicity#
The spin multiplicity named in the descriptor, or None.
- exception seamm_thermochemistry.ATcTError[source]#
Bases:
ExceptionBase class for errors from the ATcT cache.
- exception seamm_thermochemistry.AmbiguousEntry(message, candidates)[source]#
Bases:
ATcTErrorMore than one ATcT entry matches; the candidates are listed.
- class seamm_thermochemistry.EnergyRecord[source]#
Bases:
dictA row of the
energytable.- property key#
- static make(geometry_hash, E, *, energy_fingerprint=None, calculation_geometry=None, model_chemistry=None, program=None, version=None, byproducts=None, job=None, configuration=None, source=None, date=None)[source]#
Build an energy row for the geometry with hash geometry_hash.
calculation_geometry is the geometry fingerprint the calculation recorded, if any; it must equal geometry_hash (the pairing guard). Without an energy fingerprint the row is keyed on the model chemistry and code version and flagged unverified.
- class seamm_thermochemistry.FragmentRecord[source]#
Bases:
dictOne record of the store, a dict with the columns of
fragmentandverifiedas a bool.- property key#
- static make(species, *, charge=0, multiplicity=1, E=None, ZPE=None, H=None, T=298.15, energy_fingerprint=None, frequency_fingerprint=None, model_chemistry=None, frequency_model_chemistry=None, program=None, version=None, geometry_hash=None, job=None, configuration=None, source=None, date=None)[source]#
Build a record. Without an energy fingerprint the record is keyed on the model chemistry and code version and flagged unverified. The model chemistries are stored bare, with the program in
program(split_model_chemistry()).
- class seamm_thermochemistry.FragmentStore(path=None, *, create=False, immutable=False, layer='local')[source]#
Bases:
GeometryEnergyMixinThe fragment-energy store (see the module docstring), with the geometry and energy tables of
geometry_store.- Parameters:
path (str or Path, optional) – The SQLite file; default next to the ATcT cache.
create (bool) – Create the file and schema if missing (the writer); a reader passes False and gets an empty store when there is no file.
immutable (bool) – Open a file that nothing will change while it is open (the official store) read-only without locking (SQLite’s
immutable=1), which is safe on network filesystems such as GPFS.layer (str) – For a new file (with create): its layer, “local” (the default), “official” or “contribution”, recorded in its
metatable.
- get(species, *, charge=0, multiplicity=1, energy_fingerprint=None, frequency_fingerprint=None, model_chemistry=None, version=None)[source]#
The best record for a species at a level, or None.
With fingerprints, the verified record with those fingerprints (the frequency fingerprint may be None to accept any, including none; each may be one or several, see
geometries()). Without, or when there is no verified record, an unverified record of the same model chemistry (either spelling, seesplit_model_chemistry(); and code version if given); the caller seesrecord["verified"]and reports it.A record with thermal data (H) comes before one with the energy alone (stored by a run without thermal corrections, e.g. MOPAC’s enthalpy of formation taken as the enthalpy), so either kind of run finds what it needs when both are stored for a level.
Any error reading the store gives None and a log message.
- property layer#
“local”, “official” or “contribution”; a store from before the meta table is a local one.
- put(record)[source]#
Insert a record unless its key exists.
Returns “inserted”, “duplicate” (same key, same values), “logged” (same key, values differing by more than 0.1 kJ/mol: the attempt went to
duplicate_log, the stored values stay) or “quarantined” (the key is held by a record quarantined by hand,UNUSABLE: <reason>: it stays, and the attempt goes torow_history; onlysupersede(), a person’s decision with a reason, replaces it).
- release(candidate, reason)[source]#
Release a quarantined row: replace it with the values of a release candidate (an id from
release_candidates()), keeping the quarantined values as superseded, with reason (a person’s).Returns “superseded”; raises ValueError if candidate is not a candidate (not logged as one, or its row no longer quarantined).
- release_candidates(species=None)[source]#
New values that met a row quarantined by hand and were kept out.
A harvest or write-back that brings a row for a key held by a quarantined row logs it in
row_history(kind “quarantined”) after it passed every check of the harvest. Each such attempt whose row is still quarantined is a candidate forrelease(). Returns, oldest first, [{id, table, row, species, charge, multiplicity, conformer, date, job, quarantine, old, new}], old and new each with E (and ZPE, H) and the model chemistry; species keeps only its rows.
- supersede(record, reason, force=False)[source]#
Replace the stored values for the record’s key, keeping the old ones.
The old values go to the
supersededtable with today’s date and reason; the record keeps its key and id. A key not yet in the store is simply inserted. A value the new record lacks (None) keeps the stored one, so a corrected energy does not erase a stored ZPE or H.With force, values that differ by less than the duplicate tolerance (0.1 kJ/mol) are replaced too, so that a correction applied to a whole level of theory reaches every record, however small; identical values are still left alone.
Returns “inserted”, “unchanged” (same values within 0.1 kJ/mol, or identical with force: nothing to replace) or “superseded”.
- class seamm_thermochemistry.GeometryRecord[source]#
Bases:
dictA row of the
geometrytable (a dict; JSON columns as Python values).- property key#
- static make(species, symbols, coordinates, *, charge=0, multiplicity=1, conformer='', frequency_fingerprint=None, optimization_fingerprint=None, model_chemistry=None, optimization_model_chemistry=None, program=None, version=None, E=None, ZPE=None, H=None, T=298.15, frequencies=None, symmetry_number=None, moments=None, conformer_provenance=None, atomic_numbers=None, geometry_hash=None, job=None, configuration=None, source=None, date=None)[source]#
Build a geometry row.
Without a frequency fingerprint the row is keyed on the model chemistry and code version and flagged unverified. The geometry hash is computed from the atomic numbers (or symbols) and coordinates unless given.
optimization_model_chemistry, when given, must be the frequency level’s (model_chemistry). The model chemistry is stored bare, with its program inprogram(split_model_chemistry()).
- class seamm_thermochemistry.Ladder(data)[source]#
Bases:
objectA named set of laddered reference values.
ladder[species]is a dict with the keysrung,H0andH298(kJ/mol, the laddered ΔfH° at 0 K and 298.15 K; one may be None),atct_coefficients({ATcT ID: coefficient}),uncertainty(the 95 % ATcT part),used_covariance,reaction,provenanceand, for species that ATcT has,atct_difference_0K/atct_difference_298K.
- class seamm_thermochemistry.LayeredStore(local_path=None, official_path=None, *, create_local=False)[source]#
Bases:
objectThe local and official fragment stores, read as one (see the module docstring).
- Parameters:
local_path (str or Path, optional) – The local store; by default
fragments.dbnext to the ATcT cache.official_path (str or Path, optional) – The official store; by default
fragments-official.dbbeside it. A missing file means no official layer.create_local (bool) – Open the local store for writing (creating it if missing).
- energy(geometry_hash, *, prefer='local', **kwargs)[source]#
The energy row on a geometry at a level (see
FragmentStore.energy()), from the layer prefer (the geometry’s) first, then the other (R5); with itslayer.
- geometries(species, **kwargs)[source]#
The geometry rows of a species at a frequency level from both layers (see
FragmentStore.geometries()for the arguments), one per key, each with itslayer.
- get(species, **kwargs)[source]#
The best fragment-table record of a species at a level (see
FragmentStore.get()) over both layers, with itslayer.
- static lowest(rows)[source]#
The lowest conformer (E + ZPE) of geometry rows, a tie within 0.1 kJ/mol going to the local layer (R4); None if none has E and ZPE.
- property path#
- exception seamm_thermochemistry.MissingEntry[source]#
Bases:
ATcTError,KeyErrorNo ATcT entry matches.
- exception seamm_thermochemistry.MissingReferenceData[source]#
Bases:
KeyErrorRaised when a composition needs reference data the ThermoDB doesn’t have.
- exception seamm_thermochemistry.PairingError[source]#
Bases:
ValueErrorAn energy that is not on the geometry it would be attached to, or a geometry optimized at one level with frequencies from another.
- class seamm_thermochemistry.ThermoDB(path=None, read_only=False)[source]#
Bases:
objectHelper around the shared atomic reference-energy SQLite database.
- Parameters:
path (str or Path, optional) – Database file. Defaults to
DEFAULT_DB_PATH: the installer- managed location registered in~/.seamm.d/seamm.ini’s[thermochemistry]section (database-path, normally~/SEAMM/Parameters/thermochemistry/thermochemistry.db– seeinstaller.py) if the installer has been run, else the bundleddata/thermochemistry.dbshipped with the package (a stale prototype snapshot, useful only before the installer has run).read_only (bool) – Open without creating/migrating the schema, and without permission to write. Use for a package-installed, already-built database.
- add_atom_energy(symbol, code, method, energy, *, ref_type='atom', settings='', units='kJ/mol', correction=None, spin_multiplicity=None, source=None, computed_date=None, code_version=None, notes=None)[source]#
Insert or update one computed atomic reference energy.
- Parameters:
symbol (str) – Element symbol, e.g. “O”. Must already exist via add_element.
code (str) – Originating code: “gaussian”, “psi4”, “orca”, “vasp”, …
method (str) – Method/functional label, e.g. “CBS-QB3”, “PBE-D3BJ”. Where the basis is folded into the label (as in the legacy Gaussian/Psi4 tables, e.g. “CCD-FC/6-31++G(2d,2p)”), pass the full label.
energy (float) – The atom’s energy, in units (converted to kJ/mol on storage).
ref_type ({"atom", "element_phase"}) – “atom” = isolated gas-phase atom. “element_phase” = energy per atom of the standard-state phase (VASP’s existing convention).
settings (str) – Free-form disambiguator that is part of the identity key, e.g. “encut=700eV”. Defaults to “” (not NULL, so the UNIQUE constraint dedups consistently).
units (str) – Units of energy and correction: one of “kJ/mol”, “kcal/mol”, “eV”, “hartree”/”E_h”.
correction (float, optional) – Additive correction (e.g. Gaussian’s “<method> correction” column), same units as energy.
- add_element(atomic_number, symbol, *, multiplicity=None, term_symbol=None, standard_state=None, dfH0_0K=None, dfH0_298K=None, dfH0_298K_stderr=None, h298_minus_h0_atom=None, h298_minus_h0_std_state=None, s298_gas=None, s298_gas_stderr=None, s298_std_state=None, s298_std_state_reference=None, reference=None, reference_note=None)[source]#
Insert or update the experimental reference row for one element.
- batch()[source]#
Defer commits until the end of the block, for bulk imports.
add_element/add_atom_energy normally commit immediately – the right default for interactive use, but one fsync per row makes a bulk import of hundreds of thousands of rows (e.g. the full Gaussian/Psi4 composite-method grids) prohibitively slow. Wrap those calls in
with db.batch():to commit once at the end instead.
- coverage(code, method, *, ref_type='atom', settings='')[source]#
Return (n_elements_present, max_atomic_number) for one (code, method).
- dfH0(symbol, *, at_0K=True)[source]#
Experimental atomic heat of formation, kJ/mol.
- Parameters:
at_0K (bool) – If True (default, and the recommended anchor for an “energy of formation” – see the design doc), return DfH(X, 0K). If False, return the 298 K gas-phase value used by the existing enthalpy-of-formation code paths.
- doi()[source]#
The Zenodo DOI of the published snapshot this database file IS, or None for a working copy that has not been published yet.
Unlike the schema version (which just says “the table layout this code understands”), the DOI identifies the actual data – the citable, permanent reference for reproducibility. Set once, right before the file is uploaded to Zenodo – see
scripts/publish_to_zenodo.pyand set_doi.
- dump_atom_energies_csv(path)[source]#
Write the atom_energy table (joined to element symbols) to CSV.
- get_atom_energy(symbol, code, method, *, ref_type='atom', settings='', units='kJ/mol')[source]#
Return one atomic reference energy (including its correction), or None.
- get_atom_energy_row(symbol, code, method, *, ref_type='atom', settings='')[source]#
Return the full atom_energy row (energy, correction, source, computed_date, code_version, notes, …) as a dict, or None.
For provenance/reporting – get_atom_energy returns just the combined number get_reference_energies/atomization_energy need.
- get_element(symbol=None, atomic_number=None)[source]#
Return the experimental reference row for one element as a dict, or None.
- get_reference_energies(code, method, *, ref_type='atom', settings='', units='kJ/mol')[source]#
Return
{symbol: energy}for every element tabulated for this (code, method, ref_type, settings) combination.This is the lookup table formation.formation_energy needs.
- missing(code, method, symbols, *, ref_type='atom', settings='')[source]#
Return the subset of symbols with no tabulated (code, method) energy.
- s298_gas(symbol)[source]#
Experimental standard molar entropy of the isolated gas atom, S°(X, g, 298.15 K), J/(mol K), or None.
- s298_std_state(symbol)[source]#
Experimental standard molar entropy of the element’s standard-state phase, PER ATOM of X (e.g. S°(H2,g,298.15K)/2 for H), J/(mol K), or None. Needed (together with s298_gas) for a Gibbs-energy-of- formation anchor – see formation.formation_gibbs_energy.
- schema_version()[source]#
The schema version stamped in this database (an int), for reports/provenance – see _SCHEMA_VERSION.
- set_doi(doi)[source]#
Stamp this database file with the Zenodo DOI of the snapshot it is about to become. Call this right before uploading the file – Zenodo prereserves a DOI as soon as a deposit/version is created, before any file is attached, so the DOI can be baked into the file itself rather than only living in the Zenodo record around it.
- seamm_thermochemistry.atomization_energy(composition, system_energy, db, code, method, *, ref_type='atom', settings='', units='kJ/mol')[source]#
sum(n_X * E_ref(X)) - E(system): the energy to separate all atoms.
This is exactly Gaussian/Psi4’s “Atomization Energy” (electronic-only, no ZPE, if system_energy is itself ZPE-free) and VASP’s “cohesive energy”. Positive for a bound system.
- Parameters:
composition (dict or collections.Counter) – {element_symbol: count}, e.g. {“C”: 2, “H”: 6}.
system_energy (float) – The system’s electronic energy, in units.
db (ThermoDB) – Open reference-energy database.
code (str) – Identify which tabulated atom energies to use.
method (str) – Identify which tabulated atom energies to use.
ref_type ({"atom", "element_phase"}) – See
ThermoDB.add_atom_energy.settings (str) – Must match the settings used when the atom energies were stored.
units (str) – A pint-parseable unit string for system_energy and the return value, e.g. “kJ/mol”, “eV”, “E_h”.
- seamm_thermochemistry.build_ladder(species, *, name, energy_level, frequency_level=None, zpe_scale=0.9863, atct_version=None, atct_base=None, store_path=None, protect='default', fetch_covariance=False, anchors_from_atct=True)[source]#
Build a ladder for species (SMILES; their lower-rung fragments are added as needed) from the fragment store.
- Parameters:
energy_level (str) – The high-level energies: a settings fingerprint, or a model chemistry string (then unverified records are accepted, and reported).
frequency_level (str, optional) – The level of the ZPE and thermal corrections (default: the energy level’s own record).
zpe_scale (float) – Scale factor for the harmonic ZPE (0.9863 for B2PLYP-D3/cc-pVTZ).
- seamm_thermochemistry.build_official(out, version, sources, *, previous=None, source=None, frequency_fingerprints=None, energy_fingerprints=None, exclude=None, atct=None, check=True, job_ids_only=True)[source]#
Build an official store: the previous official one (if given) plus the contributions sources, marked official with version (and source, e.g. its Zenodo DOI), compacted, with a
.sha256file and a.report.jsonbeside it.frequency_fingerprints and energy_fingerprints, when given, keep only the rows reachable at those levels (e.g. the presets’ r2SCAN-3c geometries and the revDSD energies on them). exclude ({(species, charge, multiplicity): reason}, or with a fourth element, a conformer label, to leave out one conformer; see
load_exclusions()) leaves rows out, with the reason in the report.With job_ids_only (the default: an official store is published) the provenance keeps each job’s id and drops its directory (see
job_id_only()), in every row and in the history; the full paths stay in the stores the rows came from.Nothing is written when a contribution’s values differ from a row already there (resolve it with
supersedeand a reason first), or when the sanity pass (sanity_check(); atct for the multiplicities) finds a problem in a species state that is not excluded. Returns (path, sha256, counts).
- seamm_thermochemistry.check_structure(symbols, coordinates, species, charge=None, multiplicity=None)[source]#
Why a structure is not one of its species, or None if it is.
The bonds are perceived again from the coordinates, not taken from the structure’s own (which came from the SMILES it was built from), so a species that rearranged while it was optimized is caught. The graph (elements and connections) must be that of species, whose formal charge must be charge and whose radical electrons must have the parity of the unpaired electrons of multiplicity; and no atoms may be too close together (
molsystem.vsepr.check_contacts(), which also catches ligands of a hypervalent centre on top of each other). Without RDKit nothing is checked.
- seamm_thermochemistry.derive_dfH0_0K(db, *, overwrite=False)[source]#
Fill in element.dfH0_0K for every element where it’s derivable.
- Parameters:
db (ThermoDB)
overwrite (bool) – If False (default), skip elements that already have dfH0_0K – the master sheet’s own H/He values are treated as authoritative over a re-derivation (though the derivation reproduces them to within rounding; see module docstring).
- Returns:
Symbols of the elements filled in.
- Return type:
list of str
- seamm_thermochemistry.export_local(out, local=None, official=None, *, label=None)[source]#
Write the local store’s rows that the official store lacks, or has with values differing by more than 0.1 kJ/mol (flagged in the history as “differs from official”, with the official values), to the contribution file out, with their provenance and superseded values. label is free text recorded in the file; nothing about the host or user is added.
Returns {table: n rows exported}, plus “differing”: n.
- seamm_thermochemistry.format_report(composition, db, code, method, *, ref_type='atom', settings='', units='kJ/mol', name=None, level_label=None, system_energy=None, system_enthalpy=None, system_gibbs_energy=None, temperature=None)[source]#
Build the detailed formation-energy report text.
- Parameters:
composition – As in atomization_energy.
db – As in atomization_energy.
code – As in atomization_energy.
method – As in atomization_energy.
ref_type – As in atomization_energy.
settings – As in atomization_energy.
units – As in atomization_energy.
name (str, optional) – A human-readable name for the system (falls back to a generic label).
level_label (str, optional) – A human-readable level of theory (falls back to
f"{code}/{method}").system_energy (float, optional) – The system’s electronic energy (0 K, ZPE-free). If given, the Atomization Energy and Energy-of-Formation (DfE0) sections are included.
system_enthalpy (float, optional) – The system’s total enthalpy H(T). If given (together with temperature), the Enthalpy-of-Formation (DfH(T)) section is included.
system_gibbs_energy (float, optional) – The system’s total Gibbs free energy G(T). If given (together with temperature), the Gibbs-Energy-of-Formation (DfG(T)) section is included.
temperature (float, optional) – Temperature, in K, for the DfH(T)/DfG(T) sections.
- Returns:
The formatted report. Never raises: any missing reference data is reported as a plain-text note in the relevant section instead of stopping the whole report (a molecule that is missing, say, the standard-state entropy for one of its elements should still get an atomization-energy and DfE0 section).
- Return type:
str
- seamm_thermochemistry.formation_energy(composition, system_energy, db, code, method, *, ref_type='atom', settings='', anchor=True, anchor_at_0K=True, units='kJ/mol')[source]#
The formation-referenced energy of a system.
- Parameters:
composition – As in atomization_energy.
system_energy – As in atomization_energy.
db – As in atomization_energy.
code – As in atomization_energy.
method – As in atomization_energy.
ref_type – As in atomization_energy.
settings – As in atomization_energy.
units – As in atomization_energy.
anchor (bool) – If True (default), add the experimental atomic heat of formation so the result is a true energy/enthalpy of formation – the physically meaningful, cross-code-comparable quantity this package exists for. If False, return the bare, no-experimental-data-needed quantity: VASP’s existing
DfE0convention (ref_type="element_phase"is the natural pairing, but any ref_type is accepted).anchor_at_0K (bool) – Use the 0 K experimental anchor (recommended: pairs with a ZPE-free system_energy to give an “energy of formation”, degrades gracefully to DfH(0K) if ZPE is added back in, and to DfH(298K) if the molecule’s thermal correction is added) or the 298 K anchor (matches the existing enthalpy-of-formation code paths directly). Ignored if anchor is False.
- Returns:
Energy/enthalpy of formation (anchor=True) or the bare atomization-referenced energy in VASP’s DfE0 sign convention (anchor=False), in units.
- Return type:
float
- seamm_thermochemistry.formation_enthalpy(composition, H_system, temperature, db, code, method, *, ref_type='atom', settings='', units='kJ/mol')[source]#
DfH(T): enthalpy of formation from the elements, at temperature T.
Generalizes
formation_energy(..., anchor=True, anchor_at_0K=True)from the electronic-only, 0 K quantity to a real temperature. The Hess cycle is elements (standard states, T) -> gas atoms (T) -> molecule (T). The gas atoms’ own H(T)-H(0) appears in both halves of the cycle and cancels exactly, leaving the standard-state elements’ thermal enthalpy (the form used by G4 and similar composite methods):DfH(T) = sum(n_X * [DfH0(X, 0 K) - (H_std(X, T) - H_std(X, 0))]) - [sum(n_X * E_ref(X)) - H_system]
where H_std is the enthalpy of the element’s standard state, per atom (ThermoDB.element.h298_minus_h0_std_state, halved for the diatomic gases). Equivalently, DfH(T) = sum(n_X * DfH0(X, T)) - [atomization enthalpy at T]; at 298.15 K the two forms use the same tabulated data and agree to rounding.
Approximation: the standard-state H(T)-H(0) is tabulated at 298.15 K only and is scaled linearly in T elsewhere – see _std_state_thermal_enthalpy for the size of the error. At 298.15 K the result is exact.
History: until 2026.10.28 this function subtracted (5/2)RT per atom, the gas atom’s own thermal enthalpy, instead of the standard-state term. That made DfH(298 K) too negative by 1.96 kJ/mol per H, 5.15 per C and 1.86 per O (5.8 kJ/mol for water).
- Parameters:
composition – As in atomization_energy.
db – As in atomization_energy.
code – As in atomization_energy.
method – As in atomization_energy.
ref_type – As in atomization_energy.
settings – As in atomization_energy.
units – As in atomization_energy.
H_system (float) – The system’s total enthalpy H(T) – electronic + ZPE + thermal correction – on the SAME absolute energy scale as the electronic energy stored for the reference atoms (not a thermal correction alone), in units.
temperature (float) – Temperature, in K.
- Returns:
DfH(T), in units.
- Return type:
float
- seamm_thermochemistry.formation_gibbs_energy(composition, G_system, temperature, db, code, method, *, ref_type='atom', settings='', units='kJ/mol')[source]#
DfG(T): Gibbs energy of formation from the elements, at T.
Same Hess-cycle construction as formation_enthalpy, extended to G. Requires each element’s s298_std_state (the standard-state phase’s molar entropy, per atom – see ThermoDB.s298_std_state) in addition to the usual 0 K atom energies, heats of formation and standard-state H(298 K)-H(0 K); raises MissingReferenceData for any element missing any of them.
Derivation: on the computed energy scale the standard-state element’s Gibbs energy per atom is:
G_std(X, T) = E_ref(X) - DfH0(X, 0K) + [H_std(X, T) - H_std(X, 0)] - T * S_std(X, T)
(the gas atom at 0 K sits DfH0(X, 0K) above the standard state, and has no ZPE), so:
DfG(T) = G_system - sum(n_X * G_std(X, T)) = sum(n_X * [DfH0(X, 0K) - (H_std(X, T) - H_std(X, 0))]) + T * sum(n_X * S298_std_state(X)) / 1000 - [sum(n_X * E_ref(X)) - G_system]
No atomic entropy is needed: the gas atom is only an intermediate.
Approximations: S298_std_state is used at its tabulated 298.15 K value regardless of T, and the standard-state H(T)-H(0) is scaled linearly from its 298.15 K value (see _std_state_thermal_enthalpy). At T=0 this reduces exactly to
formation_energy(..., anchor=True, anchor_at_0K=True)evaluated with G_system in place of the electronic energy.History: until 2026.10.28 this function subtracted (5/2)RT per atom instead of the standard-state thermal enthalpy; see formation_enthalpy.
- Parameters:
composition – As in atomization_energy.
db – As in atomization_energy.
code – As in atomization_energy.
method – As in atomization_energy.
ref_type – As in atomization_energy.
settings – As in atomization_energy.
units – As in atomization_energy.
G_system (float) – The system’s total Gibbs free energy G(T), on the same absolute scale as the electronic energy, in units.
temperature (float) – Temperature, in K.
- Returns:
DfG(T), in units.
- Return type:
float
- seamm_thermochemistry.frequency_correction(model_chemistry)[source]#
The frequency correction for a frequency level, or None if there is none.
Only the exact (method, basis) of an entry matches: a correction fitted for PM7 is not used for PM6, PM6-ORG or any other method.
- seamm_thermochemistry.harvest_configuration(store, configuration, species, *, charge=None, multiplicity=None, conformer='', job=None, mapped=None, label=None, energy='total energy#*#*', zpe='ZPE#*#*', enthalpy='H(298.15 K)#*#*', fingerprint='settings fingerprint#*#*', frequency_fingerprint='frequency fingerprint#*#*', supersede=None, force=False, tables='all', structure_check=True, write_back=False, counts=None)[source]#
Put one configuration’s species into the store: what
harvest_job()does for each system of a job, and what the Thermochemistry step does for each species it computes (write-back).Every energy property matching energy gives a fragment row (unless tables is “geometry”) and an energy row on the configuration’s geometry, and, with the ZPE or enthalpy of the same level, a geometry row. See
harvest_job()for the properties read and the guards.A geometry with an imaginary mode beyond
IMAGINARY_TOLERANCEis refused, with every energy on it, in a harvest as in a write-back. A key held by a row quarantined by hand (UNUSABLE: <reason>) keeps it: the attempt is logged and reported, “quarantined” in the tallies, and only an explicit supersede (a person’s decision, with a reason) replaces it.write_back adds the rule for results written by a running job:
a geometry lower in E + ZPE by more than 0.1 kJ/mol than every stored conformer of the species at its level, and structurally distinct from each (RMSD above
CONFORMER_RMSDÅ), is added as a new conformerauto:<geometry hash>instead of being only logged; lookups use the lowest.
Returns the tallies, counts updated in place when given.
- seamm_thermochemistry.harvest_job(job_directory, store, *, energy='total energy#*#*', zpe='ZPE#*#*', enthalpy='H(298.15 K)#*#*', fingerprint='settings fingerprint#*#*', frequency_fingerprint='frequency fingerprint#*#*', name_prefix='CBH:', all_systems=False, database='seamm.db', supersede=None, force=False, species_map=None, tables='all', structure_check=True)[source]#
Promote the species energies of one job into the store.
Every system whose name starts with name_prefix (
CBH:<SMILES>: the SMILES is the species identity), or every system when all_systems (the name, less the prefix if present, canonicalized, is the species, as for systems that Read Structure named by SMILES), contributes one record per energy property found on its current configuration, with the ZPE and enthalpy of the same level when present, the fingerprints if the QC step stored them, and the provenance (job directory, configuration id, geometry hash, date). The model chemistry is the third#field of the energy property’s name.With a species_map ({(job, system name): row}, see
load_species_map()) the systems are found by their names in the map instead of by the prefix: the map gives each one’s species (the store key, with the stereo of its structure), and optionally its configuration, charge, multiplicity and conformer label. Systems the map does not list are skipped. tables is “all” (the fragment table too) or “geometry” (only the geometry and energy tables, e.g. for several conformers of a species, which the fragment table cannot hold).The species map may also carry the thermal corrections of the structures whose jobs did not store them as properties:
E (kJ/mol),ZPE (kJ/mol)(unscaled),H298 (kJ/mol)(H at 298.15 K) andsymmetry number. They are used only for an energy with no ZPE or enthalpy of its own level, as H - E added to the job’s energy, and only when the map’s E agrees with the job’s within 0.1 kJ/mol (otherwise the system is reported and gets no geometry row).With supersede (the reason, e.g. “lowest conformer from B2PLYP geometries”), a record whose key is already stored with different values replaces them, the old values kept in the
supersededtable, instead of being logged as a differing duplicate. With force as well, values within the duplicate tolerance are replaced too (seeFragmentStore.supersede()).The same properties also fill the geometry and energy tables (
geometry_store): an energy property with a ZPE or enthalpy of the same level makes a geometry row at that level (the configuration’s coordinates, its energy, ZPE and H, and the frequencies, symmetry number and moments of inertia when stored asfrequencies,symmetry numberandmoments of inertiaproperties of the same level), and every energy property makes an energy row on the configuration’s geometry. An energy whosecalculationrecord names a different geometry is refused (the pairing guard) and reported in “skipped”.With structure_check (the default) a structure whose bonds, perceived again from its coordinates, are not those of its species, or that has atoms too close together, is refused (see
check_structure()).Returns {“inserted”: n, “duplicate”: n, “logged”: n, “superseded”: n, “skipped”: [reasons], “geometry”: {outcome: n}, “energy”: {outcome: n}}, the first four counting the fragment rows.
- seamm_thermochemistry.layer_conflicts(local=None, official=None)[source]#
The keys in both the local and the official store whose values differ by more than 0.1 kJ/mol: [{“table”, “key”, “local”, “official”, “difference”, “superseded”}], “superseded” when the local value is one the official store superseded (the official value is then used).
- seamm_thermochemistry.load_species_map(path)[source]#
A species map from a CSV file: {(job, system name): row}.
The columns are
job(the job id, or empty for any job),system nameandspecies(the store key: the canonical SMILES, with the stereo of the structure), and optionallyconfiguration name(the configuration to use, default the system’s current one),charge,multiplicityandconformer label, and the thermal correctionsE (kJ/mol),ZPE (kJ/mol),H298 (kJ/mol)andsymmetry number(seeharvest_job()). Other columns are ignored.
- seamm_thermochemistry.merge_stores(target, sources, *, label=None, allow_official=False)[source]#
Add the rows of sources (paths or stores) to target (a path or a writable store): insert-or-ignore, differing duplicates logged, superseded values copied as history. An official target is refused unless allow_official (for
build_official()).Returns {table: {outcome: n}} plus “history”: n.
- seamm_thermochemistry.migrate_store(path=None, *, backup=True)[source]#
Bring a store up to date: the
metaandwriteback_logtables (schema 4), and the model chemistries stored bare with the program filled in (ORCA:CMP@R2SCAN-3C->CMP@R2SCAN-3C+ORCA). A dated copy of the file is kept first. Running it again changes nothing (and makes no copy).Keys do not change: verified rows are keyed by fingerprints, and the keys of unverified rows keep the prefixed spelling. Returns {“backup”: path or None, “changed”: {table: n}, “schema”: (old, new)}.
- seamm_thermochemistry.official_store_path(path=None)[source]#
The official store: path if given, else
fragments-official.dbnext to the local one (<...>/Parameters/thermochemistry/).
- seamm_thermochemistry.reaction_uncertainty(coefficients, uncertainties, covariance=None)[source]#
The 95 % uncertainty of Σ νᵢ ΔfHᵢ at 298.15 K.
- Parameters:
coefficients ([float]) – The stoichiometric coefficients ν of the reference species.
uncertainties ([float]) – Their listed 95 % uncertainties, same order.
covariance ([[float]], optional) – Their covariance matrix, same order. If None the uncertainties are combined in quadrature, which ignores the correlations.
- Returns:
The uncertainty, and whether the covariance was used.
- Return type:
(float, bool)
- seamm_thermochemistry.split_model_chemistry(text, program=None)[source]#
The bare model chemistry and the program of a model chemistry string.
ORCA:CMP@R2SCAN-3Cgives (”CMP@R2SCAN-3C”, “ORCA”); a bareCMP@R2SCAN-3Cgives (”CMP@R2SCAN-3C”, program). Only a prefix before the first@is a program, soORCA:DFT@B3LYP/bse:def2-SVPgives (”DFT@B3LYP/bse:def2-SVP”, “ORCA”), and a string without@(e.g. a fingerprint,sha256:...) is returned as it is. A program given explicitly wins over a different prefix, which is logged.- Parameters:
text (str or None) – The model chemistry.
program (str, optional) – The program, if known otherwise.
- Return type:
(str or None, str or None)
- seamm_thermochemistry.store_is_writable(path=None)[source]#
(bool, reason): whether a running job may write the fragment store.
Only on a local filesystem: SQLite’s locking is unreliable on network and parallel filesystems (NFS, GPFS, Lustre). seamm.ini’s
[thermochemistry]fragment-store-writable = auto | yes | nooverrides the check.