@essrt/physics
v23.3.3
Published
ESSRT (Expanding Solar System Resonance Theory) physics core — pure, injectable-constants, no I/O, no globals, no DOM. The complete model incl. fitted coefficients; every published version immutably ships one recorded model identity.
Maintainers
Readme
@essrt/physics
The physics core of ESSRT — the Expanding Solar System Resonance Theory — as implemented by the ESSRT simulator (source, AGPL-3.0). Pure computation: no I/O, no globals, no DOM, constants injected. The package ships the complete model, including the fitted coefficients, so an installed copy reproduces the simulator's engine on the shared golden masters (bit-exact in Node; the browser build is held to a measured cross-runtime tolerance).
Versioning — two axes, deliberately
- Package semver (this package's
version) tracks the API axis — whatimportgives you. - Model identity rides inside every published version: the
essrt.modelVersionfield inpackage.jsonplusCONSTANTS_HASHandCOEFFICIENTS_HASHinsrc/constants/. A refit or structural model change always lands as a new package version — npm's immutability then guarantees a pinned version's results never change. The hashes cover VALUES: the artifacts' provenance stamps (theirinputsblocks) are excluded, so a re-stamp that moves no number moves no hash.
Cite results as model vX.Y (package A.B.C).
Usage
import { DEFAULT_CONSTANTS, FITTED_COEFFICIENTS, createEpochPrimitives } from '@essrt/physics';Domain modules are CJS subpath exports, each a create* factory taking its
dependencies explicitly (the injectable-constants design — counterfactual
runs are first-class):
const { createEclipseFinders } = require('@essrt/physics/eclipse/finders');
const { createMoonSeries } = require('@essrt/physics/moon/series');TypeScript: the package ships declaration files (types/, one per
subpath export) generated from the sources' JSDoc — import from
@essrt/physics or any subpath is fully typed; no ambient shim needed.
See the exports map in package.json for the full surface: moon
(arguments, series, series-extension, apparent, ecc-channel,
deep-ecc-channel, month-chain), earth (year-lengths, precession-composed,
frame-of-date, sidereal-year-channel, deep-orbital-history), planets
(keplerian-chain, orbit-chain, secular-shape, secular-series,
inv-plane-frame, spin-channel, ecc-channel, geometry, model), deltat
(cycles, deep-time, moon-distance-history + moon-distance-artifact — the cited
AstroGeo22 Earth–Moon distance history, historical), the cardinal structure,
the deep calendar, chain-cycles, sun, climate and eclipse finders.
For rendered display values (day/year lengths, precession rates, orbital
elements as preformatted strings), see
@essrt/model-values —
generated from this engine at the same model version.
The assembled model — createModel()
The canonical assembly, wired once inside the package:
import { createModel, DEFAULT_CONSTANTS } from '@essrt/physics';
const model = createModel();
model.identity.modelVersion; // the shipped model identity (e.g. 'v21.0') + both content hashes
model.lunisolar.meanPeriodYearsAtYear(2000 - 380e6); // 21699 — the Devonian precession period
model.epoch.lodSecondsAtYear(2000 - 380e6); // 78895 — the Devonian day (Wells 1963's coral count)
model.earth.obliquityDeg(2000); // 23.4393
model.cardinal.jd(2000, 'SS'); // 2451716.5751 — June solstice 2000, the apparent Sun's crossing (USNO 2451716.575)
model.lengths.tropicalYearDays(2000); // 365.24219
// Counterfactual (§2d): inject different constants, get a different solar
// system — self-identifying, with its own constants hash.
const cf = createModel({
...DEFAULT_CONSTANTS,
earthOrbital: { ...DEFAULT_CONSTANTS.earthOrbital, obliquityJ2000_deg: 23.5 },
});
cf.earth.obliquityDeg(2000); // 23.5 — the injected anchor read through the assembly
cf.identity.counterfactual; // trueThe surface groups: time (exact JD ↔ model-year conversion on the SI
axis the fits were anchored on — callers holding a JD convert here, never
with their own formula), identity (version + hashes + citation DOI), epoch
(the internal unit, LOD, α, ΔT, the precession period and the deep-time
quantities at any year ±500 Myr), lunisolar (the mean lunisolar precession
period and rate at any year, its solar/lunar torque split, the nodal mode
and the obliquity beat, the apsidal and perihelion-of-date periods and their
ratios over the published window), earth
(obliquity, eccentricity, inclination, perihelion), lengths (year and
day lengths), cardinal (solstice/equinox JD, RA, year length), moon
(distance and months at epoch, plus the apparent-position chain on the
JD(UT) axis — ecliptic longitude, latitude and distance from the shared
Meeus series over framework-native arguments), eclipse (geocentric
solar/lunar eclipse search over a JD window, greatest-eclipse convention),
climate (the L1 orbital-forcing formula) and planets (the N-body chain's
elements of date — perihelion longitude, node and inclination on the
invariable plane, eccentricity — and spin). Validation targets are refused as
inputs — a counterfactual cannot move the goalposts it is judged by.
The series artifact
Earth's published orbit history (eccentricity, perihelion, obliquity) is
the engine's banked N-body series — a 9-MB governed artifact kept in the
public repository
(data/nbody-secular-series.json), not inside this package. Pass it in to
get the published values:
const series = JSON.parse(readFileSync('nbody-secular-series.json', 'utf8'));
const model = createModel(undefined, { secularSeriesArtifact: series });
model.earth.eccentricity(2000 - 100000); // 0.04008 — the series (La2004: 0.04006)
createModel().earth.eccentricity(2000 - 100000); // 0.03374 — the mode tablemodel.identity.earthElements names which one a result came from
('secular-series' or 'mode-table'), so a stored value is never ambiguous.
Without the artifact createModel() evaluates the embedded 18-term mode
table at every epoch: exact at J2000, a compact approximation away from it
(RMS 3.6·10⁻³ in e against La2010a over the last 500 kyr, where the series
reads 2.5·10⁻⁵). The API handler, the MCP server and the simulator always
pass the artifact.
Underneath: a parts library
createModel() is assembly over unassembled factories. Each factory
receives everything through one deps object — plain numbers plus
functions — and no module imports another; that is what makes
counterfactuals first-class and lets the same core run in Node and the
browser. For custom wiring (different α channel, subset assembly), use
the factories directly: the reference adapter is tools/lib/ in the
public repository — the hosted
simulator and the project's verification gates run on it. A service
surface over createModel() lives in the same repository
(packages/api, an OpenAPI 3.1 handler with zero runtime dependencies)
— build-only until the hosting decision.
Provenance
Every value derives from the model parameters and fitted coefficients in this package; the fitting pipeline, verification gates, dataset manifest and documentation live in the public repository. Preprint: https://doi.org/10.21203/rs.3.rs-8758810/v4.
Licence
AGPL-3.0-or-later. Commercial licensing: [email protected].
