Resources Numerical accuracy Calculation policy

Molar mass and group-contribution estimates

Works out a molecule's formula and molar mass from its structure and estimates boiling point, critical constants, formation energies and heat capacity by Joback's method.

How the precision is kept

The arithmetic is 64-bit sums of table values. The atomic weights are abridged to four to six significant digits and the Joback group values, the file says, were typed without the paper open and are not confirmed. A group-contribution estimate is good to a few percent at best, which the result states in its notes.

Calculations under this policy

4 calculations in 1 source file. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.

process/env/cheminfo.js CadCraft 0.8.2 64-bit 4 calculations Reviewed

Tests tools/process-test/test-dec.mjs

Atomic weights

64-bit

The atomic weight of each element the structure reader knows.

H 1.008; C 12.011; N 14.007; O 15.999; S 32.06; Cl 35.45 …

Abridged values of four to six significant digits, so a molar mass built from them carries no more than that.

  • process/env/cheminfo.js
  • line 23
  • CadCraft 0.8.2
export const WEIGHT = { H: 1.008, B: 10.81, C: 12.011, N: 14.007, O: 15.999, F: 18.998, Si: 28.085, P: 30.974, S: 32.06, Cl: 35.45, Br: 79.904, I: 126.904, Na: 22.990, K: 39.098 };

Molar mass

64-bit

The molar mass of a molecule in g/mol.

M = Σ count × atomic weight

A sum over the elements present, each a whole-number count times a table weight.

  • process/env/cheminfo.js
  • lines 100 to 101
  • CadCraft 0.8.2
/** Molar mass, g/mol. */
export function molarMass(mol) { const c = composition(mol); let m = 0; for (const [k, n] of Object.entries(c)) { if (!(k in WEIGHT)) throw new Error('No weight for ' + k); m += n * WEIGHT[k]; } return m; }

Joback estimate

64-bit

Boiling point, critical temperature, pressure and volume, melting point, formation enthalpy and Gibbs energy, heat capacity and acentric factor of a molecule.

T_b = 198 + Σ; T_c = T_b ÷ (0.584 + 0.965 Σ − Σ²); P_c = (0.113 + 0.0032 n_atoms − Σ)⁻²; ω = 3⁄7 × θ ÷ (1 − θ) × log₁₀(P_c ÷ 1.01325) − 1, θ = T_b ÷ T_c

Sums of group values of three or four digits, then closed forms. The acentric factor subtracts 1 from a product of similar size and so has fewer digits than the estimates it is built from.

  • process/env/cheminfo.js
  • lines 197 to 210
  • CadCraft 0.8.2
/** Joback estimate for a molecule. → { ok, Tb, Tc, Pc (bar), Vc, Tf, Hf (kJ/mol), Gf, cp: (T) => J/mol/K | null, omega, groups, problems, notes } */
export function joback(mol) {
  const { groups, missing } = groupsOf(mol), problems = [];
  if (missing.length) { problems.push(problem('joback.missing', 'warning', 'No Joback estimate: this molecule has groups the table does not hold (' + missing.join('; ') + ').', [{ kind: 'molecule', id: formula(mol) }])); return { ok: false, groups, missing, problems }; }
  const S = new Array(11).fill(0); let hasCp = true;
  for (const [g, n] of Object.entries(groups)) { const row = GROUPS[g]; for (let k = 0; k < 7; k++) S[k] += n * row[k]; if (row[7] == null) hasCp = false; else for (let k = 7; k < 11; k++) S[k] += n * row[k]; }
  const nAtoms = mol.atoms.length + mol.atoms.reduce((s, a) => s + a.h, 0);
  const Tb = 198 + S[3], Tc = Tb / (0.584 + 0.965 * S[0] - S[0] * S[0]), Pc = Math.pow(0.113 + 0.0032 * nAtoms - S[1], -2), Vc = 17.5 + S[2];
  const th = Tb / Tc, omega = 3 / 7 * th / (1 - th) * Math.log10(Pc / 1.01325) - 1;
  const cp = hasCp ? (T) => (S[7] - 37.93) + (S[8] + 0.210) * T + (S[9] - 3.91e-4) * T * T + (S[10] + 2.06e-7) * T ** 3 : null;
  const notes = ['Group-contribution estimate: a few percent for small hydrocarbons, worse for polar molecules. Not a measured value.'];
  if (!hasCp) notes.push('The table holds no Cp terms for one of the groups, so no heat capacity was estimated.');
  return { ok: true, groups, Tb, Tc, Pc, Vc, Tf: 122 + S[4], Hf: 68.29 + S[5], Gf: 53.88 + S[6], cp, omega, problems, notes };
}

Component from a structure

64-bit

A component for the property package from the Joback estimate, with a quadratic heat-capacity fit.

C_p ÷ R ≈ A + B T + C T² fitted at 11 temperatures from 300 to 800 K; h_f (J/mol) = H_f (kJ/mol) × 1000

The normal equations are scaled by 1, 1e3 and 1e6 before the linear solve, which keeps the three unknowns of similar size. The gas constant is typed here as 8.314462618. The largest relative misfit of the quadratic is returned with the component.

  • process/env/cheminfo.js
  • lines 212 to 227
  • CadCraft 0.8.2
/** A component spec for lib/process (`defineComponent`) from SMILES: MW, Tc, Pc, omega, Tb, hf, gf and a quadratic Cp/R fitted to the Joback cubic over 300-800 K.
 *  opts { id, name?, define?: true (register it), TcRange? } Throws when the estimate or the Cp is not available (give measured values instead). */
export function toComponent(smiles, { id, name, define = false } = {}) {
  if (!id || !/^[a-z][a-z0-9_]{0,30}$/.test(id)) throw new Error('Give a component id of lower-case letters, digits and underscores.');
  const mol = parse(smiles), j = joback(mol);
  if (!j.ok) throw new Error(j.problems[0].text);
  if (!j.cp) throw new Error('No heat-capacity estimate for this molecule; give Cp yourself.');
  const T = Array.from({ length: 11 }, (_, i) => 300 + 50 * i), ata = [[0, 0, 0], [0, 0, 0], [0, 0, 0]], atb = [0, 0, 0];
  for (const t of T) { const row = [1, t, t * t], y = j.cp(t) / 8.314462618; for (let a = 0; a < 3; a++) { atb[a] += row[a] * y; for (let b = 0; b < 3; b++) ata[a][b] += row[a] * row[b]; } }
  const k = [1, 1e3, 1e6]; for (let a = 0; a < 3; a++) { atb[a] /= k[a]; for (let b = 0; b < 3; b++) ata[a][b] /= k[a] * k[b]; }
  const co = solveLinear(ata, atb).map((v, i) => v / k[i]);
  const maxErr = Math.max(...T.map((t) => Math.abs((co[0] + co[1] * t + co[2] * t * t) * 8.314462618 - j.cp(t)) / j.cp(t)));
  const spec = { id, name: name || id, formula: formula(mol), MW: molarMass(mol), Tc: j.Tc, Pc: j.Pc, omega: j.omega, Tb: j.Tb, hf: j.Hf * 1000, gf: j.Gf * 1000, cp: { A: co[0], B: co[1], C: co[2], D: 0 }, src: 'Joback estimate (' + smiles + '), NOT confirmed', estimated: true, cpFitMaxError: maxErr };
  if (define) defineComponent(spec);
  return spec;
}

In detail

Method
Joback and Reid (1987) group sums and Edmister's acentric factor, with the formulas quoted in the file's SOURCES; a quadratic heat-capacity fit by scaled normal equations.
Number types
64-bit numbers. Atomic weights of four to six significant digits.
Limits
A molecule with a group that is not in the table gets no estimate and a problem naming the group. The review state of the table is 'not confirmed'.

Approach: 64-bit, stated limit

The arithmetic is 64-bit, but the method or the data sets the accuracy: a solver tolerance, a mesh, a step size, a datum shift, a table. The limit is stated where the result is shown.

Every policy that takes this approach