✨ feat(unitsystems): name the Planck and atomic base units - #875
Conversation
Closes GalacticDynamics#868. `astropy.units` has no names for these, so they could only be spelled as a scale times an SI unit -- and `to_string()` truncates past six significant figures, so that spelling neither read well nor reparsed: planck["length"] -> Unit("1.61626e-35 m") # and does *not* reparse planck["length"] -> Unit("l_P") # now Eight units: `l_P`, `m_P`, `t_P`, `T_P` for Planck, and `a_0`, `m_e`, `t_au`, `e` for Hartree atomic. Only these two systems get names -- `hep` and `geometrized` take a free scale, so their bases change with the parameter and no fixed name would be right. Each is defined from the *same constant expression* that already built its unit system rather than a hard-coded number, so the values track whatever CODATA revision `astropy.constants` ships. A test pins that. Three things the naming had to dodge, each verified rather than assumed: - `a0` and `Eh` are **not** free. astropy already parses them as *dimensionless* and as a *time* (`E`×`h`, hour). Hence `a_0`, and no Hartree-energy unit. - A unit named `e` does not disturb scientific notation -- `1e5 m` and `1.5e3 kg` still parse as a scale times a unit. - astropy ignores a `format` string identical to the unit's own name and then escapes the underscore, so `a_0` needed `a_{0}` to render as `$a_{0}$` rather than `$\mathrm{a\_0}$`. Registered with `add_enabled_units` at import, so `unxt.unit("l_P")` resolves and the spelling round-trips. That mutates astropy's registry process-wide; it is the reason this was kept out of GalacticDynamics#855, and the trade is deliberate. Upstream issue to follow, which would let unxt drop the module entirely. Doctests that hid these values behind `...` no longer need to: a name is stable across CODATA revisions where six significant figures were not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces explicit, parseable names for the base units of the fully-determined natural unit systems (planck and Hartree atomic) so they round-trip through astropy.units string formatting/parsing without losing precision due to to_string()’s significant-figure truncation.
Changes:
- Add a new
named_unitsmodule definingl_P,m_P,t_P,T_P,a_0,m_e,t_au, ande, and register them viaastropy.units.add_enabled_unitsso they are parseable. - Switch the
PlanckUSysFlagandAtomicUSysFlagrealizations to use the named units (and update doctest outputs accordingly). - Add unit tests covering naming, round-tripping, LaTeX formatting, scientific-notation safety for
e, and ensuringastropy’s pre-existinga0meaning remains unchanged.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/test_unitsystems.py | Adds tests asserting named natural bases, exact reparsing, LaTeX formatting, and safety checks around e/a0. |
| src/unxt/_src/unitsystems/named_units.py | Defines and registers the eight named units for Planck and Hartree atomic systems. |
| src/unxt/_src/unitsystems/flags.py | Updates doctest outputs for PlanckUSysFlag / AtomicUSysFlag to show named units. |
| src/unxt/_src/unitsystems/core.py | Uses PLANCK_UNITS / ATOMIC_UNITS instead of recomputing unnamed scaled SI units. |
| docs/guides/units_and_systems.md | Updates documentation examples to reflect named base units. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #875 +/- ##
==========================================
+ Coverage 99.82% 99.96% +0.13%
==========================================
Files 84 47 -37
Lines 3985 2700 -1285
Branches 311 162 -149
==========================================
- Hits 3978 2699 -1279
+ Misses 3 0 -3
+ Partials 4 1 -3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Closes #868.
astropy.unitshas no names for the Planck or Hartree atomic base units, sothey could only be spelled as a scale times an SI unit — and
to_string()truncates past six significant figures, so that spelling neither read well nor
reparsed:
Eight units:
l_P,m_P,t_P,T_Pfor Planck;a_0,m_e,t_au,efor Hartree atomic.
Scope
Only these two systems get names.
hepandgeometrizedtake a free scale(an energy and a length respectively), so their bases change with the
parameter — verified — and no fixed name would be correct:
Three traps, each checked rather than assumed
a0andEhare not free. astropy already parses them asdimensionless and as a time (
E×h, hour). Either would have silentlymeant the wrong thing. Hence
a_0, and no Hartree-energy unit here.edoes not disturb scientific notation —1e5 mand1.5e3 kgstill parse as a scale times a unit. Tested, since this was themain worry with the conventional name for elementary charge.
formatstring identical to the unit's own name andthen escapes the underscore, so
a_0renders$\mathrm{a\_0}$unless theoverride differs —
a_{0}fixes it.Values track astropy
Each unit is defined from the same constant expression that already built
its unit system, not from a hard-coded number, so a CODATA revision in
astropy.constantsmoves these with it. A test pins that.That also means doctests which hid these behind
...no longer need to: aname is stable across CODATA revisions where six significant figures were not.
The registry trade-off
Registered with
add_enabled_unitsat import, sounxt.unit("l_P")resolvesand the spelling round-trips. This mutates astropy's registry process-wide —
it is exactly why the change was kept out of #855, and the trade is
deliberate. All eight names were checked unclaimed.
Display alone would not have needed this (a
def_unitknows its own name);parsing does.
Follow-up: #876 tracks upstreaming these into astropy, which would let unxt
drop the module entirely.
Interaction with #855
#855 should rebase on top of this. Its unit-system
reprroutes a system whoseunits have no short exact spelling to the realization's registered name
(
unitsystem('planck')). With these units named,planck's bases do spellexactly, so it will instead render
unitsystem(['l_P', 'm_P', 't_P', 'T_P'])—informative and round-trippable, and quite possibly making that name-lookup
path redundant.
Verification
nox -s "pytest(package='unxt')"— 4068 passed. Every other package sessiongreen (
interop_gala,parametric,api,hypothesis,interop_xarray),plus
unxts.linalg287. pylint 10.00/10, ruff clean.