Skill Tracer
Skills › Scientific Agent Skills › uncertainty-and-units

uncertainty-and-units

skill · 415 lines · MIT · skills/uncertainty-and-units/SKILL.md

The facts

Its description

Tracks physical units and propagates measurement uncertainty in scientific calculations using pint and uncertainties. Use for unit conversion and dimensional checking, GUM uncertainty budgets, Type A and Type B evaluation, coverage factors and expanded uncertainty, Monte Carlo propagation, significant-figure and plus-minus reporting, error propagation through curve fits, CODATA constants, auditing Python code for stripped units or broken uncertainty propagation, and order-of-magnitude plausibility checks using dimensionless groups (Reynolds, Peclet, Damkohler, Knudsen, Biot, Womersley), characteristic scales such as diffusion time or Debye length, and observed magnitude ranges. Trigger on "is this number physically reasonable", "sanity check these units", "what regime is this flow in", or a result that looks off by orders of magnitude.

The description the agent reads to decide when to use it.

From its settingsline 3

Tools it may use, or may not

allowed-tools: Read Write Edit Bash

Tools it may use while it runs without asking you first.

From its settingsline 6

Runs scripts

bin/activate, python skills/uncertainty-and-units/scripts/propagate_uncertainty.py, python skills/uncertainty-and-units/scripts/uncertainty_budget.py, python skills/uncertainty-and-units/scripts/format_result.py, python skills/uncertainty-and-units/scripts/convert_units.py (first 5 of 16 lines)

Scripts the text names. Found by matching words, so treat them as pointers.

Found by matching wordsline 48 line 154 line 155 line 156 line 157

Files it mentions

references/plausibility-scales.md, references/gum-methodology.md, references/pint-recipes.md, references/uncertainties-recipes.md, references/domain-conversions.md (first 5 of 8 lines)

Files the text names, such as ones to read or write. Found by matching words, so treat them as pointers.

Found by matching wordsline 324 line 360 line 363 line 366 line 369

Licence

MIT, from the skill file

The terms its author shares it under, which let it be shown here.

From its settingsline 4 licence text

Other settings

compatibility: Requires Python 3.12+. The numeric CLIs need pint, uncertainties, NumPy, and SciPy; the static auditor is standard-library only. All bundled tooling runs locally with no network access. metadata: version: "1.3" last-reviewed: "2026-10-01" skill-author: K-Dense Inc.

More settings at the top of the file, shown as written.

From its settingsline 5 line 7
Other files in this skill (14) Close

The text

Open on GitHub

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

1---
2name: uncertainty-and-units
3description: Tracks physical units and propagates measurement uncertainty in scientific calculations using pint and uncertainties. Use for unit conversion and dimensional checking, GUM uncertainty budgets, Type A and Type B evaluation, coverage factors and expanded uncertainty, Monte Carlo propagation, significant-figure and plus-minus reporting, error propagation through curve fits, CODATA constants, auditing Python code for stripped units or broken uncertainty propagation, and order-of-magnitude plausibility checks using dimensionless groups (Reynolds, Peclet, Damkohler, Knudsen, Biot, Womersley), characteristic scales such as diffusion time or Debye length, and observed magnitude ranges. Trigger on "is this number physically reasonable", "sanity check these units", "what regime is this flow in", or a result that looks off by orders of magnitude.
4license: MIT
5compatibility: Requires Python 3.12+. The numeric CLIs need pint, uncertainties, NumPy, and SciPy; the static auditor is standard-library only. All bundled tooling runs locally with no network access.
6allowed-tools: Read Write Edit Bash
7metadata:
8 version: "1.3"
9 last-reviewed: "2026-10-01"
10 skill-author: K-Dense Inc.
11---
13# Uncertainty and units

Uncertainty and units

Raw lines 1 to 14
15## Scope
17Use this skill whenever a calculation carries physical units or a reported number needs
18an uncertainty. Concretely:
20- converting between units, including conversions that need a physical context
21 (wavelength to photon energy, mass to amount of substance, energy to temperature);
22- propagating uncertainty through a measurement model, with or without correlated inputs;
23- building a GUM uncertainty budget from calibration certificates, specifications, and
24 repeatability data;
25- choosing a coverage factor and deciding whether `k = 2` is defensible;
26- rounding and writing a result so a reader knows what the `±` means;
27- extracting parameter uncertainties from a curve fit without discarding correlations;
28- reviewing existing analysis code for silent unit and uncertainty defects;
29- checking that a dimensionally consistent answer is also physically possible — the
30 order of magnitude, the dimensionless group, and the regime it implies.
32This skill covers the metrology and the two libraries that implement it. It does not
33cover statistical inference, model selection, or study design — see `statistical-analysis`,

Scope

Use this skill whenever a calculation carries physical units or a reported number needs an uncertainty. Concretely:

  • converting between units, including conversions that need a physical context (wavelength to photon energy, mass to amount of substance, energy to temperature);
  • propagating uncertainty through a measurement model, with or without correlated inputs;
  • building a GUM uncertainty budget from calibration certificates, specifications, and repeatability data;
  • choosing a coverage factor and deciding whether k = 2 is defensible;
  • rounding and writing a result so a reader knows what the ± means;
  • extracting parameter uncertainties from a curve fit without discarding correlations;
  • reviewing existing analysis code for silent unit and uncertainty defects;
  • checking that a dimensionally consistent answer is also physically possible — the order of magnitude, the dimensionless group, and the regime it implies.

This skill covers the metrology and the two libraries that implement it. It does not cover statistical inference, model selection, or study design — see statistical-analysis, statistical-power, and experimental-design.

Raw lines 15 to 35
Names a skill statistical-analysis :33
Names a skill statistical-power :34
Names a skill experimental-design :34
36## Current release and installation
38Verified 2026-10-01:
40- **pint 0.26.1**, released 2026-09-10; requires Python 3.12+.
41- **uncertainties 3.2.3**, released 2025-04-21; requires Python 3.8+.
42- **NumPy 2.5.3** and **SciPy 1.18.1**; both require Python 3.12+.
43- `scipy.constants` in SciPy 1.18.1 serves **CODATA 2022**. SciPy 1.11 and earlier
44 served CODATA 2018; the switch to CODATA 2022 occurred in SciPy 1.15.
46```bash
47uv venv --python 3.13
48source .venv/bin/activate
49uv pip install "pint==0.26.1" "uncertainties==3.2.3" "numpy==2.5.3" "scipy==1.18.1"
50```
52`pint-pandas` and `pint-xarray` add unit-aware columns and arrays and are separate
53installs.

Current release and installation

Verified 2026-10-01:

  • pint 0.26.1, released 2026-09-10; requires Python 3.12+.
  • uncertainties 3.2.3, released 2025-04-21; requires Python 3.8+.
  • NumPy 2.5.3 and SciPy 1.18.1; both require Python 3.12+.
  • scipy.constants in SciPy 1.18.1 serves CODATA 2022. SciPy 1.11 and earlier served CODATA 2018; the switch to CODATA 2022 occurred in SciPy 1.15.
uv venv --python 3.13
source .venv/bin/activate
uv pip install "pint==0.26.1" "uncertainties==3.2.3" "numpy==2.5.3" "scipy==1.18.1"

pint-pandas and pint-xarray add unit-aware columns and arrays and are separate installs.

Raw lines 36 to 54
Runs code bin/activate :48
55## Non-negotiable workflow
571. **Attach units at input and strip them only at output.** Convert at function
58 boundaries with `ureg.wraps` or `m_as("unit")`, never mid-calculation.
592. **Write the measurement model explicitly** before computing anything, including
60 corrections whose estimated value is zero. A correction left out of the model leaves
61 its uncertainty out of the budget.
623. **Give every input four things**: an estimate, a standard uncertainty, the
63 distribution the uncertainty came from, and its degrees of freedom.
644. **Convert Type B statements with the right divisor.** A certificate's expanded
65 uncertainty divides by its stated `k`; rectangular limits divide by `sqrt(3)`.
665. **Identify correlations before combining.** A shared calibration, instrument correction, or fit can induce correlation;
67 quantify shared components instead of assuming every pair is correlated.
686. **Compute sensitivity coefficients**, and read the budget from `c_i * u(x_i)` rather
69 than from the raw uncertainties.
707. **Check the linearization.** Compare propagated distributions with the GUM interval when in doubt.
71 Establish Monte Carlo numerical stability before a JCGM 101 clause 8 claim;
72 neither method validates the measurement model or the assigned input PDFs.
738. **Justify coverage.** A Student-t factor from effective degrees of freedom
74 is an approximation requiring suitable distributional assumptions.
759. **Round the uncertainty first, then the value to the same decimal place.**
7610. **State what the `±` is** — standard or expanded, with `k`, the coverage probability,
77 and the method.
7811. **Sanity-check the magnitude before reporting.** A dimensionally consistent result can
79 still be impossible. Compare it against a known scale or a dimensionless group, and
80 confirm every assumption you relied on still holds in that regime.

Non-negotiable workflow

  1. Attach units at input and strip them only at output. Convert at function boundaries with ureg.wraps or m_as("unit"), never mid-calculation.
  2. Write the measurement model explicitly before computing anything, including corrections whose estimated value is zero. A correction left out of the model leaves its uncertainty out of the budget.
  3. Give every input four things: an estimate, a standard uncertainty, the distribution the uncertainty came from, and its degrees of freedom.
  4. Convert Type B statements with the right divisor. A certificate's expanded uncertainty divides by its stated k; rectangular limits divide by sqrt(3).
  5. Identify correlations before combining. A shared calibration, instrument correction, or fit can induce correlation; quantify shared components instead of assuming every pair is correlated.
  6. Compute sensitivity coefficients, and read the budget from c_i * u(x_i) rather than from the raw uncertainties.
  7. Check the linearization. Compare propagated distributions with the GUM interval when in doubt. Establish Monte Carlo numerical stability before a JCGM 101 clause 8 claim; neither method validates the measurement model or the assigned input PDFs.
  8. Justify coverage. A Student-t factor from effective degrees of freedom is an approximation requiring suitable distributional assumptions.
  9. Round the uncertainty first, then the value to the same decimal place.
  10. **State what the ± is** — standard or expanded, with k, the coverage probability, and the method.
  11. Sanity-check the magnitude before reporting. A dimensionally consistent result can still be impossible. Compare it against a known scale or a dimensionless group, and confirm every assumption you relied on still holds in that regime.
Raw lines 55 to 81
82## The failures this skill exists to prevent
84Each of the following runs without error and produces a plausible number.

The failures this skill exists to prevent

Each of the following runs without error and produces a plausible number.

Raw lines 82 to 85
86### A unit stripped at an unknown scale
88```python
89length = (12.7 * ureg.mm).magnitude # 12.7 -- of what?
90length = (12.7 * ureg.mm).m_as("m") # 0.0127 metres, stated
91```
93`.magnitude` returns whatever the quantity happened to be carrying. Name the unit at the
94point of extraction, every time.

A unit stripped at an unknown scale

length = (12.7 * ureg.mm).magnitude          # 12.7 -- of what?
length = (12.7 * ureg.mm).m_as("m")          # 0.0127 metres, stated

.magnitude returns whatever the quantity happened to be carrying. Name the unit at the point of extraction, every time.

Raw lines 86 to 95
96### Offset temperature arithmetic
98```python
99Q(20, "degC") + Q(5, "degC") # OffsetUnitCalculusError -- correctly refused
100Q(20, "degC") + Q(5, "delta_degC") # 25 degree_Celsius
101Q(25, "degC") - Q(20, "degC") # 5 delta_degree_Celsius
102```
104Celsius and Fahrenheit are interval scales. An uncertainty on a temperature is always a
105difference and belongs in a `delta_` unit: converting `20 ± 0.5 degC` to Fahrenheit
106gives `68 degF ± 0.9 delta_degF`, two different conversions on one line.

Offset temperature arithmetic

Q(20, "degC") + Q(5, "degC")     # OffsetUnitCalculusError -- correctly refused
Q(20, "degC") + Q(5, "delta_degC")   # 25 degree_Celsius
Q(25, "degC") - Q(20, "degC")        # 5 delta_degree_Celsius

Celsius and Fahrenheit are interval scales. An uncertainty on a temperature is always a difference and belongs in a delta_ unit: converting 20 ± 0.5 degC to Fahrenheit gives 68 degF ± 0.9 delta_degF, two different conversions on one line.

Raw lines 96 to 107
108### Logarithmic units that add by multiplying
110```python
111Q(10, "dBm") + Q(10, "dBm") # 0.0001 kilogram**2 * meter**4 / second**6
112```
114That is 10 mW × 10 mW, not 20 mW and not 13 dBm. Nothing raises. Convert to a linear
115unit before any arithmetic.

Logarithmic units that add by multiplying

Q(10, "dBm") + Q(10, "dBm")   # 0.0001 kilogram**2 * meter**4 / second**6

That is 10 mW × 10 mW, not 20 mW and not 13 dBm. Nothing raises. Convert to a linear unit before any arithmetic.

Raw lines 108 to 116
117### A correlation destroyed by a round trip
119```python
120x = ufloat(1.0, 0.1)
121x - x # 0.0+/-0
122x - ufloat(x.nominal_value, x.std_dev) # 0.00+/-0.14
123```
125Rebuilding a variable from its nominal value and standard deviation creates an
126independent variable. So does any serialization that passes through a pair of floats.
127Use `correlated_values(values, covariance_matrix)` to rebuild a correlated set.

A correlation destroyed by a round trip

x = ufloat(1.0, 0.1)
x - x                                     # 0.0+/-0
x - ufloat(x.nominal_value, x.std_dev)    # 0.00+/-0.14

Rebuilding a variable from its nominal value and standard deviation creates an independent variable. So does any serialization that passes through a pair of floats. Use correlated_values(values, covariance_matrix) to rebuild a correlated set.

Raw lines 117 to 128
129### A covariance matrix silently rescaled
131```python
132popt, pcov = curve_fit(f, x, y, sigma=sigma) # default
133popt, pcov = curve_fit(f, x, y, sigma=sigma, absolute_sigma=True)
134```
136The default rescales `pcov` by the reduced chi-square. Relative weights from `sigma` still affect the fit and covariance: equality with an unweighted fit holds for constant `sigma`, not generally for heteroscedastic inputs. On
137one synthetic straight-line fit the two give `[0.0364, 0.2154]` and `[0.0477, 0.2820]` —
138a 31% difference. Pass `absolute_sigma=True` whenever `sigma` holds real standard
139uncertainties.

A covariance matrix silently rescaled

popt, pcov = curve_fit(f, x, y, sigma=sigma)                        # default
popt, pcov = curve_fit(f, x, y, sigma=sigma, absolute_sigma=True)

The default rescales pcov by the reduced chi-square. Relative weights from sigma still affect the fit and covariance: equality with an unweighted fit holds for constant sigma, not generally for heteroscedastic inputs. On one synthetic straight-line fit the two give [0.0364, 0.2154] and [0.0477, 0.2820] — a 31% difference. Pass absolute_sigma=True whenever sigma holds real standard uncertainties.

Raw lines 129 to 140
141### A linearization that was never checked
143For `y = x²` with `x = 1.0 ± 0.5`, the GUM framework gives `y = 1.0`, `u_c = 1.0`, and a
14495% interval of `[-0.96, 2.96]` — mostly negative, for a squared quantity. Monte Carlo
145gives a mean of 1.25, `u_c = 1.06`, and a shortest 95% interval of `[0, 3.32]`. Nothing
146in a linear-propagation library will tell you this happened.

A linearization that was never checked

For y = x² with x = 1.0 ± 0.5, the GUM framework gives y = 1.0, u_c = 1.0, and a 95% interval of [-0.96, 2.96] — mostly negative, for a squared quantity. Monte Carlo gives a mean of 1.25, u_c = 1.06, and a shortest 95% interval of [0, 3.32]. Nothing in a linear-propagation library will tell you this happened.

Raw lines 141 to 147
148## Bundled local CLIs
150All helpers run offline, reject URLs and symlinks, bound their inputs, write output
151atomically with private permissions, and refuse to overwrite without `--force`.
153```bash
154python skills/uncertainty-and-units/scripts/propagate_uncertainty.py --help
155python skills/uncertainty-and-units/scripts/uncertainty_budget.py --help
156python skills/uncertainty-and-units/scripts/format_result.py --help
157python skills/uncertainty-and-units/scripts/convert_units.py --help
158python skills/uncertainty-and-units/scripts/audit_units.py --help
159python skills/uncertainty-and-units/scripts/check_plausibility.py --help
160```

Bundled local CLIs

All helpers run offline, reject URLs and symlinks, bound their inputs, write output atomically with private permissions, and refuse to overwrite without --force.

python skills/uncertainty-and-units/scripts/propagate_uncertainty.py --help
python skills/uncertainty-and-units/scripts/uncertainty_budget.py --help
python skills/uncertainty-and-units/scripts/format_result.py --help
python skills/uncertainty-and-units/scripts/convert_units.py --help
python skills/uncertainty-and-units/scripts/audit_units.py --help
python skills/uncertainty-and-units/scripts/check_plausibility.py --help
Raw lines 148 to 161
Runs code python skills/uncertainty-and-units/scripts/propagate_uncertainty.py :154
Runs code python skills/uncertainty-and-units/scripts/uncertainty_budget.py :155
Runs code python skills/uncertainty-and-units/scripts/format_result.py :156
Runs code python skills/uncertainty-and-units/scripts/convert_units.py :157
Runs code python skills/uncertainty-and-units/scripts/audit_units.py :158
Runs code python skills/uncertainty-and-units/scripts/check_plausibility.py :159
162### propagate_uncertainty.py
164Runs both propagation methods on the same numerical model and compares interval
165endpoints. This fixed-trial diagnostic does **not** implement the adaptive stabilization
166in JCGM 101 7.9/8.2 and never labels the GUM result fully validated.
168```bash
169python skills/uncertainty-and-units/scripts/propagate_uncertainty.py \
170 --expression "m / (pi * (d / 2) ** 2 * h)" \
171 --variable "m=250.0,0.05" \
172 --variable "d=2.0,0.002,rectangular" \
173 --variable "h=4.0,0.005,rectangular" \
174 --measurand density --unit "g/cm3" --format markdown
175```
177Each `--variable` is `name=value,standard_uncertainty[,distribution[,dof]]`, where the
178distribution is `normal`, `rectangular`, `triangular`, `arcsine`, or `exact` and controls
179Monte Carlo sampling only. Here mass is in g and lengths in cm: `--unit` is
180a report label, **not** dimensional validation or conversion. JSON variable units are
181also labels; convert inputs to a consistent numerical model first. Correlations go in as `--correlation "a,b=0.9"`. A JSON
182`--spec` file holds the same model for anything long-lived.
183The joint normal sampler supports positive semidefinite correlation matrices, including
184perfect correlations. Other joint distributions need a separate sampler. Correlated
185finite-dof inputs are refused because independent-input Welch-Satterthwaite is invalid.
186For independent finite-dof inputs the CLI's MC PDFs remain fixed: `dof` affects the
187GUM factor only, not MC sampling. A small-sample t input needs a separate justified model.
189The expression is parsed into an abstract syntax tree and reduced by an explicit walk
190over `+ - * / **` and a fixed list of functions. It is never compiled or executed.
192The report gives the estimate, `u_c`, sensitivity coefficients, the budget in percent,
193effective degrees of freedom, `k`, `U`, both Monte Carlo coverage intervals, and the
194endpoint-agreement diagnostic. Repeat/increase sampling and verify model/PDF
195assumptions before deciding what to report.

propagate_uncertainty.py

Runs both propagation methods on the same numerical model and compares interval endpoints. This fixed-trial diagnostic does not implement the adaptive stabilization in JCGM 101 7.9/8.2 and never labels the GUM result fully validated.

python skills/uncertainty-and-units/scripts/propagate_uncertainty.py \
  --expression "m / (pi * (d / 2) ** 2 * h)" \
  --variable "m=250.0,0.05" \
  --variable "d=2.0,0.002,rectangular" \
  --variable "h=4.0,0.005,rectangular" \
  --measurand density --unit "g/cm3" --format markdown

Each --variable is name=value,standard_uncertainty[,distribution[,dof]], where the distribution is normal, rectangular, triangular, arcsine, or exact and controls Monte Carlo sampling only. Here mass is in g and lengths in cm: --unit is a report label, not dimensional validation or conversion. JSON variable units are also labels; convert inputs to a consistent numerical model first. Correlations go in as --correlation "a,b=0.9". A JSON --spec file holds the same model for anything long-lived. The joint normal sampler supports positive semidefinite correlation matrices, including perfect correlations. Other joint distributions need a separate sampler. Correlated finite-dof inputs are refused because independent-input Welch-Satterthwaite is invalid. For independent finite-dof inputs the CLI's MC PDFs remain fixed: dof affects the GUM factor only, not MC sampling. A small-sample t input needs a separate justified model.

The expression is parsed into an abstract syntax tree and reduced by an explicit walk over + - * / ** and a fixed list of functions. It is never compiled or executed.

The report gives the estimate, u_c, sensitivity coefficients, the budget in percent, effective degrees of freedom, k, U, both Monte Carlo coverage intervals, and the endpoint-agreement diagnostic. Repeat/increase sampling and verify model/PDF assumptions before deciding what to report.

Raw lines 162 to 196
Runs code python skills/uncertainty-and-units/scripts/propagate_uncertainty.py :169
197### uncertainty_budget.py
199Combines components stated the way certificates and data sheets state them.
201```bash
202python skills/uncertainty-and-units/scripts/uncertainty_budget.py --template > budget.json
203python skills/uncertainty-and-units/scripts/uncertainty_budget.py --spec budget.json --format markdown
204```
206Each component names a `distribution` that fixes its divisor — `expanded` divides by its
207`coverage_factor`, `rectangular` by `sqrt(3)`, `triangular` by `sqrt(6)`, `arcsine` by
208`sqrt(2)`, `normal` by 1 — with an optional `sensitivity`, `dof`, and `relative: true`.
209The tool assumes independent components, requires an explicit factor for `expanded`,
210and rejects nonzero `exact` components. It computes `u_c`, effective degrees of freedom, `k` from
211the t-distribution, and `U`, and warns when a Type A component has no degrees of
212freedom, when `nu_eff` is small enough that `k = 2` is wrong, when one component
213dominates, and when a Type B component declared `normal` is probably an undivided
214expanded uncertainty.

uncertainty_budget.py

Combines components stated the way certificates and data sheets state them.

python skills/uncertainty-and-units/scripts/uncertainty_budget.py --template > budget.json
python skills/uncertainty-and-units/scripts/uncertainty_budget.py --spec budget.json --format markdown

Each component names a distribution that fixes its divisor — expanded divides by its coverage_factor, rectangular by sqrt(3), triangular by sqrt(6), arcsine by sqrt(2), normal by 1 — with an optional sensitivity, dof, and relative: true. The tool assumes independent components, requires an explicit factor for expanded, and rejects nonzero exact components. It computes u_c, effective degrees of freedom, k from the t-distribution, and U, and warns when a Type A component has no degrees of freedom, when nu_eff is small enough that k = 2 is wrong, when one component dominates, and when a Type B component declared normal is probably an undivided expanded uncertainty.

Raw lines 197 to 215
Runs code python skills/uncertainty-and-units/scripts/uncertainty_budget.py :202
216### format_result.py
218```bash
219python skills/uncertainty-and-units/scripts/format_result.py \
220 --value 12.34567 --uncertainty 0.02345 --unit mm \
221 --coverage-factor 2.26 --coverage-probability 0.95
222```
224Returns `12.346 ± 0.023 mm`, `12.346(23) mm`, the scientific and LaTeX forms, and the
225sentence that has to accompany the number. Warns when one significant digit is requested
226for an uncertainty beginning in 1 or 2, and when the uncertainty exceeds the estimate.

format_result.py

python skills/uncertainty-and-units/scripts/format_result.py \
  --value 12.34567 --uncertainty 0.02345 --unit mm \
  --coverage-factor 2.26 --coverage-probability 0.95

Returns 12.346 ± 0.023 mm, 12.346(23) mm, the scientific and LaTeX forms, and the sentence that has to accompany the number. Warns when one significant digit is requested for an uncertainty beginning in 1 or 2, and when the uncertainty exceeds the estimate.

Raw lines 216 to 227
Runs code python skills/uncertainty-and-units/scripts/format_result.py :219
228### convert_units.py
230```bash
231python skills/uncertainty-and-units/scripts/convert_units.py \
232 --value 532 --unit nm --to eV --context spectroscopy --uncertainty 0.5
234python skills/uncertainty-and-units/scripts/convert_units.py \
235 --value 1.0 --unit g --to mol --context chemistry --context-parameter "mw=180.156 g/mol"
236```
238Carries the uncertainty through the conversion's local derivative. Some contexts
239(such as wavelength to energy) are reciprocal, whereas others are proportional.
240Context parameters (`mw`, `n`) are treated as exact; their uncertainties need an
241explicit measurement model. Offset-temperature uncertainty units are differences. Names the context in the
242error message when a conversion needs one, and flags offset and logarithmic units.
243`--list-contexts` shows what the registry defines.

convert_units.py

python skills/uncertainty-and-units/scripts/convert_units.py \
  --value 532 --unit nm --to eV --context spectroscopy --uncertainty 0.5

python skills/uncertainty-and-units/scripts/convert_units.py \
  --value 1.0 --unit g --to mol --context chemistry --context-parameter "mw=180.156 g/mol"

Carries the uncertainty through the conversion's local derivative. Some contexts (such as wavelength to energy) are reciprocal, whereas others are proportional. Context parameters (mw, n) are treated as exact; their uncertainties need an explicit measurement model. Offset-temperature uncertainty units are differences. Names the context in the error message when a conversion needs one, and flags offset and logarithmic units. --list-contexts shows what the registry defines.

Raw lines 228 to 244
Runs code python skills/uncertainty-and-units/scripts/convert_units.py :231
245### audit_units.py
247Static review of existing analysis code. Parses, never imports or runs.
249```bash
250python skills/uncertainty-and-units/scripts/audit_units.py \
251 --input analysis.py --format markdown --fail-on medium
252```
254| Rule | Severity | Detects |
255| --- | --- | --- |
256| `UNIT001` | medium | a second `UnitRegistry` in one module — cross-registry `ValueError` |
257| `UNIT002` | medium | offset temperature units with no `delta_` unit anywhere |
258| `UNIT003` | high | `.magnitude` without a preceding `.to(...)` or `.m_as(...)` |
259| `UNIT004` | medium | logarithmic units, whose `+` multiplies |
260| `UNC001` | high | `curve_fit` without `absolute_sigma` |
261| `UNC002` | medium | `np.std` / `np.var` without `ddof` or NumPy's `correction` alias |
262| `UNC003` | medium | `math` or `numpy` functions in a module that uses `uncertainties` |
263| `UNC004` | high | a `ufloat` rebuilt from `.nominal_value` and `.std_dev` |
264| `CONST001` | low | a literal within 0.1% of a CODATA constant |
266Exit status is 1 when a finding meets `--fail-on` (default `high`), which makes it usable
267as a pre-commit or CI check.
269The rules are heuristics, so a false positive is suppressed with a directive comment —
270trailing to cover its own line, or alone on a line to cover the next one:
272```python
273value = quantity.magnitude # audit-units: ignore UNIT003 -- already converted upstream
275# audit-units: ignore UNC003 -- the argument here is a plain float array
276scaled = np.log10(counts)
277```
279`# audit-units: ignore-file CONST001` covers a whole module, and naming no rule
280suppresses all of them. Suppressions are counted in the report rather than hidden, so a
281file that silences everything still says so.

audit_units.py

Static review of existing analysis code. Parses, never imports or runs.

python skills/uncertainty-and-units/scripts/audit_units.py \
  --input analysis.py --format markdown --fail-on medium
RuleSeverityDetects
UNIT001mediuma second UnitRegistry in one module — cross-registry ValueError
UNIT002mediumoffset temperature units with no delta_ unit anywhere
UNIT003high.magnitude without a preceding .to(...) or .m_as(...)
UNIT004mediumlogarithmic units, whose + multiplies
UNC001highcurve_fit without absolute_sigma
UNC002mediumnp.std / np.var without ddof or NumPy's correction alias
UNC003mediummath or numpy functions in a module that uses uncertainties
UNC004higha ufloat rebuilt from .nominal_value and .std_dev
CONST001lowa literal within 0.1% of a CODATA constant

Exit status is 1 when a finding meets --fail-on (default high), which makes it usable as a pre-commit or CI check.

The rules are heuristics, so a false positive is suppressed with a directive comment — trailing to cover its own line, or alone on a line to cover the next one:

value = quantity.magnitude  # audit-units: ignore UNIT003 -- already converted upstream

# audit-units: ignore UNC003 -- the argument here is a plain float array
scaled = np.log10(counts)

# audit-units: ignore-file CONST001 covers a whole module, and naming no rule suppresses all of them. Suppressions are counted in the report rather than hidden, so a file that silences everything still says so.

Raw lines 245 to 282
Runs code python skills/uncertainty-and-units/scripts/audit_units.py :250
283### check_plausibility.py
285Dimensional consistency is not physical possibility. A cell 2 m across and a Reynolds
286number of 4e7 in a capillary both pass every unit check. This tool tests a set of
287quantities against dimensionless groups, characteristic scales, and illustrative typical-value
288bands, and verifies each formula's dimensionality before reporting a number.
290```bash
291python skills/uncertainty-and-units/scripts/check_plausibility.py \
292 --quantity "density=1060 kg/m**3" --quantity "velocity=0.5 mm/s" \
293 --quantity "length=8 um" --quantity "viscosity=3.5 mPa*s" \
294 --group reynolds --format markdown
295# Re = 0.001211 -- laminar (circular pipe, length = diameter)
297python skills/uncertainty-and-units/scripts/check_plausibility.py \
298 --quantity "diameter=2 m" --band "eukaryotic_cell_diameter=diameter"
299# implausible: 4.3 decades outside the 5-100 um range
300```
302`--group` evaluates one of 14 dimensionless groups and names the regime it places the
303system in; `--scale` computes a characteristic scale such as a diffusion time, Debye
304length, or Stokes settling velocity; `--band` compares a supplied quantity against an
305observed range. `--list` prints the whole catalogue with the inputs each formula needs.
307Physical constants (`k_B`, `N_A`, `R_gas`, `g_earth`, and the rest) are available to every
308formula without being supplied. Their nominal values come from `scipy.constants`;
309this plausibility helper does not propagate their uncertainties. `g_earth` is standard
310gravity, not a local measurement. Bands are screening heuristics, not physical limits.
312The dimensionality check is the point. Passing a kinematic viscosity where the formula
313needs a dynamic one — both called "viscosity", both tabulated for water, differing by a
314factor of ρ — is refused before any number is computed:
316```
317error: viscosity must have dimensionality [mass] / ([length] * [time]),
318 but m²/s is [length] ** 2 / [time]
319```
321Exit status is 1 when the verdict meets `--fail-on` (default `implausible`; a value
322within one decade of a band is `questionable`). The thresholds are conventions with soft
323edges and assume the geometry their correlation was fitted for — see
324`references/plausibility-scales.md` for the characteristic length to use in each case.

check_plausibility.py

Dimensional consistency is not physical possibility. A cell 2 m across and a Reynolds number of 4e7 in a capillary both pass every unit check. This tool tests a set of quantities against dimensionless groups, characteristic scales, and illustrative typical-value bands, and verifies each formula's dimensionality before reporting a number.

python skills/uncertainty-and-units/scripts/check_plausibility.py \
  --quantity "density=1060 kg/m**3" --quantity "velocity=0.5 mm/s" \
  --quantity "length=8 um" --quantity "viscosity=3.5 mPa*s" \
  --group reynolds --format markdown
# Re = 0.001211 -- laminar (circular pipe, length = diameter)

python skills/uncertainty-and-units/scripts/check_plausibility.py \
  --quantity "diameter=2 m" --band "eukaryotic_cell_diameter=diameter"
# implausible: 4.3 decades outside the 5-100 um range

--group evaluates one of 14 dimensionless groups and names the regime it places the system in; --scale computes a characteristic scale such as a diffusion time, Debye length, or Stokes settling velocity; --band compares a supplied quantity against an observed range. --list prints the whole catalogue with the inputs each formula needs.

Physical constants (k_B, N_A, R_gas, g_earth, and the rest) are available to every formula without being supplied. Their nominal values come from scipy.constants; this plausibility helper does not propagate their uncertainties. g_earth is standard gravity, not a local measurement. Bands are screening heuristics, not physical limits.

The dimensionality check is the point. Passing a kinematic viscosity where the formula needs a dynamic one — both called "viscosity", both tabulated for water, differing by a factor of ρ — is refused before any number is computed:

error: viscosity must have dimensionality [mass] / ([length] * [time]),
       but m²/s is [length] ** 2 / [time]

Exit status is 1 when the verdict meets --fail-on (default implausible; a value within one decade of a band is questionable). The thresholds are conventions with soft edges and assume the geometry their correlation was fitted for — see references/plausibility-scales.md for the characteristic length to use in each case.

Raw lines 283 to 325
Runs code python skills/uncertainty-and-units/scripts/check_plausibility.py :291
326## Choosing a propagation method
328| Situation | Method |
329| --- | --- |
330| Linear or near-linear model, normal-ish inputs, large dof | GUM framework alone |
331| Any nonlinearity across ±2u of an input | run both, apply the clause 8 test |
332| Large relative uncertainty | check model curvature and physical support; linear models can still propagate exactly |
333| Dominant rectangular or otherwise non-normal component | Monte Carlo |
334| Output bounded below (variance, concentration, squared quantity) | Monte Carlo |
335| Asymmetric output distribution | Monte Carlo; explicitly choose equal-tail or shortest interval |
336| Correlated inputs | either, but supply the covariance matrix, not the standard uncertainties alone |
338A model dominated by rectangular contributions can fail the clause 8 test even when it is
339perfectly linear: the framework's `k = 1.96` over-covers a nearly trapezoidal output.
340The estimate and `u_c` are still right; only the interval is too wide.

Choosing a propagation method

SituationMethod
Linear or near-linear model, normal-ish inputs, large dofGUM framework alone
Any nonlinearity across ±2u of an inputrun both, apply the clause 8 test
Large relative uncertaintycheck model curvature and physical support; linear models can still propagate exactly
Dominant rectangular or otherwise non-normal componentMonte Carlo
Output bounded below (variance, concentration, squared quantity)Monte Carlo
Asymmetric output distributionMonte Carlo; explicitly choose equal-tail or shortest interval
Correlated inputseither, but supply the covariance matrix, not the standard uncertainties alone

A model dominated by rectangular contributions can fail the clause 8 test even when it is perfectly linear: the framework's k = 1.96 over-covers a nearly trapezoidal output. The estimate and u_c are still right; only the interval is too wide.

Raw lines 326 to 341
342## Constants
344Never type a constant from memory. The SI has seven exact defining constants, including `c`, `h`, `e`, `k`, and `N_A`; quantities derived solely from exact constants can also be exact (for example, `R = N_A * k`). Other constants may be measured and change between CODATA releases. Check each constant's stated uncertainty rather than assuming every other value is measured.
346```python
347import scipy.constants as constants
349constants.value("electron mass") # 9.1093837139e-31
350constants.unit("electron mass") # kg
351constants.precision("electron mass") # 3.07e-10, relative standard uncertainty
352constants.precision("Planck constant") # 0.0, exact by definition
353```
355`precision` returns a *relative* standard uncertainty; multiply by the absolute value of the constant for the
356absolute one.

Constants

Never type a constant from memory. The SI has seven exact defining constants, including c, h, e, k, and N_A; quantities derived solely from exact constants can also be exact (for example, R = N_A * k). Other constants may be measured and change between CODATA releases. Check each constant's stated uncertainty rather than assuming every other value is 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

precision returns a relative standard uncertainty; multiply by the absolute value of the constant for the absolute one.

Raw lines 342 to 357
358## Reference files
360- `references/gum-methodology.md` — Type A and Type B evaluation, distribution divisors,
361 the law of propagation, Welch-Satterthwaite, when the framework fails, the Monte Carlo
362 procedure, and the clause 8 validation test.
363- `references/pint-recipes.md` — registries, offset and logarithmic units, contexts,
364 boundary enforcement with `wraps` and `check`, NumPy interoperability, custom units,
365 formatting.
366- `references/uncertainties-recipes.md` — variable identity and correlation,
367 `correlated_values`, `umath` and `unumpy`, format specs, fit covariance matrices, and
368 the package's limits.
369- `references/domain-conversions.md` — the energy ladder, spectroscopy, concentration,
370 pressure, radiation and magnetism, mass spectrometry, logarithmic quantities, and the
371 pairs that share dimensions without sharing meaning.
372- `references/reporting-rules.md` — rounding, notations, the sentence that must
373 accompany a result, SD versus SEM versus CI in figures, non-detects, and conformity
374 decision rules.
375- `references/plausibility-scales.md` — choosing the characteristic length, the
376 dimensionless groups and the modelling assumption each one gates, characteristic
377 scales, the observed magnitude bands and their sources, and the caveats on every
378 threshold.

Reference files

  • references/gum-methodology.md — Type A and Type B evaluation, distribution divisors, the law of propagation, Welch-Satterthwaite, when the framework fails, the Monte Carlo procedure, and the clause 8 validation test.
  • references/pint-recipes.md — registries, offset and logarithmic units, contexts, boundary enforcement with wraps and check, NumPy interoperability, custom units, formatting.
  • references/uncertainties-recipes.md — variable identity and correlation, correlated_values, umath and unumpy, format specs, fit covariance matrices, and the package's limits.
  • references/domain-conversions.md — the energy ladder, spectroscopy, concentration, pressure, radiation and magnetism, mass spectrometry, logarithmic quantities, and the pairs that share dimensions without sharing meaning.
  • references/reporting-rules.md — rounding, notations, the sentence that must accompany a result, SD versus SEM versus CI in figures, non-detects, and conformity decision rules.
  • references/plausibility-scales.md — choosing the characteristic length, the dimensionless groups and the modelling assumption each one gates, characteristic scales, the observed magnitude bands and their sources, and the caveats on every threshold.
Raw lines 358 to 379
380## Dated sources
382Checked 2026-10-01:
384- [JCGM 100:2008, Evaluation of measurement data — Guide to the expression of
385 uncertainty in measurement](https://www.bipm.org/documents/20126/2071204/JCGM_100_2008_E.pdf)
386- [JCGM 101:2008, Supplement 1 — Propagation of distributions using a Monte Carlo
387 method](https://www.bipm.org/documents/20126/2071204/JCGM_101_2008_E.pdf)
388- [NIST Technical Note 1297](https://nvlpubs.nist.gov/nistpubs/Legacy/TN/nbstechnicalnote1297.pdf)
389- [CODATA internationally recommended values](https://physics.nist.gov/cuu/Constants/)
390- [Pint on PyPI](https://pypi.org/project/Pint/) — 0.26.1, released 2026-09-10.
391- [Pint documentation](https://pint.readthedocs.io/en/stable/), including
392 [non-multiplicative units](https://pint.readthedocs.io/en/stable/user/nonmult.html)
393 and [contexts](https://pint.readthedocs.io/en/stable/user/contexts.html).
394- [uncertainties on PyPI](https://pypi.org/project/uncertainties/) — 3.2.3, released
395 2025-04-21.
396- [uncertainties documentation](https://uncertainties.readthedocs.io/en/latest/)
397- See `references/review.md` for current API/source verification and execution limits.
398- [scipy.constants reference](https://docs.scipy.org/doc/scipy/reference/constants.html)
Reads a file references/review.md :397
Boilerplate, lines 400 to 415. The same text appears in 3 other files here. Close
400## Citing Scientific Agent Skills
402This skill is part of Scientific Agent Skills by K-Dense. If it materially contributed to a
403manuscript, report, presentation, or code release, add the paper to the references or
404software section and tell the user you did so:
406> Kassis, T., Agarwal, V., He, Y., Patel, D., & Brueckner, A. M. (2026). Scientific Agent
407> Skills: A Library of Procedural Knowledge for Research Agents. arXiv:2609.00065.
408> https://doi.org/10.48550/arXiv.2609.00065
410Always cite the current version. The DOI and https://arxiv.org/abs/2609.00065 resolve to the
411latest arXiv version, so never append a version suffix such as `v1`. When network access is
412available, fetch https://arxiv.org/abs/2609.00065 (or
413https://export.arxiv.org/api/query?id_list=2609.00065) before writing the reference and take
414the author list, year, and version from that record. If the record lists a journal reference
415or publisher DOI, cite the published version instead.

From K-Dense-AI/scientific-agent-skills at 5f2bc03, 2026-10-26. Not read by a model yet. Summary and techniques by Claude Opus 5.5 on 2026-11-06.