Skill Tracer
Skills › Scientific Agent Skills › uncertainty-and-units › domain-conversions.md

domain-conversions.md

204 lines · skills/uncertainty-and-units/references/domain-conversions.md at 5f2bc03

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1# Domain conversions and dimensional blind spots
2
3Values below were produced with pint 0.26.1 and SciPy 1.18.1 (CODATA 2022). Anything
4marked *exact* is fixed by definition and carries zero uncertainty.
5

Domain conversions and dimensional blind spots

Values below were produced with pint 0.26.1 and SciPy 1.18.1 (CODATA 2022). Anything marked exact is fixed by definition and carries zero uncertainty.

Raw lines 1 to 5
6## Dimensional analysis does not catch these
7
8Two quantities with the same dimensions convert freely, whether or not the conversion
9means anything.
11| Pair | Shared dimension | What pint does | Why it matters |
12| --- | --- | --- | --- |
13| gray and sievert | L²T⁻² | converts 1:1, silently | equivalent/effective dose uses radiation/tissue weighting; a unit conversion cannot supply these factors |
14| newton-metre and joule | ML²T⁻² | converts 1:1, silently | torque is a vector product, energy a scalar; adding them is meaningless |
15| hertz and becquerel | T⁻¹ | converts 1:1, silently | one is periodic, the other stochastic |
16| radian and dimensionless | none | radians vanish | `sin(x)` needs radians; a degrees value that lost its unit is silently wrong |
17| mol/L and mol/kg | different | raises | molarity and molality are genuinely different quantities |
18| mg/L and ppm | different | raises | equal only for dilute aqueous solutions near 1 g/mL |
20The last two raise because they *are* dimensionally distinct. The first four are the
21dangerous ones: no tool will warn you.

Dimensional analysis does not catch these

Two quantities with the same dimensions convert freely, whether or not the conversion means anything.

PairShared dimensionWhat pint doesWhy it matters
gray and sievertL²T⁻²converts 1:1, silentlyequivalent/effective dose uses radiation/tissue weighting; a unit conversion cannot supply these factors
newton-metre and jouleML²T⁻²converts 1:1, silentlytorque is a vector product, energy a scalar; adding them is meaningless
hertz and becquerelT⁻¹converts 1:1, silentlyone is periodic, the other stochastic
radian and dimensionlessnoneradians vanishsin(x) needs radians; a degrees value that lost its unit is silently wrong
mol/L and mol/kgdifferentraisesmolarity and molality are genuinely different quantities
mg/L and ppmdifferentraisesequal only for dilute aqueous solutions near 1 g/mL

The last two raise because they are dimensionally distinct. The first four are the dangerous ones: no tool will warn you.

Raw lines 6 to 22
23## Energy ladder
25Molecular science quotes the same energy in at least six units, three of which are
26per-mole and therefore need the Avogadro constant.
28| From | To | Factor |
29| --- | --- | --- |
30| 1 eV | kJ/mol | 96.48533212331002 |
31| 1 hartree | eV | 27.21138624598103 |
32| 1 hartree | kcal/mol | 627.5094740628942 |
33| 1 cm⁻¹ | eV | 1.2398419843320026e-4 |
34| 1 cm⁻¹ | K (as E/k_B) | 1.4387768775039336 |
35| k_B T at 298.15 K | eV | 0.02569257912108585 |
36| k_B T at 298.15 K | kJ/mol | 2.478957029602389 |
37| 1 cal (thermochemical) | J | 4.184 (exact) |
38| 1 cal_it | J | 4.1868 (exact) |
40Two traps. First, **per-mole and per-particle units are not dimensionally
41interchangeable**: eV is an energy, kJ/mol is an energy per amount of substance, and the
42conversion needs N_A. Pint will refuse `Q(1, "eV").to("kJ/mol")` and accept
43`Q(1, "eV * N_A").to("kJ/mol")`. Second, **there are two calories** and a factor of
441.00067 between them; thermochemical is the default in chemistry, IT in engineering.
46```python
47Q(1, "eV * N_A").to("kJ/mol") # 96.48533212331002 kilojoule / mole
48Q(1, "1/cm").to("eV", "sp") # 0.00012398419843320026 electron_volt
49Q(298.15, "K").to("eV", "boltzmann") # 0.02569257912108585 electron_volt
50```

Energy ladder

Molecular science quotes the same energy in at least six units, three of which are per-mole and therefore need the Avogadro constant.

FromToFactor
1 eVkJ/mol96.48533212331002
1 hartreeeV27.21138624598103
1 hartreekcal/mol627.5094740628942
1 cm⁻¹eV1.2398419843320026e-4
1 cm⁻¹K (as E/k_B)1.4387768775039336
k_B T at 298.15 KeV0.02569257912108585
k_B T at 298.15 KkJ/mol2.478957029602389
1 cal (thermochemical)J4.184 (exact)
1 cal_itJ4.1868 (exact)

Two traps. First, per-mole and per-particle units are not dimensionally interchangeable: eV is an energy, kJ/mol is an energy per amount of substance, and the conversion needs N_A. Pint will refuse Q(1, "eV").to("kJ/mol") and accept Q(1, "eV * N_A").to("kJ/mol"). Second, there are two calories and a factor of 1.00067 between them; thermochemical is the default in chemistry, IT in engineering.

Q(1, "eV * N_A").to("kJ/mol")            # 96.48533212331002 kilojoule / mole
Q(1, "1/cm").to("eV", "sp")              # 0.00012398419843320026 electron_volt
Q(298.15, "K").to("eV", "boltzmann")     # 0.02569257912108585 electron_volt
Raw lines 23 to 51
52## Spectroscopy
54Wavelength, frequency, wavenumber, and photon energy are related by physics, not by
55dimensional analysis, and wavelength-frequency/energy relations are *reciprocal* — an uncertainty does not
56convert by the same factor as the value.
58```python
59Q(532, "nm").to("THz", "sp") # 563.5196578947367 terahertz
60Q(532, "nm").to("1/cm", "sp") # 18796.992481203004 / centimeter
61Q(532, "nm").to("eV", "sp") # 2.3305300457368467 electron_volt
62```
64Because E = hc/λ, a constant wavelength uncertainty becomes an energy uncertainty that
65scales as 1/λ². Propagate through the relation, do not scale the uncertainty by the
66value's conversion factor. `scripts/convert_units.py --uncertainty` does this with the
67conversion's local derivative.
69The `sp` context assumes vacuum unless given a refractive index. With `n=1.33`,
70532 nm is interpreted as the wavelength **in that medium**, giving 423.7 THz.
71A photon crossing an interface retains its frequency; its wavelength changes. Do not
72apply n to a vacuum wavelength merely because the experiment contains water.

Spectroscopy

Wavelength, frequency, wavenumber, and photon energy are related by physics, not by dimensional analysis, and wavelength-frequency/energy relations are reciprocal — an uncertainty does not convert by the same factor as the value.

Q(532, "nm").to("THz", "sp")     # 563.5196578947367 terahertz
Q(532, "nm").to("1/cm", "sp")    # 18796.992481203004 / centimeter
Q(532, "nm").to("eV", "sp")      # 2.3305300457368467 electron_volt

Because E = hc/λ, a constant wavelength uncertainty becomes an energy uncertainty that scales as 1/λ². Propagate through the relation, do not scale the uncertainty by the value's conversion factor. scripts/convert_units.py --uncertainty does this with the conversion's local derivative.

The sp context assumes vacuum unless given a refractive index. With n=1.33, 532 nm is interpreted as the wavelength in that medium, giving 423.7 THz. A photon crossing an interface retains its frequency; its wavelength changes. Do not apply n to a vacuum wavelength merely because the experiment contains water.

Raw lines 52 to 73
Runs code scripts/convert_units.py :66
74## Concentration
76| Quantity | Unit | Depends on |
77| --- | --- | --- |
78| Molarity | mol/L | temperature, through solution volume |
79| Molality | mol/kg solvent | composition and solvent basis, not thermal expansion alone |
80| Mole fraction | dimensionless | composition and entity definition |
81| Mass fraction, ppm(m/m) | dimensionless | composition and mass basis |
82| Volume fraction, ppm(v/v) | dimensionless | temperature |
83| Mass concentration | mg/L, g/L | temperature |
85"ppm" alone is ambiguous: mass/mass, volume/volume, and mol/mol differ by the ratio of
86densities or molar masses. In environmental water chemistry ppm conventionally means
87mg/L, which equals mg/kg only because dilute water is close to 1 kg/L. In gas analysis
88it conventionally means volume/volume. State which.
90Pint treats `ppm` and `percent` as plain dimensionless scale factors (1e-6 and 0.01),
91which is right for arithmetic and gives no protection against mixing the three senses.
92Defining `ureg.define("ppm_v = 1e-6 = ppmv")` only adds an alias-like scale; it
93provides **no** dimensional protection. To enforce semantic separation, use a custom
94base dimension (for example `volume_fraction_kind = [volume_fraction_kind]`, then
95`ppm_v = 1e-6 * volume_fraction_kind`) and explicit adapters to physical dimensionless
96fractions. This is an application convention, not a new SI dimension.
98Mass and amount of substance need the `chemistry` context and a molar mass:
100```python
101Q(1, "g").to("mol", "chemistry", mw=Q(180.156, "g/mol")) # 0.005550744909966918 mole
102```

Concentration

QuantityUnitDepends on
Molaritymol/Ltemperature, through solution volume
Molalitymol/kg solventcomposition and solvent basis, not thermal expansion alone
Mole fractiondimensionlesscomposition and entity definition
Mass fraction, ppm(m/m)dimensionlesscomposition and mass basis
Volume fraction, ppm(v/v)dimensionlesstemperature
Mass concentrationmg/L, g/Ltemperature

"ppm" alone is ambiguous: mass/mass, volume/volume, and mol/mol differ by the ratio of densities or molar masses. In environmental water chemistry ppm conventionally means mg/L, which equals mg/kg only because dilute water is close to 1 kg/L. In gas analysis it conventionally means volume/volume. State which.

Pint treats ppm and percent as plain dimensionless scale factors (1e-6 and 0.01), which is right for arithmetic and gives no protection against mixing the three senses. Defining ureg.define("ppm_v = 1e-6 = ppmv") only adds an alias-like scale; it provides no dimensional protection. To enforce semantic separation, use a custom base dimension (for example volume_fraction_kind = [volume_fraction_kind], then ppm_v = 1e-6 * volume_fraction_kind) and explicit adapters to physical dimensionless fractions. This is an application convention, not a new SI dimension.

Mass and amount of substance need the chemistry context and a molar mass:

Q(1, "g").to("mol", "chemistry", mw=Q(180.156, "g/mol"))   # 0.005550744909966918 mole
Raw lines 74 to 103
104## Pressure
106| From | To Pa | Note |
107| --- | --- | --- |
108| 1 atm | 101325 | exact |
109| 1 bar | 100000 | exact |
110| 1 torr | 133.32236842105263 | atm/760, exact by definition |
111| 1 psi | 6894.7572931683635 | |
112| 1 mmHg | 133.322387415 | *not* identical to torr, differs by about 1.4e-7 relatively |
114**Gauge and absolute pressure need an explicit reference pressure.** Pint can define
115custom offset units, but a static offset cannot infer the actual ambient pressure. "psig" and "psia" have the same dimensions; a gauge reading needs the
116ambient pressure added before it can be used in a gas law. Vacuum work, autoclave
117protocols, and chromatography backpressures are where this bites.

Pressure

FromTo PaNote
1 atm101325exact
1 bar100000exact
1 torr133.32236842105263atm/760, exact by definition
1 psi6894.7572931683635
1 mmHg133.322387415not identical to torr, differs by about 1.4e-7 relatively

Gauge and absolute pressure need an explicit reference pressure. Pint can define custom offset units, but a static offset cannot infer the actual ambient pressure. "psig" and "psia" have the same dimensions; a gauge reading needs the ambient pressure added before it can be used in a gas law. Vacuum work, autoclave protocols, and chromatography backpressures are where this bites.

Raw lines 104 to 118
119## Radiation, magnetism, rotation
121```python
122Q(1, "Ci").to("Bq") # 37000000000.0 becquerel (exact by definition)
123Q(1, "gauss").to("T", "Gaussian") # 9.999999999338245e-05 tesla
124Q(1, "rpm").to("rad/s") # 0.10471975511965977 radian / second
125```
127Gauss fails *without* the Gaussian context: CGS electromagnetic units have different
128dimensions from SI ones, not merely different scales. Magnetic field strength H (A/m,
129oersted) and magnetic flux density B (T, gauss) are distinct quantities that literature
130routinely calls "the field".

Radiation, magnetism, rotation

Q(1, "Ci").to("Bq")               # 37000000000.0 becquerel   (exact by definition)
Q(1, "gauss").to("T", "Gaussian") # 9.999999999338245e-05 tesla
Q(1, "rpm").to("rad/s")           # 0.10471975511965977 radian / second

Gauss fails without the Gaussian context: CGS electromagnetic units have different dimensions from SI ones, not merely different scales. Magnetic field strength H (A/m, oersted) and magnetic flux density B (T, gauss) are distinct quantities that literature routinely calls "the field".

Raw lines 119 to 131
132## Mass spectrometry
134The unified atomic mass unit and the dalton are the same thing:
1351 Da = 1.66053906892e-27 kg (CODATA 2022, relative standard uncertainty 3.1e-10).
137m/z is conventionally reported as a dimensionless number: the ratio of mass in daltons
138to charge number. The thomson (Th) exists but is not SI and is rarely used. Treating m/z
139as a mass is wrong for any ion with z > 1, which is most of a protein spectrum.

Mass spectrometry

The unified atomic mass unit and the dalton are the same thing: 1 Da = 1.66053906892e-27 kg (CODATA 2022, relative standard uncertainty 3.1e-10).

m/z is conventionally reported as a dimensionless number: the ratio of mass in daltons to charge number. The thomson (Th) exists but is not SI and is rarely used. Treating m/z as a mass is wrong for any ion with z > 1, which is most of a protein spectrum.

Raw lines 132 to 140
141## Logarithmic quantities
143pH, pKa, dB, and magnitudes are logarithms of ratios. They do not add, average, or
144propagate like ordinary quantities:
146- the arithmetic mean of pH 5 and 7 is pH 6 **on the pH scale**. If the target is
147 mean hydrogen-ion activity, transform to activity first. Mixing two solutions needs
148 volume, activity coefficients, acid-base equilibria and buffering, not merely averaging;
149- pH is minus log10 of hydrogen-ion activity, not generally concentration. Locally,
150 `u(a)/a = ln(10)*u(pH)`; large uncertainties give asymmetric transformed intervals;
151- adding two dB quantities multiplies the underlying linear quantities (see
152 `pint-recipes.md`);
153- decibel scales differ by reference: dBm references 1 mW, dBW references 1 W, dBV
154 references 1 V, and dB alone references nothing until you say so.

Logarithmic quantities

pH, pKa, dB, and magnitudes are logarithms of ratios. They do not add, average, or propagate like ordinary quantities:

  • the arithmetic mean of pH 5 and 7 is pH 6 on the pH scale. If the target is mean hydrogen-ion activity, transform to activity first. Mixing two solutions needs volume, activity coefficients, acid-base equilibria and buffering, not merely averaging;
  • pH is minus log10 of hydrogen-ion activity, not generally concentration. Locally, u(a)/a = ln(10)*u(pH); large uncertainties give asymmetric transformed intervals;
  • adding two dB quantities multiplies the underlying linear quantities (see pint-recipes.md);
  • decibel scales differ by reference: dBm references 1 mW, dBW references 1 W, dBV references 1 V, and dB alone references nothing until you say so.
Raw lines 141 to 155
156## Temperature
158Kelvin and rankine are ratio scales and behave normally. Celsius and Fahrenheit are
159interval scales: 20 degC is not "twice" 10 degC, and their differences live in
160`delta_degC` / `delta_degF`. See `pint-recipes.md` for what pint permits.
162Absolute zero is exactly 273.15 K below 0 degC — `scipy.constants.zero_Celsius`.

Temperature

Kelvin and rankine are ratio scales and behave normally. Celsius and Fahrenheit are interval scales: 20 degC is not "twice" 10 degC, and their differences live in delta_degC / delta_degF. See pint-recipes.md for what pint permits.

Absolute zero is exactly 273.15 K below 0 degC — scipy.constants.zero_Celsius.

Raw lines 156 to 163
164## Constants: which values, and which uncertainties
166The 2019 SI redefinition fixed several constants **exactly**, so their relative standard
167uncertainty is zero and no future CODATA release will change them:
169| Constant | Exact value |
170| --- | --- |
171| speed of light in vacuum, c | 299792458 m/s |
172| Planck constant, h | 6.62607015e-34 J/Hz |
173| elementary charge, e | 1.602176634e-19 C |
174| Boltzmann constant, k | 1.380649e-23 J/K |
175| Avogadro constant, N_A | 6.02214076e23 /mol |
177These are five of the seven SI defining constants; the cesium frequency and luminous
178efficacy complete the set. Quantities derived only from exact defining constants are
179also exact (for example R = N_A*k and the Faraday constant N_A*e). Electron mass,
180the gravitational constant, fine-structure constant, and Rydberg constant are measured.
181Check each uncertainty rather than declaring all other constants measured.
183```python
184import scipy.constants as constants
186constants.value("electron mass") # 9.1093837139e-31
187constants.unit("electron mass") # kg
188constants.precision("electron mass") # 3.07e-10 relative standard uncertainty
189constants.precision("Planck constant") # 0.0 exact by definition
190```
192`scipy.constants` in SciPy 1.18.1 defaults to **CODATA 2022**; SciPy 1.11 and earlier
193served CODATA 2018; SciPy 1.15 introduced the CODATA 2022 data. Hard-coding a constant pins you to whichever release you copied it
194from and discards its uncertainty entirely. `scripts/audit_units.py` flags literals that
195match a known constant (`CONST001`).
197Note also that `constants.precision` returns a *relative* standard uncertainty. The
198absolute standard uncertainty is `abs(value) * precision`. Nominal constants used in
199Pint definitions do not automatically carry the measured constant's uncertainty.
201Primary definitions reviewed 2026-10-01: [BIPM SI Brochure, updated 2026](https://www.bipm.org/en/publications/si-brochure),
202[NIST CODATA 2022](https://physics.nist.gov/cuu/Constants/),
203[IUPAC pH](https://goldbook.iupac.org/terms/view/P04524), and
204[Pint 0.26.1 definitions](https://github.com/hgrecco/pint/blob/0.26.1/pint/default_en.txt).

Constants: which values, and which uncertainties

The 2019 SI redefinition fixed several constants exactly, so their relative standard uncertainty is zero and no future CODATA release will change them:

ConstantExact value
speed of light in vacuum, c299792458 m/s
Planck constant, h6.62607015e-34 J/Hz
elementary charge, e1.602176634e-19 C
Boltzmann constant, k1.380649e-23 J/K
Avogadro constant, N_A6.02214076e23 /mol

These are five of the seven SI defining constants; the cesium frequency and luminous efficacy complete the set. Quantities derived only from exact defining constants are also exact (for example R = N_A*k and the Faraday constant N_A*e). Electron mass, the gravitational constant, fine-structure constant, and Rydberg constant are measured. Check each uncertainty rather than declaring all other constants measured.

import scipy.constants as constants

constants.value("electron mass")       # 9.1093837139e-31
constants.unit("electron mass")        # kg
constants.precision("electron mass")   # 3.07e-10  relative standard uncertainty
constants.precision("Planck constant") # 0.0       exact by definition

scipy.constants in SciPy 1.18.1 defaults to CODATA 2022; SciPy 1.11 and earlier served CODATA 2018; SciPy 1.15 introduced the CODATA 2022 data. Hard-coding a constant pins you to whichever release you copied it from and discards its uncertainty entirely. scripts/audit_units.py flags literals that match a known constant (CONST001).

Note also that constants.precision returns a relative standard uncertainty. The absolute standard uncertainty is abs(value) * precision. Nominal constants used in Pint definitions do not automatically carry the measured constant's uncertainty.

Primary definitions reviewed 2026-10-01: BIPM SI Brochure, updated 2026, NIST CODATA 2022, IUPAC pH, and Pint 0.26.1 definitions.

Raw lines 164 to 204
Runs code scripts/audit_units.py :194