@essrt/physics 23.3.3 → 24.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +4 -1
  2. package/package.json +2 -2
  3. package/src/cardinal/one-source-structure.cjs +3 -1
  4. package/src/constants/coefficients.d.ts +5 -1
  5. package/src/constants/coefficients.js +3 -2
  6. package/src/constants/generated.d.ts +13 -14
  7. package/src/constants/generated.js +14 -15
  8. package/src/constants/units.cjs +4 -1
  9. package/src/deltat/deep-time.cjs +37 -72
  10. package/src/earth/deep-orbital-history.cjs +48 -34
  11. package/src/earth/year-lengths.cjs +2 -1
  12. package/src/eclipse/besselian.cjs +24 -11
  13. package/src/index.js +30 -48
  14. package/src/model/cardinal-instants.js +101 -0
  15. package/src/model/context.js +54 -0
  16. package/src/model/deep-time-chain.js +79 -0
  17. package/src/model/delta-t.js +38 -0
  18. package/src/model/derived-constants.js +58 -0
  19. package/src/model/earth-scalars.js +47 -0
  20. package/src/model/inv-plane-elements.js +56 -0
  21. package/src/model/lunar-theory.js +216 -0
  22. package/src/model/moon-epoch.js +28 -0
  23. package/src/model/one-source-movement.js +192 -0
  24. package/src/model/planet-elements-of-date.js +61 -0
  25. package/src/model/planet-records.js +63 -0
  26. package/src/model/planet-spin-channel.js +41 -0
  27. package/src/model/published-periods.js +38 -0
  28. package/src/model/sun-and-eclipses.js +199 -0
  29. package/src/model/surface.js +332 -0
  30. package/src/model/time-axis.js +24 -0
  31. package/src/model/year-day-lengths.js +42 -0
  32. package/src/model.js +62 -1612
  33. package/src/moon/arguments.cjs +27 -43
  34. package/src/planets/chain-artifact.js +2 -2
  35. package/src/planets/keplerian-chain.cjs +3 -3
  36. package/src/planets/model.cjs +2 -2
  37. package/types/constants/coefficients.d.ts +5 -1
  38. package/types/constants/generated.d.ts +13 -14
  39. package/types/constants/units.d.cts +3 -0
  40. package/types/deltat/deep-time.d.cts +18 -24
  41. package/types/earth/deep-orbital-history.d.cts +23 -0
  42. package/types/eclipse/besselian.d.cts +37 -18
  43. package/types/index.d.ts +1 -1
  44. package/types/model/cardinal-instants.d.ts +14 -0
  45. package/types/model/context.d.ts +87 -0
  46. package/types/model/deep-time-chain.d.ts +288 -0
  47. package/types/model/delta-t.d.ts +11 -0
  48. package/types/model/derived-constants.d.ts +25 -0
  49. package/types/model/earth-scalars.d.ts +14 -0
  50. package/types/model/inv-plane-elements.d.ts +6 -0
  51. package/types/model/lunar-theory.d.ts +72 -0
  52. package/types/model/moon-epoch.d.ts +10 -0
  53. package/types/model/one-source-movement.d.ts +74 -0
  54. package/types/model/planet-elements-of-date.d.ts +9 -0
  55. package/types/model/planet-records.d.ts +7 -0
  56. package/types/model/planet-spin-channel.d.ts +29 -0
  57. package/types/model/published-periods.d.ts +7 -0
  58. package/types/model/sun-and-eclipses.d.ts +53 -0
  59. package/types/model/surface.d.ts +281 -0
  60. package/types/model/time-axis.d.ts +12 -0
  61. package/types/model/year-day-lengths.d.ts +8 -0
  62. package/types/model.d.ts +10 -81
  63. package/types/moon/arguments.d.cts +5 -11
  64. package/types/planets/keplerian-chain.d.cts +4 -2
  65. package/types/planets/model.d.cts +3 -3
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Cardinal instants — the apparent Sun (completed, aberrated, nutated) and the Newton crossings.
3
+ *
4
+ * One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
5
+ */
6
+
7
+ /** @param {import('./published-periods.js').Ctx} ctx @returns the bindings the later slices read */
8
+ export function assembleCardinalInstants(ctx) {
9
+ const {
10
+ C, F, currentAUDistance, eclipseFinders, j2000JD, jdTTFromUT, julianCenturyDays, moonArgs, sunEccentricityAt,
11
+ sunPlanetaryCompletionDeg, yearLengthsM,
12
+ } = ctx;
13
+ /** The completed certified Sun (finder Sun − the derived planetary
14
+ * completion) at a UT model-JD — the same longitude the scene renders
15
+ * inside the overlay window; MEAN GEOMETRIC, mean equinox of date.
16
+ * @param {number} jdUT @returns {number} */
17
+ const sunLonCompletedDegAtJdUT = (jdUT) => eclipseFinders.sunLonDegAt(jdUT)
18
+ - sunPlanetaryCompletionDeg((jdTTFromUT(jdUT) - j2000JD) / julianCenturyDays);
19
+ /** The APPARENT Sun the cardinal instants are defined on (an equinox is
20
+ * the apparent Sun crossing the equator — the USNO/Meeus instants are
21
+ * apparent): the completed geometric Sun minus the aberration constant,
22
+ * plus the nutation in longitude. Both derived, zero new constants —
23
+ * κ = 2π·a/(c·T_sid·√(1−e²)) from the AU, c, the one-family sidereal year
24
+ * and the Sun's e (20.50″ at J2000); Δψ from the four IAU 1980 leading
25
+ * terms on the model's OWN lunar node, mean Sun and mean Moon (the
26
+ * framework arguments), the same family the registry's JPL Sun
27
+ * instrument bridges with. @param {number} jdUT @returns {number} */
28
+ /** The Sun's derived aberration constant of date, arcsec — κ = 2π·a/(c·T_sid·√(1−e²))
29
+ * from the AU, c, the one-family sidereal year and the Sun's e (20.50″ at
30
+ * J2000); ONE home for the cardinal instants and the eclipse location tier.
31
+ * @param {number} year @returns {number} */
32
+ const sunAberrationArcsecAt = (year) => {
33
+ const e = sunEccentricityAt(year);
34
+ return (2 * Math.PI * currentAUDistance
35
+ / (C.physicalConstants.speedOfLight * yearLengthsM.siderealYearSecondsAtYear(year) * Math.sqrt(1 - e * e)))
36
+ * (648000 / Math.PI);
37
+ };
38
+ /** @param {number} jdUT @returns {number} */
39
+ const sunApparentLonDegAtJdUT = (jdUT) => {
40
+ const jdTT = jdTTFromUT(jdUT);
41
+ const year = 2000 + (jdTT - j2000JD) / 365.25;
42
+ const kappaArcsec = sunAberrationArcsecAt(year);
43
+ const a = moonArgs.argsAt(jdTT);
44
+ const d2r = Math.PI / 180;
45
+ const Om = (a.Lp - a.F) * d2r, Ls = (a.Lp - a.D) * d2r, Lm = a.Lp * d2r;
46
+ const NU = C.physicalConstants.nutationLeadingTermsArcsec;
47
+ const dPsiArcsec = NU.psiOmega * Math.sin(Om) + NU.psi2Ls * Math.sin(2 * Ls)
48
+ + NU.psi2Lm * Math.sin(2 * Lm) + NU.psi2Omega * Math.sin(2 * Om);
49
+ return sunLonCompletedDegAtJdUT(jdUT) + (dPsiArcsec - kappaArcsec) / 3600;
50
+ };
51
+ /** @param {string} type @returns {number} */
52
+ const cardinalTargetDeg = (type) => {
53
+ const t = { VE: 0, SS: 90, AE: 180, WS: 270 }[type];
54
+ if (t === undefined) throw new RangeError(`cardinal type must be VE|SS|AE|WS, got ${type}`);
55
+ return t;
56
+ };
57
+ const CARDINAL_SI_YEAR_D = 365.2422;
58
+ /** The crossing NEAREST a seed instant — the unique root within ±½ tropical
59
+ * year of the seed (the wrapped longitude difference is the Newton residual).
60
+ * Plan 06 R4c: the seed is a JD because the caller's year may live on a
61
+ * different axis than the model year — the simulator's cardinal panel
62
+ * labels its year with the displayed CALENDAR date (the Julian calendar
63
+ * before 1582, 365.25-d years) while `year` below is the model year (the SI
64
+ * axis, 365.2422-d count from J2000); the two part by 0.0078 d/yr, 114 yr at
65
+ * −5.34 Myr, where the panel showed the events of −5341772 for a date in
66
+ * −5341886 (owner). Seeding at the calendar year's midpoint returns that
67
+ * year's own events at every epoch. @param {number} jdSeedUT @param {string} type @returns {number} */
68
+ const cardinalCrossingNearJdUT = (jdSeedUT, type) => {
69
+ const target = cardinalTargetDeg(type);
70
+ const RATE = 360 / CARDINAL_SI_YEAR_D;
71
+ let jd = jdSeedUT;
72
+ for (let i = 0; i < 20; i++) {
73
+ const d = ((((sunApparentLonDegAtJdUT(jd) - target) + 540) % 360 + 360) % 360) - 180;
74
+ jd -= d / RATE;
75
+ if (Math.abs(d) < 1e-10) break;
76
+ }
77
+ return jd - F.DT_ANCHOR.deltaTStartSeconds / 86400;
78
+ };
79
+ /** The TRUE-UT JD at which the APPARENT Sun's longitude of date equals
80
+ * the cardinal target in the given year — Newton on the crossing (the
81
+ * Sun's rate 360°/tropical year; a handful of steps from the tropical-year
82
+ * seed). Replaces the fitted cardinal model's instants (R1). Plan 06 R3
83
+ * item 2: the Newton root lives on the FINDER axis (the curve-only ΔT the
84
+ * eclipse finders certify), and the returned instant is that root minus the
85
+ * deltaTStart bridge — true UT = TT − (deltaTStart + curve), the same
86
+ * bridge the besselian and the registry instruments apply. Before this the
87
+ * published instants were 54.55 s (0.9 min) late. Against Horizons over
88
+ * ±3000 yr the remaining offset is the Sun's (plan 06 I2 measured).
89
+ * `year` is the MODEL year (the SI axis) — see cardinalCrossingNearJdUT.
90
+ * @param {number} year @param {string} type @returns {number} */
91
+ const cardinalCrossingJdUT = (year, type) => {
92
+ const target = cardinalTargetDeg(type);
93
+ // seed: Jan 1.5 of the year (J2000 = 2000 Jan 1.5) + the mean date of the
94
+ // March equinox (day 79.3) + the quarter-turns to the target
95
+ const seed = j2000JD + (year - 2000) * CARDINAL_SI_YEAR_D + 79.3 + (target / 360) * CARDINAL_SI_YEAR_D;
96
+ return cardinalCrossingNearJdUT(seed, type);
97
+ };
98
+
99
+ return { cardinalCrossingJdUT, cardinalCrossingNearJdUT, cardinalTargetDeg, sunAberrationArcsecAt, sunApparentLonDegAtJdUT };
100
+ }
101
+ /** @typedef {import('./published-periods.js').Ctx & ReturnType<typeof assembleCardinalInstants>} Ctx */
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The model assembly's shared context — the types the slices of `assembleModel` (../model.js) pass along.
3
+ *
4
+ * Each slice takes the context the earlier slices built (`import('./<previous>.js').Ctx`, an intersection
5
+ * that grows slice by slice) and returns the bindings the later ones read; the composer merges them
6
+ * into ONE object, so every closure sees the same instance of every memo and sampler. The inputs
7
+ * below are the composer's parameters; `late` carries the handful of bindings a slice reads at CALL
8
+ * time that a later slice defines — the composer fills each one as the owning slice returns, so a
9
+ * reader finds every forward dependency of the assembly in these seven lines and in the composer.
10
+ */
11
+
12
+ /**
13
+ * RESEARCH OVERRIDES for Earth's orbit laws (doc 109 §7): an alternative e(t), de/dt(t) and
14
+ * ϖ_of-date(t) flow through every consumer (the eclipse Sun, the Moon's E-factor, the cardinal
15
+ * points) exactly as the shipped laws do. `{}` = the shipped laws. Not a counterfactual in the §2d
16
+ * sense (no hash change) — callers must say when they used it; the generators refuse to --write
17
+ * under an override.
18
+ * @typedef {{ eccentricityAt?: (year: number) => number, eccentricityRateAt?: (year: number) => number, perihelionLongitudeDegAt?: (year: number) => number }} Laws
19
+ */
20
+
21
+ /**
22
+ * `giaEllipticityOfDate: false` builds the one-source history WITHOUT the J₂(t)/J₂₀ factor of date
23
+ * (the ice-driven GIA channel, which rides the climate formula's own post-MPT layer): the orbital
24
+ * FORCING alone. The T1 exporter derives the climate lines' obliquity rate on it, so the lines
25
+ * cannot depend on their own fit (v23.0; measured 3e-5 of the rate, 2–3 yr of period). Default true =
26
+ * the shipped physics.
27
+ * @typedef {{ giaEllipticityOfDate?: boolean }} Options
28
+ */
29
+
30
+ /**
31
+ * The forward bindings — what an early slice reads at call time from a later one. Typed by the
32
+ * members actually read, so the composer's assignment is checked against each owner's surface.
33
+ * @typedef {object} LateBindings
34
+ * @property {() => number} certifiedAxialPrecessionJ2000Years owner: year-day-lengths — read by deep-time-chain
35
+ * @property {{ epsAt(year: number): number, eAt(year: number): number, periOfDateDegAt(year: number): number, precessionOfDateFactorsAt(year: number): { solarTorque: number, ellipticity: number }, axialPrecessionYearsLunisolarJ2000: number }} oneSourceM owner: one-source-movement — read by deep-time-chain and earth-scalars
36
+ * @property {{ anomalisticYearSecondsAtYear(year: number): number }} yearLengthsM owner: one-source-movement — read by year-day-lengths
37
+ * @property {(year: number) => number} sunMeanLongitudeDegAt owner: sun-and-eclipses — read by lunar-theory (the arguments' Sun)
38
+ * @property {(year: number) => number} publishedAxialPrecessionYearsAtYear owner: published-periods — read by lunar-theory (the dynamical rate of date)
39
+ * @property {(year: number) => number} publishedSiderealYearSecondsAtYear owner: published-periods — read by year-day-lengths
40
+ * @property {(year: number) => number} sunAberrationArcsecAt owner: cardinal-instants — read by sun-and-eclipses (the besselian's apparent Sun)
41
+ */
42
+
43
+ /**
44
+ * The composer's inputs — the first slice's context.
45
+ * @typedef {object} ModelInputs
46
+ * @property {Readonly<Record<string, any>>} C the frozen constants context
47
+ * @property {Readonly<Record<string, any>>} F the fitted coefficients
48
+ * @property {Laws} laws
49
+ * @property {any} secularSeriesArtifact the governed N-body secular series (the API/Node path; null for the bare package)
50
+ * @property {Options} options
51
+ * @property {LateBindings} late
52
+ */
53
+
54
+ export {};
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The deep-time chain — ONE assembly (createDeepTimeChain destructured), the climate L1 layer and the realized pole rate over a window.
3
+ *
4
+ * One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
5
+ */
6
+ import { evalClimateL1OrbitalPermil } from '../climate/l1-orbital.cjs';
7
+ import { createDeepTimeChain } from '../deltat/deep-time-chain.js';
8
+ import { MOON_DISTANCE_ARTIFACT } from '../deltat/moon-distance-artifact.cjs';
9
+ import { DEEP_MODES_ARTIFACT } from '../moon/deep-modes-artifact.cjs';
10
+
11
+ /** @param {import('./derived-constants.js').Ctx} ctx @returns the bindings the later slices read */
12
+ export function assembleDeepTimeChain(ctx) {
13
+ const { C, F, late, startmodelYear } = ctx;
14
+ // The GIA α channel (the lagged response to the L1 ice history, with the R2
15
+ // lattice pin and the exact-argument memo), the Layer-0 bundle, the cited
16
+ // Earth–Moon distance history (plan 12 option A), the ΔT cycle stack (all
17
+ // channels ON here), |s₃|, the LOD/ΔT core and the lunar month chain are
18
+ // assembled once in deltat/deep-time-chain.js from this context's constants
19
+ // and fitted coefficients (so a counterfactual context flows through them);
20
+ // the Node twin and the browser destructure the same module.
21
+ const chain = createDeepTimeChain({
22
+ constants: C, fitted: F, deepModes: DEEP_MODES_ARTIFACT, moonDistanceArtifact: MOON_DISTANCE_ARTIFACT,
23
+ ageAnchorYear: startmodelYear,
24
+ });
25
+ const { alphaGia, earthMoiFactorAtAge, EPOCH_PARAMS, recession, dtCycles, dtCycleLodCorrectionSum, s3ArcsecPerYr, deepLod, deepEcc, moonChain } = chain;
26
+ const CLIMATE_REGIME = F.CLIMATE_FORMULA_COEFFS.regimes['lr04-post-mpt'];
27
+ /** The climate formula's L1 (unlagged) — the orbital δ¹⁸O layer itself. @param {number} year @returns {number} */
28
+ const evalClimateL1 = (year) => evalClimateL1OrbitalPermil(year, {
29
+ l1Terms: CLIMATE_REGIME.L1,
30
+ yStdDenormalization: CLIMATE_REGIME.denormalization.y_std,
31
+ });
32
+ const moonDistanceMetresAtAge = recession.distanceMetresAtAge;
33
+ /** The published surfaces' km reading — NaN beyond the cited history's span (the API's
34
+ * validity span lies inside it; the deep charts stop where the input stops).
35
+ * @param {number} year @returns {number} */
36
+ const moonDistanceKmAtYearOrNaN = (year) => { const m = moonDistanceMetresAtAge(yearToTMa(year)); return m === null ? NaN : m / 1000; };
37
+
38
+ /** THE REALIZED POLE RATE OVER A WINDOW, ″/yr — the mean over [yearFrom, yearTo] of the rate the spin
39
+ * integration actually turns the pole at: the inertial secular rate of date × the solar-torque factor at
40
+ * e(t) × J₂(t)/J₂₀ × cos ε(t)/cos ε₀ (ds/dt = α (ŝ·n̂)(ŝ×n̂): the azimuthal rate about n̂ is α cos ε).
41
+ * The secular rate here is the CONSTANT's scaling (lunisolarTorqueRate…), not the published rate of
42
+ * date: the hybrid's own ε(t) carries the ONE cosine, so the cited mean obliquity's factor (plan 12 §8)
43
+ * does not enter twice — inside the ζ-series span the hybrid IS the model's obliquity of date.
44
+ * Measured (2026-10): the dominant obliquity line of the model's own ε(t) over the last 5 Myr sits at
45
+ * this mean minus |s₃| (40,938 fitted vs 40,936 predicted), NOT at the J2000 beat (41,098) — the pole
46
+ * turned faster in the past (spin +0.06 ″/yr over 5 Myr, lower mean obliquity +0.05, higher mean
47
+ * eccentricity +0.015); La2004's own series reads the same (40,917). The climate records' preferred
48
+ * "+0.12…+0.20 ″/yr on the obliquity lines" is this window mean (50.515 over the LR04 record).
49
+ * Reads the one-source history and p₀ through `late` (one-source-movement.js, year-day-lengths.js —
50
+ * evaluated at call time) and the deep-time LOD.
51
+ * `ellipticity: false` leaves the J₂(t)/J₂₀ factor out: that factor is the ice-driven GIA channel, which
52
+ * rides the climate formula's own post-MPT layer — the one piece that would make the climate LINES
53
+ * depend on their own fit (measured: its LR04-window mean moved 1 − 3e-5 → 1 across one refit,
54
+ * 1.4e-3 ″/yr, 1 yr of period). The line derivation (the T1 exporter) excludes it; the published
55
+ * realized period and the hybrid verdict's gate keep the full rate.
56
+ * @param {number} yearFrom @param {number} yearTo @param {number} [stepYears=500]
57
+ * @param {{ellipticity?: boolean}} [opts] @returns {number} */
58
+ const spinAxisInertialRateMeanArcsecPerYr = (yearFrom, yearTo, stepYears = 500, opts = {}) => {
59
+ const useEllipticity = opts.ellipticity !== false;
60
+ const lo = Math.min(yearFrom, yearTo), hi = Math.max(yearFrom, yearTo);
61
+ const cosEps0 = Math.cos(late.oneSourceM.epsAt(2000) * Math.PI / 180);
62
+ const kInertial = late.certifiedAxialPrecessionJ2000Years() / late.oneSourceM.axialPrecessionYearsLunisolarJ2000;
63
+ let sum = 0, n = 0;
64
+ for (let y = lo; y <= hi; y += stepYears) {
65
+ const r = deepLod.lunisolarTorqueRateArcsecPerYrAtAge(yearToTMa(y));
66
+ const psiDot = (r === null ? 1296000 / late.certifiedAxialPrecessionJ2000Years() : r) * kInertial;
67
+ const f = late.oneSourceM.precessionOfDateFactorsAt(y);
68
+ sum += psiDot * f.solarTorque * (useEllipticity ? f.ellipticity : 1) * Math.cos(late.oneSourceM.epsAt(y) * Math.PI / 180) / cosEps0;
69
+ n++;
70
+ }
71
+ return sum / n;
72
+ };
73
+
74
+ /** @param {number} year @returns {number} */
75
+ const yearToTMa = (year) => (2000 - year) / 1e6;
76
+
77
+ return { EPOCH_PARAMS, alphaGia, chain, deepEcc, deepLod, dtCycleLodCorrectionSum, dtCycles, earthMoiFactorAtAge, evalClimateL1, moonChain, moonDistanceKmAtYearOrNaN, moonDistanceMetresAtAge, s3ArcsecPerYr, spinAxisInertialRateMeanArcsecPerYr, yearToTMa };
78
+ }
79
+ /** @typedef {import('./derived-constants.js').Ctx & ReturnType<typeof assembleDeepTimeChain>} Ctx */
@@ -0,0 +1,38 @@
1
+ /**
2
+ * ΔT (TT − UT1) — the raw integral plus the cycle stack, memoized on the exact argument.
3
+ *
4
+ * One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
5
+ */
6
+
7
+ /** @param {import('./moon-epoch.js').Ctx} ctx @returns the bindings the later slices read */
8
+ export function assembleDeltaT(ctx) {
9
+ const { F, deepLod, dtCycles, yearToTMa } = ctx;
10
+ /** @param {number} tMa @returns {number} */
11
+ // Exact-argument memo (the alphaMemo pattern of deltat/deep-time-chain.js): the cardinal Newton
12
+ // solve asks for ΔT at the SAME jd twice per step (jdTTFromUT in the
13
+ // apparent Sun and again under the planetary completion) — measured 8.8 →
14
+ // 5.6 ms per panel year (8 solves) at modern epochs, values bit-identical.
15
+ /** @type {Map<number, number>} */
16
+ const dtMemo = new Map();
17
+ /** @param {number} tMa @returns {number} */
18
+ const meanDeltaTSecondsAtAge = (tMa) => {
19
+ if (tMa === 0) return 0;
20
+ const hit = dtMemo.get(tMa);
21
+ if (hit !== undefined) return hit;
22
+ let result = deepLod.deltaTRawSecondsAtAge(tMa);
23
+ const yearY = 2000 - tMa * 1e6;
24
+ result += dtCycles.cycleDeltaTSecondsAt('bond', yearY);
25
+ result += dtCycles.cycleDeltaTSecondsAt('hallstatt', yearY);
26
+ result += dtCycles.cycleDeltaTSecondsAt('jose5', yearY);
27
+ result += dtCycles.cycleDeltaTSecondsAt('jose4', yearY);
28
+ result += dtCycles.swingDeltaTSecondsAt(yearY);
29
+ if (dtMemo.size >= 8192) dtMemo.clear();
30
+ dtMemo.set(tMa, result);
31
+ return result;
32
+ };
33
+ /** @param {number} year @returns {number} */
34
+ const deltaTSeconds = (year) => F.DT_ANCHOR.deltaTStartSeconds + meanDeltaTSecondsAtAge(yearToTMa(year)); // the joint fit's anchor (plan 14: its own artifact)
35
+
36
+ return { deltaTSeconds, meanDeltaTSecondsAtAge };
37
+ }
38
+ /** @typedef {import('./moon-epoch.js').Ctx & ReturnType<typeof assembleDeltaT>} Ctx */
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Derived constants — the §9-order scalars from the constants context and the J2000 mass chain.
3
+ *
4
+ * One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
5
+ */
6
+ import { computeMassChainJ2000 } from '../earth/mass-chain.cjs';
7
+ import { computeMeanTropicalYearJ2000Days } from '../earth/year-lengths.cjs';
8
+
9
+ /** @param {import('./context.js').ModelInputs} ctx @returns the bindings the later slices read */
10
+ export function assembleDerivedConstants(ctx) {
11
+ const { C } = ctx;
12
+ // The mean solar year in days (the calendar's year axis) is the J2000 mean
13
+ // tropical year DERIVED in one home from the IAU sidereal year and p_A per
14
+ // Julian year (year-lengths.cjs).
15
+ const meanSolarYearDays = computeMeanTropicalYearJ2000Days(C.yearLengthRef.siderealYear, C.earthOrbital.generalPrecessionJ2000_arcsecPerYr);
16
+ // The scene epoch's ONE home — astro-reference timeReference.sceneEpochJD /
17
+ // sceneEpochYear; the SI-year axis (time-axis.js) anchors on the epoch itself.
18
+ const startmodelYear = C.timeReference.sceneEpochYear;
19
+ const startmodelJD = C.timeReference.sceneEpochJD;
20
+
21
+ const meanSiderealYearDays = C.yearLengthRef.siderealYear;
22
+ // (the sidereal year in SI seconds is the mass chain's own, below)
23
+ // The J2000 day length ω₀ is the SI day (an input).
24
+ const meanLengthOfDay = C.yearLengthRef.solarDay;
25
+ const meanTropicalYearJ2000Seconds = meanSolarYearDays * meanLengthOfDay;
26
+ // THE J2000 precession anchor p₀ in days (Julian years) — the IAU 2006
27
+ // general precession; the Moon's star-referenced periods read it.
28
+ const axialPrecessionJ2000Days = (1296000 / C.earthOrbital.generalPrecessionJ2000_arcsecPerYr) * 365.25;
29
+
30
+ const G_CONSTANT = C.physicalConstants.G_CONSTANT;
31
+ const MASS_RATIO_EARTH_MOON = C.physicalConstants.MASS_RATIO_EARTH_MOON;
32
+ const currentAUDistance = C.physicalConstants.currentAUDistance;
33
+ const moonDistanceKm = C.moonReference.moonDistance;
34
+ const moonSiderealMonthInput = C.moonReference.moonSiderealMonthInput;
35
+
36
+ // The Moon's sidereal month is the IAU input itself.
37
+ const moonSiderealMonth = moonSiderealMonthInput;
38
+
39
+ // The mass chain (Moon Kepler → GM_EM → the Earth/Moon split → the Sun, §9
40
+ // order) — ONE home: earth/mass-chain.cjs (the deep-time chain,
41
+ // deep-time-chain.js, computes the same scalars from the same context).
42
+ const mass = computeMassChainJ2000({
43
+ moonDistanceKm, moonSiderealMonthDays: moonSiderealMonth, meanSiderealYearDays,
44
+ meanLengthOfDaySeconds: meanLengthOfDay, massRatioEarthMoon: MASS_RATIO_EARTH_MOON,
45
+ currentAUDistanceKm: currentAUDistance, gravitationalConstantKm3KgS2: G_CONSTANT,
46
+ });
47
+ const moonOrbitalShift = mass.moonOrbitalShiftKm;
48
+ const moonDistanceCorrected = mass.moonDistanceCorrectedKm;
49
+ const GM_EARTH_MOON_SYSTEM = mass.gmEarthMoonSystemKm3S2;
50
+ const GM_EARTH_ALONE = mass.gmEarthAloneKm3S2;
51
+ const GM_MOON_ALONE = mass.gmMoonAloneKm3S2;
52
+ const GM_SUN_PLUS_EARTH = mass.gmSunPlusEarthKm3S2;
53
+ const GM_SUN = mass.gmSunKm3S2;
54
+ const M_SUN = mass.sunMassKg;
55
+
56
+ return { GM_EARTH_ALONE, GM_EARTH_MOON_SYSTEM, GM_MOON_ALONE, GM_SUN, GM_SUN_PLUS_EARTH, G_CONSTANT, MASS_RATIO_EARTH_MOON, M_SUN, axialPrecessionJ2000Days, currentAUDistance, meanLengthOfDay, meanSiderealYearDays, meanSolarYearDays, meanTropicalYearJ2000Seconds, moonDistanceCorrected, moonDistanceKm, moonOrbitalShift, moonSiderealMonth, moonSiderealMonthInput, startmodelJD, startmodelYear };
57
+ }
58
+ /** @typedef {import('./context.js').ModelInputs & ReturnType<typeof assembleDerivedConstants>} Ctx */
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Earth scalars of date — e, ϖ, ε read from the one-source movement; the eclipse Sun's mean elements (the research `laws` hook).
3
+ *
4
+ * One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
5
+ */
6
+
7
+ /** @param {import('./deep-time-chain.js').Ctx} ctx @returns the bindings the later slices read */
8
+ export function assembleEarthScalars(ctx) {
9
+ const { F, laws, late } = ctx;
10
+ /** @param {number} year @returns {number} */
11
+ // Plan 06 layer B — the eclipse Sun onto the series. The PUBLISHED Earth
12
+ // ϖ of date is the one-source series (the secular orbit; its J2000 element
13
+ // ≡ La2004's — the same field the D4c apsidal wheel renders). The SUN's
14
+ // equation of centre needs the MEAN elements of the era: the series plus the
15
+ // derived mean offset of the osculating channel (F.EARTH_OSCULATING_MEAN_
16
+ // OFFSET, tools/verify/earth-osculating-offset.js — the 1890–2110 mean of
17
+ // osculating − secular from the Horizons-seeded nine-body run). Measured:
18
+ // the fast osculating wobble is carried by the completion's direct terms and
19
+ // double-counts if added; the window mean is what the Sun residual holds
20
+ // (all-phase JPL sd 3.23″ → 1.59″).
21
+ const earthPerihelionDeg = (year) => late.oneSourceM.periOfDateDegAt(year);
22
+ const SUN_OFF = F.EARTH_OSCULATING_MEAN_OFFSET;
23
+ /** The eclipse Sun's ϖ of date (mean elements). @param {number} year @returns {number} */
24
+ const sunPerihelionDegAt = laws.perihelionLongitudeDegAt ?? ((year) => late.oneSourceM.periOfDateDegAt(year) + SUN_OFF.dPomArcsec / 3600);
25
+ // The obliquity of date is the one-source hybrid everywhere (the eclipse
26
+ // tier's besselian reads it here).
27
+ /** @param {number} year @returns {number} */
28
+ const obliquityDeg = (year) => late.oneSourceM.epsAt(year);
29
+ // ONE eccentricity for the whole model, two readers. The PUBLISHED Earth
30
+ // eccentricity is the one-source series (|z|, the secular orbit) and the
31
+ // SUN's e adds the derived mean offset (see sunPerihelionDegAt) — the
32
+ // clock-side machinery reads these: the eclipse Sun's equation of centre,
33
+ // the besselian Sun distance, the cardinal braid. The LUNAR chain
34
+ // (E-factor, perigee/node modulation, argument Δe² and the of-date rates)
35
+ // rides the ONE deep e — the engine's own ±10-Myr z-vector, `deepEcc` of
36
+ // the deep-time chain (deep-time-chain.js); the two agree within 4.2e-5
37
+ // in-era. The laws hook deliberately does NOT reach the deep e channel.
38
+ /** @param {number} year @returns {number} */
39
+ const eccentricityAt = (year) => late.oneSourceM.eAt(year);
40
+ /** The eclipse Sun's e (mean elements). @param {number} year @returns {number} */
41
+ const sunEccentricityAt = laws.eccentricityAt ?? ((year) => late.oneSourceM.eAt(year) + SUN_OFF.dE);
42
+ // (Earth's inclination and node of date — `inclinationDeg` /
43
+ // `ascendingNodeDeg` — are the one-source construction in inv-plane-elements.js.)
44
+
45
+ return { earthPerihelionDeg, eccentricityAt, obliquityDeg, sunEccentricityAt, sunPerihelionDegAt };
46
+ }
47
+ /** @typedef {import('./deep-time-chain.js').Ctx & ReturnType<typeof assembleEarthScalars>} Ctx */
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Earth's invariable-plane elements of date — the ONE published i_inv / Ω_inv.
3
+ *
4
+ * One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
5
+ */
6
+ import { CHAIN_ARTIFACT } from '../planets/chain-artifact.js';
7
+ import { convertNodeSFrameToEquatorOriginDeg } from '../planets/inv-plane-frame.cjs';
8
+
9
+ /** @param {import('./planet-elements-of-date.js').Ctx} ctx @returns the bindings the later slices read */
10
+ export function assembleInvPlaneElements(ctx) {
11
+ const { nodeOriginSSDeg, oneSourceM } = ctx;
12
+ // ONE published i_inv / Ω_inv everywhere — the mirror of the browser's
13
+ // inclInvPlaneModel / ascNodeInvPlaneModel (identical ops): the engine's
14
+ // own Earth-orbit normal of date (the one-source sampler — the banked
15
+ // secular series inside ±10 Myr, the mode tail beyond) against the
16
+ // artifact's invariable plane, the node expressed in the Souami & Souchay
17
+ // longitude origin through the same derived conversion the planets' node
18
+ // takes. Doc 05 banks the one-source reading at rms 0.003° vs La2010 over
19
+ // −500 kyr. Argument: the engine year (dynamical time,
20
+ // Julian from J2000 TT — the sampler's own coordinate, as the chain's).
21
+ const invPlaneGeometry = (() => {
22
+ const ip = /** @type {{ inclEclipticDeg: number, ascNodeEclipticDeg: number }} */ (/** @type {any} */ (CHAIN_ARTIFACT).invariablePlane);
23
+ const D2R = Math.PI / 180;
24
+ const si = Math.sin(ip.inclEclipticDeg * D2R), ci = Math.cos(ip.inclEclipticDeg * D2R);
25
+ const nInv = [si * Math.sin(ip.ascNodeEclipticDeg * D2R), -si * Math.cos(ip.ascNodeEclipticDeg * D2R), ci];
26
+ const xIp = [Math.cos(ip.ascNodeEclipticDeg * D2R), Math.sin(ip.ascNodeEclipticDeg * D2R), 0];
27
+ const yIp = [nInv[1] * xIp[2] - nInv[2] * xIp[1], nInv[2] * xIp[0] - nInv[0] * xIp[2], nInv[0] * xIp[1] - nInv[1] * xIp[0]];
28
+ return { ascNodeEclipticDeg: ip.ascNodeEclipticDeg, nInv, xIp, yIp };
29
+ })();
30
+ /** The engine Earth-orbit normal of date, J2000 ecliptic frame. @param {number} year @returns {number[]} */
31
+ const earthOrbitNormalJ2000At = (year) => {
32
+ const s = oneSourceM.sampleAt(year);
33
+ return [s.orbitNormalX, s.orbitNormalY, Math.sqrt(Math.max(0, 1 - s.orbitNormalX * s.orbitNormalX - s.orbitNormalY * s.orbitNormalY))];
34
+ };
35
+ /** Earth's inclination of date to the model's invariable plane, degrees. @param {number} year @returns {number} */
36
+ const inclinationDeg = (year) => {
37
+ const nE = earthOrbitNormalJ2000At(year), nInv = invPlaneGeometry.nInv;
38
+ const d = nE[0] * nInv[0] + nE[1] * nInv[1] + nE[2] * nInv[2];
39
+ return Math.acos(Math.min(1, Math.max(-1, d))) / (Math.PI / 180);
40
+ };
41
+ /** Earth's ascending node of date on the invariable plane, S&S origin, degrees in [0, 360). @param {number} year @returns {number} */
42
+ const ascendingNodeDeg = (year) => {
43
+ const D2R = Math.PI / 180;
44
+ const { ascNodeEclipticDeg, nInv, xIp, yIp } = invPlaneGeometry;
45
+ const nE = earthOrbitNormalJ2000At(year);
46
+ let N = [nInv[1] * nE[2] - nInv[2] * nE[1], nInv[2] * nE[0] - nInv[0] * nE[2], nInv[0] * nE[1] - nInv[1] * nE[0]];
47
+ const L = Math.hypot(N[0], N[1], N[2]);
48
+ N = [N[0] / L, N[1] / L, N[2] / L];
49
+ const sFrameDeg = ((Math.atan2(N[0] * yIp[0] + N[1] * yIp[1] + N[2] * yIp[2],
50
+ N[0] * xIp[0] + N[1] * xIp[1] + N[2] * xIp[2]) / D2R + ascNodeEclipticDeg) % 360 + 360) % 360;
51
+ return convertNodeSFrameToEquatorOriginDeg(sFrameDeg, nodeOriginSSDeg());
52
+ };
53
+
54
+ return { ascendingNodeDeg, inclinationDeg };
55
+ }
56
+ /** @typedef {import('./planet-elements-of-date.js').Ctx & ReturnType<typeof assembleInvPlaneElements>} Ctx */