Skip to content

Depositing a thermo record

One page for whoever is building a deposit. It states what a thermo record must contain, what belongs somewhere else, what TCKDB refuses and why, and what it will never guess on your behalf.

What a thermo record is

A thermo record holds thermochemistry for one species entry: heat capacity, entropy, and enthalpy, as scalars at 298.15 K, as tabulated points, or as a fitted model such as a NASA polynomial or a Wilhoit form. A record may carry any combination of those.

It is a scientific result. Once a curator has approved it, it is immutable. Nothing is edited in place afterwards, so what you deposit is what stands.

The enthalpy rule, which is the part people get wrong

Every enthalpy on a thermo record is a standard enthalpy of formation: the energy to build one mole of the species from its elements in their reference forms, with each element in its reference form counted as zero.

At 298.15 K that is the stored value. At any other temperature the value is that same formation energy plus the species' own enthalpy increment from 298.15 K. The elemental term stays pinned at 298.15 K and is not recomputed. This is the convention CHEMKIN files, Cantera, Burcat's tables and Arkane already use, so a value coming from any of those needs no conversion.

Because of that, a deposit carrying any enthalpy must say so:

{ "enthalpy_reference_kind": "formation_298k" }

That is the only accepted value today. "Any enthalpy" means any of these:

Field Where it lives
h298_kj_mol the record itself
nasa a fitted NASA-7 block, whose constant term is an enthalpy offset
nasa9_intervals the same for NASA-9
wilhoit.h0_kj_mol a Wilhoit block
points[*].h_kj_mol any tabulated point carrying an enthalpy
points[*].g_kj_mol any tabulated point carrying a Gibbs energy

A stored Gibbs energy counts because it is H(T) − T·S(T) on the record's enthalpy zero, so it means nothing until that zero is declared. This holds even when the points carry no enthalpy of their own: a record with only Gibbs energies and entropies must still declare formation_298k.

A record that carries only entropy and heat capacity has no enthalpy, so it must leave the declaration out. Declaring a reference for a quantity the record does not contain is refused.

enthalpy_formation_0k_kj_mol is outside this rule. Its name already says what it is, and it has its own reference.

What does not belong on a thermo record

Two real and useful quantities are not formation enthalpies and are refused here. Nothing is lost; they are deposited elsewhere.

  • A sensible increment, meaning the enthalpy of the species at some temperature minus its value at 0 K.
  • An absolute enthalpy straight out of a quantum chemistry program.

Both belong in the molecular property observation route, which records the property kind, the physical basis, the temperature, the pressure and the meaning of the uncertainty separately.

State fields, and what leaving them out means

Field Effect if you omit it What is true of a record that predates this rule
phase For a scientific_origin: computed record, defaults to gas — the ideal-gas rigid-rotor/harmonic-oscillator statistical mechanics every computed producer here uses has no other mode. For any other origin the record just does not say which phase it describes. Same as omitting it today: null, unenforced, nothing retroactive.
reference_pressure_bar Never defaulted, for any origin (decided 2026-09-24, issue #529). Entropy comparisons against other records become unavailable, because the standard-state pressure convention is built into the entropy. Heat-capacity comparisons are unaffected, since reference pressure cannot change a heat capacity. A record deposited before 2026-09-24 may carry an invented 1.0 (bar) stamped by the since-removed default. That value is not corrected retroactively — see the note below.
enthalpy_reference_kind Refused when the record carries an enthalpy or a Gibbs energy; required to be absent when it carries neither. A record deposited before this rule existed, carrying an enthalpy or a Gibbs energy with no declaration, is left exactly as deposited. (Point Gibbs energies joined the rule on 2026-09-27; undeclared ones deposited before then stay as they are.) It is not rewritten and not frozen: every column other than h298_kj_mol and enthalpy_reference_kind stays writable, and an update that only adds the declaration is accepted. Only a write that sets h298_kj_mol or changes enthalpy_reference_kind without leaving the row in a valid combination is refused — the same rule a new record gets, applied only at the moment either of those two columns is actually written.

A null in reference_pressure_bar or enthalpy_reference_kind means the deposit did not state it. It never means a default was assumed — until 2026-09-24 that was not true of reference_pressure_bar on a computed record (see issue #529): every computed deposit that omitted it was silently stamped 1.0, a standard state the dominant producer's own numbers (ARC computes entropy at 1 atm) were never computed at. That default is now removed. phase is the one field here that still defaults, and only for computed-origin records — see the table above for why that case is judged differently.

Saying what the values describe, and how they were made

Two optional blocks let a record state things it could not state before. Both are claims, stored exactly as you make them. TCKDB does not infer either from anything else (a statmech link does not make a record "equilibrium"; a method name in a level of theory does not make it "G4"), never defaults either, and never fills one in for a record that was deposited without it. Leave a block out to say nothing; a record that says nothing reads null for it.

thermodynamic_target says what the values describe.

  • {"kind": "equilibrium_ensemble"} — the thermally equilibrated population of the species' conformers. It names no conformer group; one that does is refused (thermo_target_group_not_allowed) rather than ignored.
  • {"kind": "single_conformer", "conformer_group_ref": "cg_..."} — one named conformer group of this record's own species entry. A group is required (thermo_target_group_required) and must belong to the same species entry (thermo_target_group_owner_mismatch). Inside a computed-species or computed-reaction bundle you name the conformer the bundle declares, "conformer_key": "...", instead of a ref.

protocol says how the values were produced. It is versioned ("version": 1 is the only accepted value, thermo_protocol_version_unsupported otherwise), and an unknown field or value is refused. Every block in it is optional, but it must say something.

Block What it says
recipe name is g3, g4, g4mp2, g4_complete or other (then other_name is required); recipe_version is free text.
formation_reference derivation is atomization, isodesmic or working_reaction; reference_data_source is atct, nist_janaf, codata or other (then reference_data_detail is required).
thermal_approximation ensemble_representation (lowest_conformer, boltzmann_conformers) and internal_motion (harmonic, hindered_rotors, anharmonic). This is not the target: the target is the ensemble you mean, this is what stands in for it. A record can target the equilibrium ensemble and represent it by its lowest conformer.
departures A list of {component, description} naming where you departed from the standard form of the recipe.
supporting_calculations The calculations the declaration rests on, by calculation_key (a calculation declared in the same request) or, on /uploads/thermo, calculation_ref (a calculation already deposited for the same species entry).

departures has three states and they mean different things: omitted says you did not state it; an empty list says you state there were no departures; a list names them. Only the empty list says "standard".

Refusals you may see, and what to do

Every one of these comes back with a code and a message. They are generated by a single shared rule used by the server, the command line and the Python client, so all three say the same thing.

enthalpy_declaration_absent

Enthalpy content requires enthalpy_reference_kind. Declare
formation_298k only when the source states that convention;
other enthalpy quantities belong in molecular_property_observation.

Add the declaration if your source really does report formation enthalpies. If it reports something else, use the observation route instead.

enthalpy_declaration_without_content

enthalpy_reference_kind declares a quantity this thermo record does not carry.
Omit the declaration for entropy and heat-capacity-only records.

enthalpy_quantity_not_storable_here

Thermo accepts only formation_298k enthalpies. Deposit sensible
increments and absolute enthalpies through the molecular_property_observation
route instead.

enthalpy_reference_kind_unrecognized

A near-miss of the one legal value — wrong case, stray whitespace — is a typo, not a different quantity, so it gets its own message rather than the enthalpy_quantity_not_storable_here refusal above:

'<value>' is not a recognized enthalpy_reference_kind -- did you mean
'formation_298k'? Matching is exact and case-sensitive.

The Python client refuses the same cases when you build the payload, before anything is sent, so you see the message locally rather than after a round trip.

thermo_role_duplicate (and statmech_role_duplicate)

A record may link at most one single point (sp) per optimisation. When the record links no opt at all, there is no optimisation to count against, so the rule applies to the structure instead: two sp links on one structure are refused. This applies on every route that links calculations by role, including the standalone /uploads/thermo and /uploads/statmech routes with a polyatomic species whose only linked calculations are sps.

  • Polyatomic species. "One structure" means the same atoms (same elements and stated isotopes) at the same shape, wherever they sit and in whatever order they are listed. Two sp links on the same geometry are refused, and so is one on a copy of it that was translated, rotated or had its atoms listed in another order (a water geometry shifted by 1 Angstrom, or with its hydrogens listed first, is the same water). The comparison is a rigid-motion RMSD (Kabsch alignment) with a tolerance taken from the precision the coordinates were written to (1.7e-6 Angstrom for six decimals), so a geometry that really differs (another conformer, a bond length changed by more than rounding) is a different structure and is not refused. An enantiomer is a different structure; a mirror image counts as the same only when a rotation superposes it atom for atom, in whatever order its atoms are listed.
  • Reordered atoms. The check looks for a relabelling of one geometry's atoms that lays it on the other, using the geometries alone (the rule has no species graph). Only atoms of one element and one isotope are exchanged, so an atom stated as 2H is never taken for a 1H, and D and T count as 2H and 3H. Interchangeable atoms (the three H of a CH3) are handled: any one relabelling that superposes is enough.
  • Limits. The search is bounded, and past any bound a pair is left undecided and counted as different: geometries over 200 atoms are not searched; a pair needing more than 256 trial alignments or examining more than 50,000 candidate placements (for example a very large, highly symmetric, chiral cluster) is undecided; and a record has a fixed budget of 4,096 alignments and 50 million work units over all its pairs for the relabelling search (a pair of different shapes is dismissed first by its radius of gyration at no cost when those radii differ by more than the rounding tolerance, else by a gate of about n^2/2 units). The comparison of two geometries listed in the same order has its own allowance of 50 million units (800 per pair plus 30 per atom). In those cases a duplicate energy on a moved copy may not be caught (the relabelling search can still catch a pair that is past the in-order allowance); ordinary molecules need one alignment and a few milliseconds.
  • A moved copy written to three or fewer decimals may not be recognised (such coordinates are held to the four-decimal tolerance).
  • A single atom. An atom has no geometry to differ in, so "one structure" means the same element and the same stated isotope mass number (D and T count as hydrogen, as they do everywhere else elements are compared). A second sp on a shifted copy of the atom (1\nH\nH 1.0 0.0 0.0 beside 1\nH\nH 0.0 0.0 0.0) is refused exactly like one on the identical geometry.
  • No geometry declared. An sp that declares no geometry is not compared: with nothing to compare, the rule does not guess.
  • Composite energies. A linked composite calculation counts as the record's energy in place of an sp, so two of them on one structure are refused the same way, under the same code.

The fix is to link one sp, or give each its own optimisation. The extra calculation can still be uploaded and stored; it just may not be linked as a second energy for the same record.

Three worked shapes

A scalar deposit with an enthalpy:

{
  "h298_kj_mol": -74.6,
  "s298_j_mol_k": 186.25,
  "enthalpy_reference_kind": "formation_298k",
  "phase": "gas",
  "reference_pressure_bar": 1.0
}

A fitted deposit with no separate scalar. This is what a mechanism file gives you, and it is accepted as it stands. Do not invent a 298 K scalar to go with it, and do not compute one from the fit:

{
  "nasa": { "...": "coefficients and temperature ranges" },
  "enthalpy_reference_kind": "formation_298k",
  "phase": "gas",
  "reference_pressure_bar": 1.0
}

An entropy and heat-capacity deposit, which carries no enthalpy and therefore no declaration:

{
  "s298_j_mol_k": 186.25,
  "points": [{ "temperature_k": 300.0, "cp_j_mol_k": 35.7 }],
  "phase": "gas",
  "reference_pressure_bar": 1.0
}

What TCKDB will never do for you

  • Infer an enthalpy reference from the size of a number, from which program produced it, from a neighbouring record, or from whether the record is computed or experimental.
  • Fill in a default reference. There is no default, deliberately, because a wrong one is a several-hundred-kilojoule error that looks entirely plausible.
  • Derive a 298.15 K scalar from a fit and store it as though you deposited it.
  • Change what an already-approved record says.

If a producer has not been told which convention it emits, the honest move is for that producer to refuse to build the thermo block rather than send an enthalpy with no declaration. The ARC and SDF adapters shipped here behave that way: pass no enthalpy_reference_kind and either refuses before it builds a thermo block. The CHEMKIN adapter is the one exception, and deliberately so: CHEMKIN's NASA-7 thermodynamic format is formation-referenced by definition, so that adapter asserts the declaration unconditionally rather than asking the depositor to configure it — see clients/python/adapters/chemkin/tckdb_chemkin/payloads.py. That is not a guess the way it would be for a source whose convention the adapter cannot actually know.

A depositor who mistypes the one legal value — wrong case, stray whitespace — gets a distinct refusal telling them so (enthalpy_reference_kind_unrecognized), rather than being pointed at the observation route as though they had named a different quantity.

Records that predate this rule

Records deposited before the declaration existed carry no reference, and they are not being rewritten. A read reports the reference as not recorded.

Three of the advisory consistency checks read the declaration, because they compare enthalpies and an enthalpy means nothing without its reference:

  • Gibbs self-consistency checks each stored Gibbs value against the same record's H − T·S. The declaration covers points[*].g_kj_mol too, so a Gibbs value on a record that declares nothing is not checked.
  • Kirchhoff checks that a record's enthalpies at different temperatures agree with its own heat capacity, and that its enthalpy forms agree with each other. Comparing two records needs both to declare the same reference.
  • Hess checks a rate record's stated reaction energy against the formation enthalpies of the thermo records named for its participants. Every one of those thermo records must declare the same reference. (At 0 K it reads enthalpy_formation_0k_kj_mol, whose name already fixes its meaning.)

A record without a declaration is excluded from all three and reported as enthalpy_reference_unrecorded. It is not assumed to be a formation enthalpy and it is not checked under a guessed convention. Such a record can no longer be deposited: any enthalpy, Gibbs energy included, now has to come with the declaration. So the excluded records are the ones that predate the rule, and they stay excluded. The heat-capacity and entropy checks do not read the declaration, because neither quantity depends on it. The check of rates against thermo does not read it either; it uses the stored NASA coefficients as they are.

These checks are advisory. They record the differences they find and never change a record, its status, or whether it is selected.

If one of these records is later exported as a contribution bundle (scripts/export_contribution_bundle.py, see docs/contribution-bundles/format.md), the exporter never re-emits an undeclared enthalpy next to a declaration of null — that shape is exactly what this page says the import route refuses. A scalar, point, or Wilhoit enthalpy with no declaration is dropped from the export (the rest of the record is unaffected); a NASA-7/NASA-9 fit with no declaration is left out of the bundle entirely, since its coefficients cannot be exported without the enthalpy they encode. Either way the export reports what it changed or left out, naming the record by its public ref, rather than doing it silently.