@essrt/physics 23.6.0 → 24.1.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.
- package/package.json +2 -2
- package/src/cardinal/one-source-structure.cjs +3 -1
- package/src/chain-cycles/index.cjs +80 -10
- package/src/constants/generated.d.ts +13 -13
- package/src/constants/generated.js +14 -14
- package/src/earth/deep-orbital-history.cjs +48 -34
- package/src/earth/year-lengths.cjs +2 -1
- package/src/eclipse/besselian.cjs +24 -11
- package/src/index.js +3 -3
- package/src/model/lunar-theory.js +2 -2
- package/src/model/planet-records.js +1 -1
- package/src/model/sun-and-eclipses.js +1 -1
- package/src/model/surface.js +2 -0
- package/src/model/year-day-lengths.js +2 -2
- package/src/moon/arguments.cjs +68 -6
- package/src/planets/chain-artifact.js +2 -2
- package/src/planets/keplerian-chain.cjs +2 -2
- package/src/planets/model.cjs +2 -2
- package/types/constants/generated.d.ts +13 -13
- package/types/earth/deep-orbital-history.d.cts +23 -0
- package/types/eclipse/besselian.d.cts +37 -18
- package/types/index.d.ts +1 -1
- package/types/model/sun-and-eclipses.d.ts +1 -0
- package/types/model/surface.d.ts +2 -0
- package/types/model.d.ts +1 -0
- package/types/planets/keplerian-chain.d.cts +1 -1
- package/types/planets/model.d.cts +3 -3
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@essrt/physics",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "24.1.0",
|
|
4
4
|
"description": "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.",
|
|
5
5
|
"essrt": {
|
|
6
|
-
"modelVersion": "v26.
|
|
6
|
+
"modelVersion": "v26.7",
|
|
7
7
|
"_note": "The model identity this package version ships (IP-unified-architecture §10: package semver tracks the API axis; the model version + CONSTANTS_HASH/COEFFICIENTS_HASH in src/constants/ carry the scientific identity). The version-pinning gate enforces this pairing against public/input/model-version.json."
|
|
8
8
|
},
|
|
9
9
|
"type": "module",
|
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
* like createDeepOrbitalHistory injects its surfaces.
|
|
41
41
|
*/
|
|
42
42
|
|
|
43
|
+
const { wrap180 } = require('../math/angles.cjs');
|
|
44
|
+
|
|
43
45
|
const D2R = Math.PI / 180;
|
|
44
46
|
|
|
45
47
|
/** Cardinal longitudes, equinox of date. @type {Record<string, number>} */
|
|
@@ -134,7 +136,7 @@ function createCardinalStructure({ sampleAt, tropicalYearSecondsAtYearFn }) {
|
|
|
134
136
|
function anomalisticYearSeconds(year) {
|
|
135
137
|
const p0 = sampleAt(year).periOfDateDeg;
|
|
136
138
|
const p1 = sampleAt(year + 1).periOfDateDeg;
|
|
137
|
-
const dPeriDeg = (
|
|
139
|
+
const dPeriDeg = wrap180(p1 - p0); // wrapped, prograde + (math/angles; a one-year step, far below 180°)
|
|
138
140
|
return tropicalYearSecondsAtYearFn(year + 0.5) * 360 / (360 - dPeriDeg);
|
|
139
141
|
}
|
|
140
142
|
|
|
@@ -26,11 +26,18 @@
|
|
|
26
26
|
* tableAnchorYear vs driftRefYear pair).
|
|
27
27
|
*
|
|
28
28
|
* NUMERICS ARE LOAD-BEARING: fixed 10-yr grid over ±250 kyr, per-cell
|
|
29
|
-
* 3-point Simpson at build, LINEAR interpolation on read;
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
29
|
+
* 3-point Simpson at build, LINEAR interpolation on read; beyond the dense
|
|
30
|
+
* grid a COARSE cumulative table (1-kyr cells, the same per-cell Simpson,
|
|
31
|
+
* anchored at the dense edge) grown lazily in 250-kyr chunks in the direction
|
|
32
|
+
* asked for — the owner's playback at 1,000 yr/s past year 250,000 had fallen
|
|
33
|
+
* to 190 ms of JS per frame: the former fallback, an adaptive Simpson of up to
|
|
34
|
+
* 1,024 samples per call cached by exact year pair, re-integrated every one
|
|
35
|
+
* of the four Moon chains from the grid edge on every frame, each sample a
|
|
36
|
+
* deep-time LOD/α evaluation. The adaptive Simpson remains only for a chain
|
|
37
|
+
* undefined somewhere in range (null) or beyond the coarse limit (±600 Myr,
|
|
38
|
+
* past the model's domain). An "analytically better" integrator here would
|
|
39
|
+
* detach the runtime from the certified numbers: the in-range path is
|
|
40
|
+
* byte-identical to before.
|
|
34
41
|
*/
|
|
35
42
|
|
|
36
43
|
'use strict';
|
|
@@ -125,6 +132,62 @@ function createChainCycleIntegrator({
|
|
|
125
132
|
return tab[i] + (idxF - i) * (tab[i + 1] - tab[i]);
|
|
126
133
|
}
|
|
127
134
|
|
|
135
|
+
// ── The coarse extension beyond the dense grid ─────────────────────────────
|
|
136
|
+
// Per chain and per direction, a cumulative table in COARSE_STEP cells that
|
|
137
|
+
// starts at the dense edge with the edge's own value and grows by COARSE_CHUNK
|
|
138
|
+
// years whenever a year beyond its reach is asked for. The cells are built
|
|
139
|
+
// with the same 3-point Simpson as the dense grid; the read is the same
|
|
140
|
+
// linear interpolation. The period laws vary on Myr scales at deep time, so
|
|
141
|
+
// 1-kyr cells hold the count to far below a cycle — the old fallback sampled
|
|
142
|
+
// ~1 per kyr too, but re-did the whole span on every call.
|
|
143
|
+
const COARSE_STEP = 1000, COARSE_CHUNK = 250000, COARSE_LIMIT_YEARS = 600e6;
|
|
144
|
+
/** @typedef {{ cum: number[], ok: boolean }} CoarseSide */
|
|
145
|
+
/** @type {WeakMap<PeriodSecondsAtAge, { fwd: CoarseSide, bwd: CoarseSide }>} */
|
|
146
|
+
const coarse = new WeakMap();
|
|
147
|
+
/** @param {PeriodSecondsAtAge} periodFnSeconds @param {number} y @returns {number | null} cycles per tick */
|
|
148
|
+
const integrandAt = (periodFnSeconds, y) => {
|
|
149
|
+
const tPeriodS = periodFnSeconds((ageAnchorYear - y) / 1e6);
|
|
150
|
+
return tPeriodS === null ? null : tropicalYearJ2000Seconds / tPeriodS;
|
|
151
|
+
};
|
|
152
|
+
/** Grow one side until it covers `distance` years from the dense edge (or fails on a null chain).
|
|
153
|
+
* @param {PeriodSecondsAtAge} periodFnSeconds @param {CoarseSide} side @param {number} edgeYear
|
|
154
|
+
* @param {1|-1} dir @param {number} distance */
|
|
155
|
+
function growCoarse(periodFnSeconds, side, edgeYear, dir, distance) {
|
|
156
|
+
const need = Math.ceil(distance / COARSE_STEP) + 1;
|
|
157
|
+
let target = side.cum.length;
|
|
158
|
+
while (target < need) target += COARSE_CHUNK / COARSE_STEP;
|
|
159
|
+
for (let i = side.cum.length; i < target && side.ok; i++) {
|
|
160
|
+
const y0 = edgeYear + dir * (i - 1) * COARSE_STEP, y1 = y0 + dir * COARSE_STEP;
|
|
161
|
+
const f0 = integrandAt(periodFnSeconds, y0), fm = integrandAt(periodFnSeconds, (y0 + y1) / 2), f1 = integrandAt(periodFnSeconds, y1);
|
|
162
|
+
if (f0 === null || fm === null || f1 === null) { side.ok = false; break; }
|
|
163
|
+
side.cum.push(side.cum[i - 1] + dir * (f0 + 4 * fm + f1) * (COARSE_STEP / 6));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/** C(y) beyond the dense grid, or null when the chain is undefined on the way there.
|
|
167
|
+
* @param {PeriodSecondsAtAge} periodFnSeconds @param {Float64Array} tab @param {number} y @returns {number | null} */
|
|
168
|
+
function coarseAt(periodFnSeconds, tab, y) {
|
|
169
|
+
let c = coarse.get(periodFnSeconds);
|
|
170
|
+
if (!c) {
|
|
171
|
+
c = { fwd: { cum: [tab[tab.length - 1]], ok: true }, bwd: { cum: [tab[0]], ok: true } };
|
|
172
|
+
coarse.set(periodFnSeconds, c);
|
|
173
|
+
}
|
|
174
|
+
const dir = y >= gridMaxYear ? 1 : -1;
|
|
175
|
+
const side = dir === 1 ? c.fwd : c.bwd;
|
|
176
|
+
const edgeYear = dir === 1 ? gridMaxYear : gridMinYear;
|
|
177
|
+
const distance = Math.abs(y - edgeYear);
|
|
178
|
+
if (distance > COARSE_LIMIT_YEARS) return null;
|
|
179
|
+
if (side.cum.length < Math.ceil(distance / COARSE_STEP) + 1) growCoarse(periodFnSeconds, side, edgeYear, dir, distance);
|
|
180
|
+
if (!side.ok || side.cum.length < Math.ceil(distance / COARSE_STEP) + 1) return null;
|
|
181
|
+
const idxF = distance / COARSE_STEP;
|
|
182
|
+
const i = Math.floor(idxF);
|
|
183
|
+
return side.cum[i] + (idxF - i) * (side.cum[i + 1] - side.cum[i]);
|
|
184
|
+
}
|
|
185
|
+
/** C(y) anywhere the tables reach: dense inside the grid, coarse beyond.
|
|
186
|
+
* @param {PeriodSecondsAtAge} periodFnSeconds @param {Float64Array} tab @param {number} y @returns {number | null} */
|
|
187
|
+
function cumulativeAt(periodFnSeconds, tab, y) {
|
|
188
|
+
return (y > gridMinYear && y < gridMaxYear) ? tableAt(tab, y) : coarseAt(periodFnSeconds, tab, y);
|
|
189
|
+
}
|
|
190
|
+
|
|
128
191
|
/** Cycles of the chain between two calendar years. Snapshot mode: linear
|
|
129
192
|
* at the J2000 rate. Deep time: table lookup in ±range, adaptive Simpson
|
|
130
193
|
* beyond. null past the tidal-lock asymptote.
|
|
@@ -147,10 +210,16 @@ function createChainCycleIntegrator({
|
|
|
147
210
|
return dy * tropicalYearJ2000Seconds / tJ2000;
|
|
148
211
|
}
|
|
149
212
|
|
|
150
|
-
// Cumulative-table fast path (deterministic, call-order independent)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
213
|
+
// Cumulative-table fast path (deterministic, call-order independent): the
|
|
214
|
+
// dense grid inside ±250 kyr — byte-identical to before — and the lazily
|
|
215
|
+
// grown coarse extension beyond it.
|
|
216
|
+
const tab = table(periodFnSeconds);
|
|
217
|
+
if (tab !== null) {
|
|
218
|
+
if (yearA > gridMinYear && yearA < gridMaxYear && yearB > gridMinYear && yearB < gridMaxYear) {
|
|
219
|
+
return tableAt(tab, yearB) - tableAt(tab, yearA);
|
|
220
|
+
}
|
|
221
|
+
const cA = cumulativeAt(periodFnSeconds, tab, yearA), cB = cumulativeAt(periodFnSeconds, tab, yearB);
|
|
222
|
+
if (cA !== null && cB !== null) return cB - cA;
|
|
154
223
|
}
|
|
155
224
|
|
|
156
225
|
let cache = caches.get(periodFnSeconds);
|
|
@@ -162,7 +231,8 @@ function createChainCycleIntegrator({
|
|
|
162
231
|
const hit = cache.get(cacheKey);
|
|
163
232
|
if (hit !== undefined) return hit;
|
|
164
233
|
|
|
165
|
-
// Adaptive Simpson
|
|
234
|
+
// Adaptive Simpson (a chain undefined in range, or beyond the coarse limit):
|
|
235
|
+
// ~1 sample per 1 kyr, capped at 1024 to bound per-call cost.
|
|
166
236
|
let n = Math.max(32, Math.ceil(Math.abs(dy) / 1000));
|
|
167
237
|
if (n > 1024) n = 1024;
|
|
168
238
|
if (n % 2 === 1) n++;
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
// Gives the TypeScript website full type safety at the boundary (§2g) while
|
|
3
3
|
// packages/physics stays JavaScript.
|
|
4
4
|
|
|
5
|
-
export declare const CONSTANTS_HASH: "
|
|
5
|
+
export declare const CONSTANTS_HASH: "da713fc2d846767d";
|
|
6
6
|
|
|
7
7
|
export declare const MODEL_VERSION: string;
|
|
8
8
|
|
|
9
9
|
export declare const PREPRINT_DOI: string;
|
|
10
10
|
|
|
11
11
|
export declare const DEFAULT_CONSTANTS: {
|
|
12
|
-
readonly hash: "
|
|
12
|
+
readonly hash: "da713fc2d846767d";
|
|
13
13
|
readonly additionalBodiesReference: {
|
|
14
14
|
"pluto": {
|
|
15
|
-
"
|
|
15
|
+
"orbitalPeriodInputDays": number;
|
|
16
16
|
"orbitalEccentricityJ2000": number;
|
|
17
17
|
"eclipticInclinationJ2000": number;
|
|
18
18
|
"invPlaneInclinationJ2000": number;
|
|
@@ -25,7 +25,7 @@ export declare const DEFAULT_CONSTANTS: {
|
|
|
25
25
|
"ascendingNodeInvPlaneClosedForm": number;
|
|
26
26
|
};
|
|
27
27
|
"halleys": {
|
|
28
|
-
"
|
|
28
|
+
"orbitalPeriodInputDays": number;
|
|
29
29
|
"orbitalEccentricityJ2000": number;
|
|
30
30
|
"eclipticInclinationJ2000": number;
|
|
31
31
|
"invPlaneInclinationJ2000": number;
|
|
@@ -37,7 +37,7 @@ export declare const DEFAULT_CONSTANTS: {
|
|
|
37
37
|
"rotationPeriodDays": number;
|
|
38
38
|
};
|
|
39
39
|
"eros": {
|
|
40
|
-
"
|
|
40
|
+
"orbitalPeriodInputDays": number;
|
|
41
41
|
"orbitalEccentricityJ2000": number;
|
|
42
42
|
"eclipticInclinationJ2000": number;
|
|
43
43
|
"invPlaneInclinationJ2000": number;
|
|
@@ -49,7 +49,7 @@ export declare const DEFAULT_CONSTANTS: {
|
|
|
49
49
|
"rotationPeriodDays": number;
|
|
50
50
|
};
|
|
51
51
|
"ceres": {
|
|
52
|
-
"
|
|
52
|
+
"orbitalPeriodInputDays": number;
|
|
53
53
|
"orbitalEccentricityJ2000": number;
|
|
54
54
|
"eclipticInclinationJ2000": number;
|
|
55
55
|
"invPlaneInclinationJ2000": number;
|
|
@@ -200,7 +200,7 @@ export declare const DEFAULT_CONSTANTS: {
|
|
|
200
200
|
};
|
|
201
201
|
readonly planetOrbitalElements: {
|
|
202
202
|
"mercury": {
|
|
203
|
-
"
|
|
203
|
+
"orbitalPeriodInputDays": number;
|
|
204
204
|
"orbitalEccentricityJ2000": number;
|
|
205
205
|
"axialTiltJ2000": number;
|
|
206
206
|
"eclipticInclinationJ2000": number;
|
|
@@ -212,7 +212,7 @@ export declare const DEFAULT_CONSTANTS: {
|
|
|
212
212
|
"ascendingNodeInvPlaneClosedForm": number;
|
|
213
213
|
};
|
|
214
214
|
"venus": {
|
|
215
|
-
"
|
|
215
|
+
"orbitalPeriodInputDays": number;
|
|
216
216
|
"orbitalEccentricityJ2000": number;
|
|
217
217
|
"axialTiltJ2000": number;
|
|
218
218
|
"eclipticInclinationJ2000": number;
|
|
@@ -225,7 +225,7 @@ export declare const DEFAULT_CONSTANTS: {
|
|
|
225
225
|
"ascendingNodeInvPlaneClosedForm": number;
|
|
226
226
|
};
|
|
227
227
|
"mars": {
|
|
228
|
-
"
|
|
228
|
+
"orbitalPeriodInputDays": number;
|
|
229
229
|
"orbitalEccentricityJ2000": number;
|
|
230
230
|
"axialTiltJ2000": number;
|
|
231
231
|
"eclipticInclinationJ2000": number;
|
|
@@ -238,7 +238,7 @@ export declare const DEFAULT_CONSTANTS: {
|
|
|
238
238
|
"ascendingNodeInvPlaneClosedForm": number;
|
|
239
239
|
};
|
|
240
240
|
"jupiter": {
|
|
241
|
-
"
|
|
241
|
+
"orbitalPeriodInputDays": number;
|
|
242
242
|
"orbitalEccentricityJ2000": number;
|
|
243
243
|
"axialTiltJ2000": number;
|
|
244
244
|
"eclipticInclinationJ2000": number;
|
|
@@ -251,7 +251,7 @@ export declare const DEFAULT_CONSTANTS: {
|
|
|
251
251
|
"ascendingNodeInvPlaneClosedForm": number;
|
|
252
252
|
};
|
|
253
253
|
"saturn": {
|
|
254
|
-
"
|
|
254
|
+
"orbitalPeriodInputDays": number;
|
|
255
255
|
"orbitalEccentricityJ2000": number;
|
|
256
256
|
"axialTiltJ2000": number;
|
|
257
257
|
"eclipticInclinationJ2000": number;
|
|
@@ -264,7 +264,7 @@ export declare const DEFAULT_CONSTANTS: {
|
|
|
264
264
|
"ascendingNodeInvPlaneClosedForm": number;
|
|
265
265
|
};
|
|
266
266
|
"uranus": {
|
|
267
|
-
"
|
|
267
|
+
"orbitalPeriodInputDays": number;
|
|
268
268
|
"orbitalEccentricityJ2000": number;
|
|
269
269
|
"axialTiltJ2000": number;
|
|
270
270
|
"eclipticInclinationJ2000": number;
|
|
@@ -277,7 +277,7 @@ export declare const DEFAULT_CONSTANTS: {
|
|
|
277
277
|
"ascendingNodeInvPlaneClosedForm": number;
|
|
278
278
|
};
|
|
279
279
|
"neptune": {
|
|
280
|
-
"
|
|
280
|
+
"orbitalPeriodInputDays": number;
|
|
281
281
|
"orbitalEccentricityJ2000": number;
|
|
282
282
|
"axialTiltJ2000": number;
|
|
283
283
|
"eclipticInclinationJ2000": number;
|
|
@@ -36,20 +36,20 @@
|
|
|
36
36
|
* carry it so a counterfactual is reproducible (§2d).
|
|
37
37
|
* @type {string}
|
|
38
38
|
*/
|
|
39
|
-
export const CONSTANTS_HASH = "
|
|
39
|
+
export const CONSTANTS_HASH = "da713fc2d846767d";
|
|
40
40
|
|
|
41
41
|
/** Model version label — single source: public/input/model-version.json (§10 two-axis scheme). */
|
|
42
|
-
export const MODEL_VERSION = "v26.
|
|
42
|
+
export const MODEL_VERSION = "v26.7";
|
|
43
43
|
|
|
44
44
|
/** Canonical preprint DOI — single source: public/input/model-version.json. */
|
|
45
45
|
export const PREPRINT_DOI = "10.21203/rs.3.rs-8758810/v4";
|
|
46
46
|
|
|
47
47
|
/** @type {Readonly<Record<string, unknown>>} */
|
|
48
48
|
export const DEFAULT_CONSTANTS = Object.freeze({
|
|
49
|
-
hash: "
|
|
49
|
+
hash: "da713fc2d846767d",
|
|
50
50
|
additionalBodiesReference: {
|
|
51
51
|
"pluto": {
|
|
52
|
-
"
|
|
52
|
+
"orbitalPeriodInputDays": 90465,
|
|
53
53
|
"orbitalEccentricityJ2000": 0.2488273,
|
|
54
54
|
"eclipticInclinationJ2000": 17.14001,
|
|
55
55
|
"invPlaneInclinationJ2000": 15.5639473,
|
|
@@ -62,7 +62,7 @@ export const DEFAULT_CONSTANTS = Object.freeze({
|
|
|
62
62
|
"ascendingNodeInvPlaneClosedForm": 101.06
|
|
63
63
|
},
|
|
64
64
|
"halleys": {
|
|
65
|
-
"
|
|
65
|
+
"orbitalPeriodInputDays": 27503,
|
|
66
66
|
"orbitalEccentricityJ2000": 0.96714291,
|
|
67
67
|
"eclipticInclinationJ2000": 162.26269,
|
|
68
68
|
"invPlaneInclinationJ2000": 150,
|
|
@@ -74,7 +74,7 @@ export const DEFAULT_CONSTANTS = Object.freeze({
|
|
|
74
74
|
"rotationPeriodDays": 2.2
|
|
75
75
|
},
|
|
76
76
|
"eros": {
|
|
77
|
-
"
|
|
77
|
+
"orbitalPeriodInputDays": 642.93,
|
|
78
78
|
"orbitalEccentricityJ2000": 0.2229512,
|
|
79
79
|
"eclipticInclinationJ2000": 10.8276,
|
|
80
80
|
"invPlaneInclinationJ2000": 9.25,
|
|
@@ -86,7 +86,7 @@ export const DEFAULT_CONSTANTS = Object.freeze({
|
|
|
86
86
|
"rotationPeriodDays": 0.21958333344885
|
|
87
87
|
},
|
|
88
88
|
"ceres": {
|
|
89
|
-
"
|
|
89
|
+
"orbitalPeriodInputDays": 1680.5,
|
|
90
90
|
"orbitalEccentricityJ2000": 0.0755347,
|
|
91
91
|
"eclipticInclinationJ2000": 10.59407,
|
|
92
92
|
"invPlaneInclinationJ2000": 0.4331698,
|
|
@@ -237,7 +237,7 @@ export const DEFAULT_CONSTANTS = Object.freeze({
|
|
|
237
237
|
},
|
|
238
238
|
planetOrbitalElements: {
|
|
239
239
|
"mercury": {
|
|
240
|
-
"
|
|
240
|
+
"orbitalPeriodInputDays": 87.9683,
|
|
241
241
|
"orbitalEccentricityJ2000": 0.20563593,
|
|
242
242
|
"axialTiltJ2000": 0.03,
|
|
243
243
|
"eclipticInclinationJ2000": 7.00497902,
|
|
@@ -249,7 +249,7 @@ export const DEFAULT_CONSTANTS = Object.freeze({
|
|
|
249
249
|
"ascendingNodeInvPlaneClosedForm": 32.83
|
|
250
250
|
},
|
|
251
251
|
"venus": {
|
|
252
|
-
"
|
|
252
|
+
"orbitalPeriodInputDays": 224.695,
|
|
253
253
|
"orbitalEccentricityJ2000": 0.00677672,
|
|
254
254
|
"axialTiltJ2000": 2.6392,
|
|
255
255
|
"eclipticInclinationJ2000": 3.39467605,
|
|
@@ -262,7 +262,7 @@ export const DEFAULT_CONSTANTS = Object.freeze({
|
|
|
262
262
|
"ascendingNodeInvPlaneClosedForm": 54.7
|
|
263
263
|
},
|
|
264
264
|
"mars": {
|
|
265
|
-
"
|
|
265
|
+
"orbitalPeriodInputDays": 686.93,
|
|
266
266
|
"orbitalEccentricityJ2000": 0.0933941,
|
|
267
267
|
"axialTiltJ2000": 25.19,
|
|
268
268
|
"eclipticInclinationJ2000": 1.84969142,
|
|
@@ -275,7 +275,7 @@ export const DEFAULT_CONSTANTS = Object.freeze({
|
|
|
275
275
|
"ascendingNodeInvPlaneClosedForm": 354.87
|
|
276
276
|
},
|
|
277
277
|
"jupiter": {
|
|
278
|
-
"
|
|
278
|
+
"orbitalPeriodInputDays": 4330.53,
|
|
279
279
|
"orbitalEccentricityJ2000": 0.04838624,
|
|
280
280
|
"axialTiltJ2000": 3.13,
|
|
281
281
|
"eclipticInclinationJ2000": 1.30439695,
|
|
@@ -288,7 +288,7 @@ export const DEFAULT_CONSTANTS = Object.freeze({
|
|
|
288
288
|
"ascendingNodeInvPlaneClosedForm": 312.89
|
|
289
289
|
},
|
|
290
290
|
"saturn": {
|
|
291
|
-
"
|
|
291
|
+
"orbitalPeriodInputDays": 10747,
|
|
292
292
|
"orbitalEccentricityJ2000": 0.05386179,
|
|
293
293
|
"axialTiltJ2000": 26.73,
|
|
294
294
|
"eclipticInclinationJ2000": 2.48599187,
|
|
@@ -301,7 +301,7 @@ export const DEFAULT_CONSTANTS = Object.freeze({
|
|
|
301
301
|
"ascendingNodeInvPlaneClosedForm": 118.81
|
|
302
302
|
},
|
|
303
303
|
"uranus": {
|
|
304
|
-
"
|
|
304
|
+
"orbitalPeriodInputDays": 30586,
|
|
305
305
|
"orbitalEccentricityJ2000": 0.04725744,
|
|
306
306
|
"axialTiltJ2000": 82.23,
|
|
307
307
|
"eclipticInclinationJ2000": 0.77263783,
|
|
@@ -314,7 +314,7 @@ export const DEFAULT_CONSTANTS = Object.freeze({
|
|
|
314
314
|
"ascendingNodeInvPlaneClosedForm": 307.8
|
|
315
315
|
},
|
|
316
316
|
"neptune": {
|
|
317
|
-
"
|
|
317
|
+
"orbitalPeriodInputDays": 59800,
|
|
318
318
|
"orbitalEccentricityJ2000": 0.00859048,
|
|
319
319
|
"axialTiltJ2000": 28.32,
|
|
320
320
|
"eclipticInclinationJ2000": 1.77004347,
|
|
@@ -30,6 +30,49 @@
|
|
|
30
30
|
|
|
31
31
|
'use strict';
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* The CO-ROTATING anchored mode sum — the z-side evaluator of the history
|
|
35
|
+
* (the ϖ̇ fix, measured): the plain DC anchor residual is a spurious
|
|
36
|
+
* zero-frequency mode that preserves the J2000 VALUE but dilutes the local
|
|
37
|
+
* argument rate by |sum(0)|/|anchor| (z: 11.47 → 7.93″/yr, a 92 s-class
|
|
38
|
+
* anomalistic-year bias in the mode tier). Here the residual ROTATES at the
|
|
39
|
+
* raw sum's own local arg-rate g* = Im(ż·z̄)/|z|² (analytic), so at t = 0
|
|
40
|
+
* both the value AND the rate are exact, and at depth the residual is one
|
|
41
|
+
* |R|-amplitude term at a physical frequency instead of a DC offset (deep
|
|
42
|
+
* envelope measured unchanged: [0.0001, 0.0672] → [0.0003, 0.0678] over
|
|
43
|
+
* 10–50 Myr). Exported: e(t) = hypot(sum(t)) needs no spin integration,
|
|
44
|
+
* so a fixture can read a deep eccentricity node here (bit-identical at the
|
|
45
|
+
* grid nodes; the published surface is the grid's linear interpolation and
|
|
46
|
+
* stays on the history).
|
|
47
|
+
* @param {ReadonlyArray<{omegaRadPerYr: number, re: number, im: number}>} modes
|
|
48
|
+
* @param {[number, number]} anchor the J2000 value (q, p) the sum is pinned to
|
|
49
|
+
* @returns {(t: number) => [number, number]} t in years from J2000
|
|
50
|
+
*/
|
|
51
|
+
function createCoRotatingAnchoredModeSum(modes, anchor) {
|
|
52
|
+
const sum = (/** @type {number} */ t) => {
|
|
53
|
+
let re = 0, im = 0;
|
|
54
|
+
for (const m of modes) {
|
|
55
|
+
const c = Math.cos(m.omegaRadPerYr * t), s = Math.sin(m.omegaRadPerYr * t);
|
|
56
|
+
re += m.re * c - m.im * s;
|
|
57
|
+
im += m.re * s + m.im * c;
|
|
58
|
+
}
|
|
59
|
+
return [re, im];
|
|
60
|
+
};
|
|
61
|
+
let q0 = 0, p0 = 0, dq0 = 0, dp0 = 0;
|
|
62
|
+
for (const m of modes) {
|
|
63
|
+
q0 += m.re; p0 += m.im;
|
|
64
|
+
dq0 += -m.omegaRadPerYr * m.im;
|
|
65
|
+
dp0 += m.omegaRadPerYr * m.re;
|
|
66
|
+
}
|
|
67
|
+
const gStar = (dp0 * q0 - dq0 * p0) / (q0 * q0 + p0 * p0);
|
|
68
|
+
const R = [anchor[0] - q0, anchor[1] - p0];
|
|
69
|
+
return (/** @type {number} */ t) => {
|
|
70
|
+
const [x, y] = sum(t);
|
|
71
|
+
const c = Math.cos(gStar * t), s = Math.sin(gStar * t);
|
|
72
|
+
return [x + R[0] * c - R[1] * s, y + R[0] * s + R[1] * c];
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
33
76
|
/**
|
|
34
77
|
* @param {{
|
|
35
78
|
* zModes: ReadonlyArray<{omegaRadPerYr: number, re: number, im: number}>,
|
|
@@ -97,43 +140,14 @@ function createDeepOrbitalHistory({
|
|
|
97
140
|
// CO-ROTATING anchor (the ϖ̇ fix, measured): the DC residual above is a
|
|
98
141
|
// spurious zero-frequency mode — it preserves the J2000 VALUE but dilutes
|
|
99
142
|
// the local argument rate by |sum(0)|/|anchor| (z: 11.47 → 7.93″/yr, a
|
|
100
|
-
// 92 s-class anomalistic-year bias in the mode tier).
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
// depth the residual is one |R|-amplitude term at a physical frequency
|
|
104
|
-
// instead of a DC offset (deep envelope measured unchanged:
|
|
105
|
-
// [0.0001, 0.0672] → [0.0003, 0.0678] over 10–50 Myr).
|
|
143
|
+
// 92 s-class anomalistic-year bias in the mode tier). The co-rotating form
|
|
144
|
+
// is the module-level createCoRotatingAnchoredModeSum (exported — the
|
|
145
|
+
// plan-06 fixture reads the deep eccentricity from it directly).
|
|
106
146
|
// z ONLY: ζ keeps the DC anchor DELIBERATELY — its anchor is ~0 by
|
|
107
147
|
// definition (Earth's inclination to the J2000 ecliptic at J2000), i.e.
|
|
108
148
|
// the coordinate origin; a rotating residual there would inject a fake
|
|
109
149
|
// wobble into n̂(t).
|
|
110
|
-
const mkAnchoredCoRot =
|
|
111
|
-
/** @type {ReadonlyArray<{omegaRadPerYr: number, re: number, im: number}>} */ modes,
|
|
112
|
-
/** @type {[number, number]} */ anchor,
|
|
113
|
-
) => {
|
|
114
|
-
const sum = (/** @type {number} */ t) => {
|
|
115
|
-
let re = 0, im = 0;
|
|
116
|
-
for (const m of modes) {
|
|
117
|
-
const c = Math.cos(m.omegaRadPerYr * t), s = Math.sin(m.omegaRadPerYr * t);
|
|
118
|
-
re += m.re * c - m.im * s;
|
|
119
|
-
im += m.re * s + m.im * c;
|
|
120
|
-
}
|
|
121
|
-
return [re, im];
|
|
122
|
-
};
|
|
123
|
-
let q0 = 0, p0 = 0, dq0 = 0, dp0 = 0;
|
|
124
|
-
for (const m of modes) {
|
|
125
|
-
q0 += m.re; p0 += m.im;
|
|
126
|
-
dq0 += -m.omegaRadPerYr * m.im;
|
|
127
|
-
dp0 += m.omegaRadPerYr * m.re;
|
|
128
|
-
}
|
|
129
|
-
const gStar = (dp0 * q0 - dq0 * p0) / (q0 * q0 + p0 * p0);
|
|
130
|
-
const R = [anchor[0] - q0, anchor[1] - p0];
|
|
131
|
-
return (/** @type {number} */ t) => {
|
|
132
|
-
const [x, y] = sum(t);
|
|
133
|
-
const c = Math.cos(gStar * t), s = Math.sin(gStar * t);
|
|
134
|
-
return [x + R[0] * c - R[1] * s, y + R[0] * s + R[1] * c];
|
|
135
|
-
};
|
|
136
|
-
};
|
|
150
|
+
const mkAnchoredCoRot = createCoRotatingAnchoredModeSum;
|
|
137
151
|
const s2h = Math.sin(anchorInclEclipticDeg / 2 * D2R);
|
|
138
152
|
const zetaAnchor = /** @type {[number, number]} */ (
|
|
139
153
|
[s2h * Math.cos(anchorAscNodeEclipticDeg * D2R), s2h * Math.sin(anchorAscNodeEclipticDeg * D2R)]);
|
|
@@ -676,4 +690,4 @@ function createDeepOrbitalHistory({
|
|
|
676
690
|
};
|
|
677
691
|
}
|
|
678
692
|
|
|
679
|
-
module.exports = { createDeepOrbitalHistory };
|
|
693
|
+
module.exports = { createDeepOrbitalHistory, createCoRotatingAnchoredModeSum };
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
// • The frozen era clock stays the certified era device (opt-out /
|
|
43
43
|
// era-certification record), not a member of this family.
|
|
44
44
|
|
|
45
|
+
const { wrap180 } = require('../math/angles.cjs');
|
|
45
46
|
const { createCardinalStructure } = require('../cardinal/one-source-structure.cjs');
|
|
46
47
|
const { createSiderealYearChannel } = require('./sidereal-year-channel.cjs');
|
|
47
48
|
|
|
@@ -64,7 +65,7 @@ function createYearLengths({ sampleAt, massLossSiderealSecondsAtYearFn }) {
|
|
|
64
65
|
if (typeof sampleAt !== 'function' || typeof massLossSiderealSecondsAtYearFn !== 'function') {
|
|
65
66
|
throw new Error('createYearLengths: sampleAt and massLossSiderealSecondsAtYearFn are required');
|
|
66
67
|
}
|
|
67
|
-
const w180 =
|
|
68
|
+
const w180 = wrap180; // math/angles — bit-identical to the former inline form for the sub-degree differences wrapped here
|
|
68
69
|
const chan = createSiderealYearChannel({ massLossSiderealSecondsAtYearFn });
|
|
69
70
|
/** A year length from a longer year and a retrograde angular rate of the
|
|
70
71
|
* reference point, RATE form: 1/T = 1/T_long + p/(360°·JY) — exact (the
|
|
@@ -69,6 +69,8 @@
|
|
|
69
69
|
|
|
70
70
|
'use strict';
|
|
71
71
|
|
|
72
|
+
const { wrap180 } = require('../math/angles.cjs');
|
|
73
|
+
|
|
72
74
|
/**
|
|
73
75
|
* @typedef {Object} BesselianDeps
|
|
74
76
|
* @property {(dDaysTT: number) => {lonDeg: number, latDeg: number, distKm: number}} moonFullAtDaysTT
|
|
@@ -88,6 +90,23 @@
|
|
|
88
90
|
* gmstMeanSiderealT2Deg: number, speedOfLightKmS: number }} constants
|
|
89
91
|
*/
|
|
90
92
|
|
|
93
|
+
/** Mean sidereal time at Greenwich, degrees (mean equinox — pairs with the
|
|
94
|
+
* mean-of-date body longitudes; coefficients single-sourced from
|
|
95
|
+
* astro-reference.json physicalConstants). THE ONE GMST of the model: the
|
|
96
|
+
* eclipse location tier, the API's observer tier, the eclipse and lunar
|
|
97
|
+
* audits and the browser's diagnostics all read it. Meeus 12.4's
|
|
98
|
+
* cubic term (−T³/38,710,000) is deliberately absent: it is 0.9″ in the
|
|
99
|
+
* eclipse canon and at most 93″ (6 s of time) at the edge of the observer
|
|
100
|
+
* era, and every recorded eclipse and lunar value was measured without it.
|
|
101
|
+
* @param {number} jdUT
|
|
102
|
+
* @param {{ j2000JD: number, julianCenturyDays: number, gmstMeanSiderealT0Deg: number, gmstMeanSiderealRateDegPerDay: number, gmstMeanSiderealT2Deg: number }} K
|
|
103
|
+
* @returns {number} */
|
|
104
|
+
function computeGmstDeg(jdUT, K) {
|
|
105
|
+
const T = (jdUT - K.j2000JD) / K.julianCenturyDays;
|
|
106
|
+
return ((K.gmstMeanSiderealT0Deg + K.gmstMeanSiderealRateDegPerDay * (jdUT - K.j2000JD)
|
|
107
|
+
+ K.gmstMeanSiderealT2Deg * T * T) % 360 + 360) % 360;
|
|
108
|
+
}
|
|
109
|
+
|
|
91
110
|
/** @param {BesselianDeps} deps */
|
|
92
111
|
function createBesselian(deps) {
|
|
93
112
|
const K = deps.constants;
|
|
@@ -97,14 +116,8 @@ function createBesselian(deps) {
|
|
|
97
116
|
const R_MOON_KM = K.moonDiameterKm / 2;
|
|
98
117
|
const F = 1 / K.earthFlatteningInverse;
|
|
99
118
|
|
|
100
|
-
/**
|
|
101
|
-
|
|
102
|
-
* astro-reference.json physicalConstants). @param {number} jdUT @returns {number} */
|
|
103
|
-
function gmstDeg(jdUT) {
|
|
104
|
-
const T = (jdUT - K.j2000JD) / K.julianCenturyDays;
|
|
105
|
-
return ((K.gmstMeanSiderealT0Deg + K.gmstMeanSiderealRateDegPerDay * (jdUT - K.j2000JD)
|
|
106
|
-
+ K.gmstMeanSiderealT2Deg * T * T) % 360 + 360) % 360;
|
|
107
|
-
}
|
|
119
|
+
/** @param {number} jdUT @returns {number} */
|
|
120
|
+
const gmstDeg = (jdUT) => computeGmstDeg(jdUT, K);
|
|
108
121
|
|
|
109
122
|
/** Ecliptic-of-date → equatorial-of-date vector (km).
|
|
110
123
|
* @param {number} lonDeg @param {number} betDeg @param {number} distKm
|
|
@@ -150,7 +163,7 @@ function createBesselian(deps) {
|
|
|
150
163
|
const moon = deps.moonFullAtDaysTT(dTT);
|
|
151
164
|
const H_DAYS = 0.02;
|
|
152
165
|
const dLon = deps.moonFullAtDaysTT(dTT + H_DAYS).lonDeg - deps.moonFullAtDaysTT(dTT - H_DAYS).lonDeg;
|
|
153
|
-
const moonRateDegPerDay = (
|
|
166
|
+
const moonRateDegPerDay = wrap180(dLon) / (2 * H_DAYS);
|
|
154
167
|
const tauDays = moon.distKm / K.speedOfLightKmS / 86400;
|
|
155
168
|
return {
|
|
156
169
|
S: eclToEq(sunLon, 0, sunDistanceKm(year, sunLon), eps),
|
|
@@ -310,7 +323,7 @@ function createBesselian(deps) {
|
|
|
310
323
|
return { kind, magnitude: Math.max(0, atMax.magnitude), maxJd, contacts, centralDurationSeconds };
|
|
311
324
|
}
|
|
312
325
|
|
|
313
|
-
return { umbraGroundAt, shadowStateAt, localCircumstances };
|
|
326
|
+
return { umbraGroundAt, shadowStateAt, localCircumstances, gmstDeg };
|
|
314
327
|
}
|
|
315
328
|
|
|
316
|
-
module.exports = { createBesselian };
|
|
329
|
+
module.exports = { createBesselian, computeGmstDeg };
|
package/src/index.js
CHANGED
|
@@ -138,9 +138,9 @@ export * as planetOrbitChain from './planets/orbit-chain.cjs';
|
|
|
138
138
|
export { computeSecularShape } from './planets/secular-shape.cjs';
|
|
139
139
|
// `computeApsidalTangentDegPerYr` is the chain's ±150-yr central tangent of the perihelion
|
|
140
140
|
// longitude (the panels' "Prec." cell — osculating, NOT the ±1-kyr secular stencil of the
|
|
141
|
-
// "Secular" cell)
|
|
142
|
-
//
|
|
143
|
-
export { buildPlanetChainsFromArtifactData, computePlanetElementsAtYear, computeHeliocentricEclipticFromElements, computePoissonArgRad, computeOsculatingElements, solveKeplerRad,
|
|
141
|
+
// "Secular" cell). Its former name `computeApsidalSecularDegPerYr` left the API at 24.0.0
|
|
142
|
+
// (no reader in the simulator, the website or the paper).
|
|
143
|
+
export { buildPlanetChainsFromArtifactData, computePlanetElementsAtYear, computeHeliocentricEclipticFromElements, computePoissonArgRad, computeOsculatingElements, solveKeplerRad, computeApsidalTangentDegPerYr, ANCHOR_EPOCH_YEAR, ANCHOR_EPOCH_JD } from './planets/keplerian-chain.cjs';
|
|
144
144
|
export { computeEquatorNodeOriginSFrameDeg, convertNodeSFrameToEquatorOriginDeg } from './planets/inv-plane-frame.cjs';
|
|
145
145
|
export { createSecularSeriesOverride } from './planets/secular-series.cjs';
|
|
146
146
|
// The planets' spin channel (the precession constant from each planet's own
|
|
@@ -61,7 +61,7 @@ export function assembleLunarTheory(ctx) {
|
|
|
61
61
|
const anomalisticMonthPeriodFn = (tMa) => moonChain.anomalisticMonthSecondsAtAge(tMa);
|
|
62
62
|
/** @param {number} tMa @returns {number|null} */
|
|
63
63
|
const siderealMonthPeriodFn = (tMa) => moonChain.siderealMonthSecondsAtAge(tMa);
|
|
64
|
-
const jupiterT0Seconds = C.planetOrbitalElements.jupiter.
|
|
64
|
+
const jupiterT0Seconds = C.planetOrbitalElements.jupiter.orbitalPeriodInputDays * 86400;
|
|
65
65
|
/** @param {number} tMa @returns {number} */
|
|
66
66
|
const jupiterPeriodFn = (tMa) => driver2PeriodSecondsAtAge(tMa, jupiterT0Seconds, EPOCH_PARAMS.solarMassLossFracPerYear);
|
|
67
67
|
/** @param {number} a @param {number} b @returns {number|null} */
|
|
@@ -164,7 +164,7 @@ export function assembleLunarTheory(ctx) {
|
|
|
164
164
|
// docs/109 §22 for the derivation)
|
|
165
165
|
const fwA2RateDegPerCy = 2 * ((360 * 36525) / moonTropicalMonthDays)
|
|
166
166
|
- (360 * 36525) / moonAnomalisticMonthDays
|
|
167
|
-
- 2 * ((360 * 36525) / C.planetOrbitalElements.jupiter.
|
|
167
|
+
- 2 * ((360 * 36525) / C.planetOrbitalElements.jupiter.orbitalPeriodInputDays);
|
|
168
168
|
const fwA3RateDegPerCy = (360 * 36525) / moonSiderealMonth;
|
|
169
169
|
|
|
170
170
|
// Meeus Ch. 47 truncated series (framework-native arguments + E-factor)
|
|
@@ -46,7 +46,7 @@ export function assemblePlanetRecords(ctx) {
|
|
|
46
46
|
const sArc = chainModes.s[k] && chainModes.s[k].arcsecPerYr;
|
|
47
47
|
PUBLISHED_PLANET_RECORDS[k] = Object.freeze({
|
|
48
48
|
name: PLANET_DISPLAY_NAMES[k], // presentation — planets/display-names, the deleted JSON's last leaf (plan 11 step 6)
|
|
49
|
-
|
|
49
|
+
orbitalPeriodInputDays: ar.orbitalPeriodInputDays,
|
|
50
50
|
longitudePerihelion: ar.longitudePerihelion,
|
|
51
51
|
ascendingNodeInvPlane: ar.ascendingNodeInvPlaneClosedForm, // the closed-form verified J2000 node — astro-reference since plan 11 step 2
|
|
52
52
|
invPlaneInclinationJ2000: ar.invPlaneInclinationJ2000,
|
|
@@ -132,7 +132,7 @@ export function assembleSunAndEclipses(ctx) {
|
|
|
132
132
|
// FQ-5 N3 — the completion's carriers are FRAMEWORK-derived from the
|
|
133
133
|
// model's own planet period records (the Moon-elongation rate from the
|
|
134
134
|
// sidereal month/year identity). Plan 06 I2 — the carriers are SIDEREAL
|
|
135
|
-
// (measured): the records'
|
|
135
|
+
// (measured): the records' orbitalPeriodInputDays are OF-DATE periods (their
|
|
136
136
|
// rates sit +1.39..1.62°/cy above the VSOP sidereal mean motions — the
|
|
137
137
|
// framework precession plus input rounding), and a perturbation argument
|
|
138
138
|
// is inertial (D'Alembert: only Σk = 0 arguments are frame-free), so on
|
package/src/model/surface.js
CHANGED
|
@@ -312,6 +312,8 @@ export function assembleSurface(ctx) {
|
|
|
312
312
|
}),
|
|
313
313
|
// 20.3g location tier (see eclipse/besselian.cjs):
|
|
314
314
|
umbraGroundAtJD: /** @param {number} jd @returns {{latDeg: number, lonDeg: number} | null} */ (jd) => besselian.umbraGroundAt(jd),
|
|
315
|
+
/** THE ONE GMST — mean sidereal time at Greenwich, degrees, at a UT JD (eclipse/besselian.cjs computeGmstDeg; the observer tier's hour angle). @param {number} jdUT @returns {number} */
|
|
316
|
+
gmstDegAtJD: (jdUT) => besselian.gmstDeg(jdUT),
|
|
315
317
|
solarLocalCircumstances: /** @param {number} jdGreatest @param {number} latDeg @param {number} lonDeg */ (jdGreatest, latDeg, lonDeg) => besselian.localCircumstances(jdGreatest, latDeg, lonDeg),
|
|
316
318
|
}),
|
|
317
319
|
climate: Object.freeze({
|
|
@@ -7,7 +7,7 @@ import { ONE_FAMILY_WINDOW_YEARS } from '../earth/year-lengths.cjs';
|
|
|
7
7
|
|
|
8
8
|
/** @param {import('./earth-scalars.js').Ctx} ctx @returns the bindings the later slices read */
|
|
9
9
|
export function assembleYearDayLengths(ctx) {
|
|
10
|
-
const {
|
|
10
|
+
const { chain, late, deepLod, meanLengthOfDay, yearToTMa } = ctx;
|
|
11
11
|
// The year lengths of date in DAYS are the published SI year lengths over
|
|
12
12
|
// the kinematic day of date; the kinematic day of date is the Layer-0 tidal
|
|
13
13
|
// mean LOD, exactly ω₀ (the SI day) at J2000.
|
|
@@ -19,7 +19,7 @@ export function assembleYearDayLengths(ctx) {
|
|
|
19
19
|
* surface all read it (the identifier predates the anchor's move onto the
|
|
20
20
|
* IAU input). @returns {number} */
|
|
21
21
|
function certifiedAxialPrecessionJ2000Years() {
|
|
22
|
-
return
|
|
22
|
+
return chain.certifiedAxialPrecessionJ2000Years(); // the ONE home: the deep-time chain's export (1,296,000 / p_A on the same constants)
|
|
23
23
|
}
|
|
24
24
|
/** Kinematic day of date (Layer 0 tidal mean LOD; ω₀ at J2000). Past the
|
|
25
25
|
* tidal-lock asymptote the chain is null — the J2000 day stands in.
|