diff --git a/packages/tide-predictor/src/constituents/2MK3.ts b/packages/tide-predictor/src/constituents/2MK3.ts new file mode 100644 index 0000000..198d5ad --- /dev/null +++ b/packages/tide-predictor/src/constituents/2MK3.ts @@ -0,0 +1,13 @@ +import { defineCompoundConstituent } from "./definition.js"; +import M2 from "./M2.js"; +import O1 from "./O1.js"; + +/** + * Shallow-water terdiurnal (2MK3 = M2 + O1). + * Lunar-lunar interaction from M2 semi-diurnal and O1 diurnal components. + * Generated in shallow-water environments; typical amplitude <1 cm. + */ +export default defineCompoundConstituent("2MK3", [ + { constituent: M2, factor: 1 }, + { constituent: O1, factor: 1 }, +]); diff --git a/packages/tide-predictor/src/constituents/2MK5.ts b/packages/tide-predictor/src/constituents/2MK5.ts new file mode 100644 index 0000000..73da81d --- /dev/null +++ b/packages/tide-predictor/src/constituents/2MK5.ts @@ -0,0 +1,17 @@ +import { defineCompoundConstituent } from "./definition.js"; +import M2 from "./M2.js"; +import K1 from "./K1.js"; + +/** + * Shallow-water fifth-diurnal from M2-K1 interaction. + * + * Note: Found in coastal tide predictions, especially in enclosed bays and harbors. + * Amplitude typically 0.1-0.5 cm depending on location and water depth. + * Shallow-water constituent only; not present in deep ocean. + * + * @see NOAA CO-OPS + */ +export default defineCompoundConstituent("2MK5", [ + { constituent: M2, factor: 2 }, + { constituent: K1, factor: 1 }, +]); diff --git a/packages/tide-predictor/src/constituents/2MO5.ts b/packages/tide-predictor/src/constituents/2MO5.ts new file mode 100644 index 0000000..ff02516 --- /dev/null +++ b/packages/tide-predictor/src/constituents/2MO5.ts @@ -0,0 +1,17 @@ +import { defineCompoundConstituent } from "./definition.js"; +import M2 from "./M2.js"; +import O1 from "./O1.js"; + +/** + * Shallow-water fifth-diurnal from M2-O1 interaction. + * + * Note: Primarily shallow-water coastal phenomenon, not present in deep ocean. + * Amplitude typically <0.5 cm except in extreme shallow-water or enclosed basins. + * Found in coastal tide predictions alongside other shallow-water constituents. + * + * @see NOAA CO-OPS + */ +export default defineCompoundConstituent("2MO5", [ + { constituent: M2, factor: 2 }, + { constituent: O1, factor: 1 }, +]); diff --git a/packages/tide-predictor/src/constituents/2MS6.ts b/packages/tide-predictor/src/constituents/2MS6.ts new file mode 100644 index 0000000..0e2fd94 --- /dev/null +++ b/packages/tide-predictor/src/constituents/2MS6.ts @@ -0,0 +1,14 @@ +import { defineCompoundConstituent } from "./definition.js"; +import M2 from "./M2.js"; +import S2 from "./S2.js"; + +/** + * Sixth-diurnal shallow-water interaction: 2×M2 + S2. + * + * Generated only in shallow water; typical amplitude 0.01-0.2 meters depending on depth. + * Included in IHO shallow-water constituent tables and NOAA analysis (order #37). + */ +export default defineCompoundConstituent("2MS6", [ + { constituent: M2, factor: 2 }, + { constituent: S2, factor: 1 }, +]); diff --git a/packages/tide-predictor/src/constituents/2N2.ts b/packages/tide-predictor/src/constituents/2N2.ts new file mode 100644 index 0000000..904e41c --- /dev/null +++ b/packages/tide-predictor/src/constituents/2N2.ts @@ -0,0 +1,9 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunar semi-diurnal (2N2). + * Second-order lunar semi-diurnal from Moon's orbital ellipticity. + * Amplitude typically 5-10% of M2; significant in semi-diurnal analysis. + */ +export default defineConstituent("2N2", [2, -2, 0, 2, 0, 0, 0], nc.uM2, nc.fM2); diff --git a/packages/tide-predictor/src/constituents/2Q1.ts b/packages/tide-predictor/src/constituents/2Q1.ts new file mode 100644 index 0000000..1771c74 --- /dev/null +++ b/packages/tide-predictor/src/constituents/2Q1.ts @@ -0,0 +1,14 @@ +import { defineCompoundConstituent } from "./definition.js"; +import N2 from "./N2.js"; +import J1 from "./J1.js"; + +/** + * Shallow-water diurnal (2Q1 = N2-J1). + * Derived from interaction of semi-diurnal N2 and diurnal J1 constituents. + * Amplitude typically very small; rarely significant. + * Found only in shallow-water regions with strong tidal distortion. + */ +export default defineCompoundConstituent("2Q1", [ + { constituent: N2, factor: 1 }, + { constituent: J1, factor: -1 }, +]); diff --git a/packages/tide-predictor/src/constituents/2SM2.ts b/packages/tide-predictor/src/constituents/2SM2.ts new file mode 100644 index 0000000..1d71762 --- /dev/null +++ b/packages/tide-predictor/src/constituents/2SM2.ts @@ -0,0 +1,14 @@ +import { defineCompoundConstituent } from "./definition.js"; +import S2 from "./S2.js"; +import M2 from "./M2.js"; + +/** + * Shallow-water semi-diurnal (2SM2). + * Compound constituent: 2×S2 - M2 + * Solar-lunar interaction constituent generated in shallow-water environments. + * Amplitude typically <2% of M2; complementary to MU2. + */ +export default defineCompoundConstituent("2SM2", [ + { constituent: S2, factor: 2 }, + { constituent: M2, factor: -1 }, +]); diff --git a/packages/tide-predictor/src/constituents/3L2.ts b/packages/tide-predictor/src/constituents/3L2.ts new file mode 100644 index 0000000..e8fc821 --- /dev/null +++ b/packages/tide-predictor/src/constituents/3L2.ts @@ -0,0 +1,13 @@ +import { defineCompoundConstituent } from "./definition.js"; +import L2 from "./L2.js"; + +/** + * Triple lunar elliptic; 3 times L2 interaction. + * + * Warning: Not in standard IHO constituent bank. Mainly historical/theoretical interest. + * Very small amplitude (<0.1 cm); found only in extreme shallow water or enclosed basins. + * Often ignored in routine tide predictions. + * + * @see Schureman, P. (1958). Manual of Harmonic Analysis and Prediction of Tides + */ +export default defineCompoundConstituent("3L2", [{ constituent: L2, factor: 3 }]); diff --git a/packages/tide-predictor/src/constituents/3N2.ts b/packages/tide-predictor/src/constituents/3N2.ts new file mode 100644 index 0000000..8bc1d8c --- /dev/null +++ b/packages/tide-predictor/src/constituents/3N2.ts @@ -0,0 +1,13 @@ +import { defineCompoundConstituent } from "./definition.js"; +import N2 from "./N2.js"; + +/** + * Triple N2 shallow-water harmonic (3 × N2). + * + * Note: Shallow-water constituent with definition based on compound frequency estimates. + * Typical amplitude <0.5 cm; often <0.1 cm except in extreme shallow-water or enclosed basins. + * Rarely significant in routine tide predictions. + * + * @see NOAA CO-OPS shallow-water constituents + */ +export default defineCompoundConstituent("3N2", [{ constituent: N2, factor: 3 }]); diff --git a/packages/tide-predictor/src/constituents/EP2.ts b/packages/tide-predictor/src/constituents/EP2.ts new file mode 100644 index 0000000..66247ee --- /dev/null +++ b/packages/tide-predictor/src/constituents/EP2.ts @@ -0,0 +1,7 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunar elliptic semi-diurnal constituent (ε2). + */ +export default defineConstituent("EP2", [2, -3, 2, 1, 0, 0, 0], nc.uM2, nc.fM2); diff --git a/packages/tide-predictor/src/constituents/J1.ts b/packages/tide-predictor/src/constituents/J1.ts new file mode 100644 index 0000000..aa2914a --- /dev/null +++ b/packages/tide-predictor/src/constituents/J1.ts @@ -0,0 +1,10 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunar diurnal (J1). + * Shallow-water lunar diurnal constituent; generally much smaller than O1 and K1. + * Typically important only in shallow-water systems and enclosed basins. + * Amplitude usually <5% of O1; rarely significant in routine predictions. + */ +export default defineConstituent("J1", [1, 2, 0, -1, 0, 0, -1], nc.uJ1, nc.fJ1); diff --git a/packages/tide-predictor/src/constituents/K1.ts b/packages/tide-predictor/src/constituents/K1.ts new file mode 100644 index 0000000..739a5e7 --- /dev/null +++ b/packages/tide-predictor/src/constituents/K1.ts @@ -0,0 +1,9 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunisolar diurnal (K1). + * Combined lunar and solar diurnal constituent; strongest diurnal tide in many regions. + * Often comparable in amplitude to O1; amplitude ratio K1/O1 varies with latitude. + */ +export default defineConstituent("K1", [1, 1, 0, 0, 0, 0, -1], nc.uK1, nc.fK1); diff --git a/packages/tide-predictor/src/constituents/K2.ts b/packages/tide-predictor/src/constituents/K2.ts new file mode 100644 index 0000000..2538318 --- /dev/null +++ b/packages/tide-predictor/src/constituents/K2.ts @@ -0,0 +1,10 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunisolar semi-diurnal (K2). + * Combined lunar and solar semi-diurnal constituent from declination effects. + * Amplitude typically 10-30% of S2; second-largest solar-related semi-diurnal component. + * Important in semi-diurnal tidal analysis, especially at mid-latitudes. + */ +export default defineConstituent("K2", [2, 2, 0, 0, 0, 0, 0], nc.uK2, nc.fK2); diff --git a/packages/tide-predictor/src/constituents/L2.ts b/packages/tide-predictor/src/constituents/L2.ts new file mode 100644 index 0000000..19085d2 --- /dev/null +++ b/packages/tide-predictor/src/constituents/L2.ts @@ -0,0 +1,10 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunar elliptic semi-diurnal (L2). + * Secondary lunar elliptic semi-diurnal from Moon's orbital variations and perigee effects. + * Amplitude typically 1-3% of M2; often grouped with other lunar elliptic constituents. + * Important in detailed harmonic analyses of semi-diurnal tides. + */ +export default defineConstituent("L2", [2, 1, 0, -1, 0, 0, 2], nc.uL2, nc.fL2); diff --git a/packages/tide-predictor/src/constituents/LAM2.ts b/packages/tide-predictor/src/constituents/LAM2.ts new file mode 100644 index 0000000..9129145 --- /dev/null +++ b/packages/tide-predictor/src/constituents/LAM2.ts @@ -0,0 +1,8 @@ +import LAMBDA2 from "./LAMBDA2.js"; + +/** + * Alias for compatibility between NOAA and IHO + * + * @see LAMBDA2 + */ +export default LAMBDA2; diff --git a/packages/tide-predictor/src/constituents/LAMBDA2.ts b/packages/tide-predictor/src/constituents/LAMBDA2.ts new file mode 100644 index 0000000..c93e98d --- /dev/null +++ b/packages/tide-predictor/src/constituents/LAMBDA2.ts @@ -0,0 +1,10 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunar semi-diurnal (λ2, lambda2). + * Lunar semi-diurnal constituent from Moon's perigee effects. + * Amplitude typically <5% of M2; important in detailed constituent analysis. + * IHO standard designation (previously abbreviated as LAM2). + */ +export default defineConstituent("LAMBDA2", [2, 1, -2, 1, 0, 0, 2], nc.uM2, nc.fM2); diff --git a/packages/tide-predictor/src/constituents/M1.ts b/packages/tide-predictor/src/constituents/M1.ts new file mode 100644 index 0000000..d99d0b0 --- /dev/null +++ b/packages/tide-predictor/src/constituents/M1.ts @@ -0,0 +1,10 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunar diurnal elliptic (M1). + * Secondary lunar diurnal constituent from Moon's elliptical orbit. + * Typically very small amplitude; usually <1% of O1. + * Rarely significant except in detailed harmonic analyses. + */ +export default defineConstituent("M1", [1, 0, 0, 0, 0, 0, 1], nc.uM1, nc.fM1); diff --git a/packages/tide-predictor/src/constituents/M2.ts b/packages/tide-predictor/src/constituents/M2.ts new file mode 100644 index 0000000..e94c5c2 --- /dev/null +++ b/packages/tide-predictor/src/constituents/M2.ts @@ -0,0 +1,10 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunar semi-diurnal (M2). + * Primary principal lunar constituent; largest semi-diurnal tidal component globally. + * Amplitude varies 10-20% over lunar node cycle; typically 0.2-0.5m in coastal areas. + * Base constituent for many compound shallow-water constituents. + */ +export default defineConstituent("M2", [2, 0, 0, 0, 0, 0, 0], nc.uM2, nc.fM2); diff --git a/packages/tide-predictor/src/constituents/M3.ts b/packages/tide-predictor/src/constituents/M3.ts new file mode 100644 index 0000000..2387c01 --- /dev/null +++ b/packages/tide-predictor/src/constituents/M3.ts @@ -0,0 +1,20 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunar terdiurnal (M3). + * Third-diurnal lunar constituent from Moon's orbital motion. + * Typically found in shallow water and resonant systems. + * Amplitude usually <2% of M2; important in some shallow-water analyses. + */ +// Third diurnal +export default defineConstituent( + "M3", + [3, 0, 0, 0, 0, 0, 0], + (a) => { + return nc.uModd(a, 3); + }, + (a) => { + return nc.fModd(a, 3); + }, +); diff --git a/packages/tide-predictor/src/constituents/M4.ts b/packages/tide-predictor/src/constituents/M4.ts new file mode 100644 index 0000000..7a31785 --- /dev/null +++ b/packages/tide-predictor/src/constituents/M4.ts @@ -0,0 +1,10 @@ +import { defineCompoundConstituent } from "./definition.js"; +import M2 from "./M2.js"; + +/** + * Shallow-water quarter-diurnal (M4 = 2×M2). + * First overtide of M2; generated in shallow-water environments. + * Amplitude typically 2-10% of M2; largest of the quarter-diurnal constituents. + * Important indicator of shallow-water tidal distortion. + */ +export default defineCompoundConstituent("M4", [{ constituent: M2, factor: 2 }]); diff --git a/packages/tide-predictor/src/constituents/M6.ts b/packages/tide-predictor/src/constituents/M6.ts new file mode 100644 index 0000000..7a59e75 --- /dev/null +++ b/packages/tide-predictor/src/constituents/M6.ts @@ -0,0 +1,10 @@ +import { defineCompoundConstituent } from "./definition.js"; +import M2 from "./M2.js"; + +/** + * Shallow-water sixth-diurnal (M6 = 3×M2). + * Second overtide of M2; generated in shallow-water environments. + * Amplitude typically 0.5-3% of M2; important in extreme shallow water. + * Indicator of significant tidal distortion and non-linear effects. + */ +export default defineCompoundConstituent("M6", [{ constituent: M2, factor: 3 }]); diff --git a/packages/tide-predictor/src/constituents/M8.ts b/packages/tide-predictor/src/constituents/M8.ts new file mode 100644 index 0000000..ee95e88 --- /dev/null +++ b/packages/tide-predictor/src/constituents/M8.ts @@ -0,0 +1,10 @@ +import { defineCompoundConstituent } from "./definition.js"; +import M2 from "./M2.js"; + +/** + * Shallow-water eighth-diurnal (M8 = 4×M2). + * Third overtide of M2; generated in extreme shallow-water environments. + * Amplitude typically <0.5% of M2; very rarely significant. + * Found only in highly distorted tidal regimes (extreme shallow water, enclosed basins). + */ +export default defineCompoundConstituent("M8", [{ constituent: M2, factor: 4 }]); diff --git a/packages/tide-predictor/src/constituents/MA2.ts b/packages/tide-predictor/src/constituents/MA2.ts new file mode 100644 index 0000000..ee0fb2d --- /dev/null +++ b/packages/tide-predictor/src/constituents/MA2.ts @@ -0,0 +1,11 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunar variational semi-diurnal constituent (μ2, mu2). + * Derived from Moon's orbital parameter variations. + * + * Note: Often included with M2 family in modern analysis. Minor constituent with + * location-dependent amplitude. + */ +export default defineConstituent("MA2", [2, 0, -1, 0, 0, 0, 0], nc.uM2, nc.fM2); diff --git a/packages/tide-predictor/src/constituents/MB2.ts b/packages/tide-predictor/src/constituents/MB2.ts new file mode 100644 index 0000000..cf51164 --- /dev/null +++ b/packages/tide-predictor/src/constituents/MB2.ts @@ -0,0 +1,17 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunar elliptic constituent from parameter variations. + * + * From https://iho.int/mtg_docs/com_wg/IHOTC/IHOTC_Misc/TWCWG_Constituent_list.pdf: + * + * > MB2 was originally called Ma2 but this became ambiguous when spoken, or typed on + * > computers without lower case, and so it was initially changed to MA2*. However, this + * > in turn was thought to be clumsy and hence MB2 was finally adopted. Although + * > theoretically they should have the same values of u and f as M2, they are so small + * > that they are commonly treated as having values of u = 0 and f = 1. + * + * @see Schureman, P. (1958). Manual of Harmonic Analysis and Prediction of Tides + */ +export default defineConstituent("MB2", [2, 0, 1, 0, 0, 0, 0], nc.uM2, nc.fM2); diff --git a/packages/tide-predictor/src/constituents/MF.ts b/packages/tide-predictor/src/constituents/MF.ts new file mode 100644 index 0000000..57dcc0c --- /dev/null +++ b/packages/tide-predictor/src/constituents/MF.ts @@ -0,0 +1,9 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunar fortnightly (MF). + * Long-period constituent from lunar inequality interactions. + * Significant in long-term water level records and coastal resonances. + */ +export default defineConstituent("MF", [0, 2, 0, 0, 0, 0, 0], nc.uMf, nc.fMf); diff --git a/packages/tide-predictor/src/constituents/MK3.ts b/packages/tide-predictor/src/constituents/MK3.ts new file mode 100644 index 0000000..e00f705 --- /dev/null +++ b/packages/tide-predictor/src/constituents/MK3.ts @@ -0,0 +1,15 @@ +import { defineCompoundConstituent } from "./definition.js"; +import M2 from "./M2.js"; +import K1 from "./K1.js"; + +/** + * Shallow-water terdiurnal (MK3). + * Compound constituent: M2 + K1 + * Lunisolar interaction from M2 semi-diurnal and K1 diurnal components. + * Generated in shallow-water environments; typical amplitude 0.5-2 cm. + * Often paired with 2MK3 in terdiurnal tide analysis. + */ +export default defineCompoundConstituent("MK3", [ + { constituent: M2, factor: 1 }, + { constituent: K1, factor: 1 }, +]); diff --git a/packages/tide-predictor/src/constituents/MKS2.ts b/packages/tide-predictor/src/constituents/MKS2.ts new file mode 100644 index 0000000..95a599e --- /dev/null +++ b/packages/tide-predictor/src/constituents/MKS2.ts @@ -0,0 +1,19 @@ +import { defineCompoundConstituent } from "./definition.js"; +import M2 from "./M2.js"; +import K1 from "./K1.js"; +import S2 from "./S2.js"; + +/** + * Three-way shallow-water interaction of M2, K1, and S2. + * + * Warning: Not in standard IHO constituent bank. Shallow-water specific constituent + * with definition varying by application and water depth. Use with caution and document + * the specific convention used in your analysis. + * + * @see NOAA CO-OPS shallow-water constituents + */ +export default defineCompoundConstituent("MKS2", [ + { constituent: M2, factor: 1 }, + { constituent: K1, factor: 1 }, + { constituent: S2, factor: -1 }, +]); diff --git a/packages/tide-predictor/src/constituents/MM.ts b/packages/tide-predictor/src/constituents/MM.ts new file mode 100644 index 0000000..cca61b7 --- /dev/null +++ b/packages/tide-predictor/src/constituents/MM.ts @@ -0,0 +1,9 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunar monthly (MM). + * Long-period constituent from lunar declination variations. + * Important for long-term water level studies. + */ +export default defineConstituent("MM", [0, 1, 0, -1, 0, 0, 0], nc.uZero, nc.fMm); diff --git a/packages/tide-predictor/src/constituents/MN4.ts b/packages/tide-predictor/src/constituents/MN4.ts new file mode 100644 index 0000000..7b3f3cd --- /dev/null +++ b/packages/tide-predictor/src/constituents/MN4.ts @@ -0,0 +1,15 @@ +import { defineCompoundConstituent } from "./definition.js"; +import M2 from "./M2.js"; +import N2 from "./N2.js"; + +/** + * Shallow-water quarter-diurnal (MN4). + * Compound constituent: M2 + N2 + * Lunar-lunar elliptic interaction from M2 and N2 semi-diurnal components. + * Generated in shallow-water environments; typical amplitude 1-5 cm. + * Often significant in shallow seas and estuaries. + */ +export default defineCompoundConstituent("MN4", [ + { constituent: M2, factor: 1 }, + { constituent: N2, factor: 1 }, +]); diff --git a/packages/tide-predictor/src/constituents/MS4.ts b/packages/tide-predictor/src/constituents/MS4.ts new file mode 100644 index 0000000..4e929bb --- /dev/null +++ b/packages/tide-predictor/src/constituents/MS4.ts @@ -0,0 +1,15 @@ +import { defineCompoundConstituent } from "./definition.js"; +import M2 from "./M2.js"; +import S2 from "./S2.js"; + +/** + * Shallow-water quarter-diurnal (MS4). + * Compound constituent: M2 + S2 + * Lunar-solar interaction from M2 and S2 semi-diurnal components. + * Generated in shallow-water environments; typical amplitude 1-5 cm. + * Often second-largest quarter-diurnal component after M4. + */ +export default defineCompoundConstituent("MS4", [ + { constituent: M2, factor: 1 }, + { constituent: S2, factor: 1 }, +]); diff --git a/packages/tide-predictor/src/constituents/MSF.ts b/packages/tide-predictor/src/constituents/MSF.ts new file mode 100644 index 0000000..7acae0b --- /dev/null +++ b/packages/tide-predictor/src/constituents/MSF.ts @@ -0,0 +1,15 @@ +import { defineCompoundConstituent } from "./definition.js"; +import S2 from "./S2.js"; +import M2 from "./M2.js"; + +/** + * Lunisolar synodic fortnightly (MSF = S2-M2). + * Long-period constituent representing beat frequency between solar S2 and lunar M2. + * Manifests as fortnightly modulation of tidal range (spring-neap cycle). + * Amplitude typically 5-15% of M2; represents the primary spring-neap variation. + * Important for tidal range predictions and coastal flooding assessments. + */ +export default defineCompoundConstituent("MSF", [ + { constituent: S2, factor: 1 }, + { constituent: M2, factor: -1 }, +]); diff --git a/packages/tide-predictor/src/constituents/MSQM.ts b/packages/tide-predictor/src/constituents/MSQM.ts new file mode 100644 index 0000000..4b0ca2c --- /dev/null +++ b/packages/tide-predictor/src/constituents/MSQM.ts @@ -0,0 +1,20 @@ +import { defineCompoundConstituent } from "./definition.js"; +import M2 from "./M2.js"; +import S2 from "./S2.js"; +import K1 from "./K1.js"; + +/** + * Lunar-solar interaction compound constituent. + * + * Warning: Non-standard constituent not in IHO or modern NOAA standard tables. + * Definition varies significantly across sources. Rarely used in modern tide prediction. + * Appears in Schureman's tables as variant shallow-water interaction. + * + * @see NOAA CO-OPS shallow-water constituents + * @see Schureman shallow-water analysis tables + */ +export default defineCompoundConstituent("MSQM", [ + { constituent: M2, factor: 1 }, + { constituent: S2, factor: 1 }, + { constituent: K1, factor: 1 }, +]); diff --git a/packages/tide-predictor/src/constituents/MTM.ts b/packages/tide-predictor/src/constituents/MTM.ts new file mode 100644 index 0000000..8bff616 --- /dev/null +++ b/packages/tide-predictor/src/constituents/MTM.ts @@ -0,0 +1,18 @@ +import { defineCompoundConstituent } from "./definition.js"; +import M2 from "./M2.js"; +import T2 from "./T2.js"; + +/** + * Lunar-solar shallow-water interaction (M2 modulated by lunar orbit). + * + * Warning: Not in modern IHO standard constituents; mostly historical interest. + * Often replaced by specific ν2, λ2, or other lunar elliptic terms in modern analysis. + * Amplitude is location and depth-dependent. + * + * @see NOAA CO-OPS shallow-water variants + * @see Schureman Manual + */ +export default defineCompoundConstituent("MTM", [ + { constituent: M2, factor: 1 }, + { constituent: T2, factor: 1 }, +]); diff --git a/packages/tide-predictor/src/constituents/MU2.ts b/packages/tide-predictor/src/constituents/MU2.ts new file mode 100644 index 0000000..d1ea392 --- /dev/null +++ b/packages/tide-predictor/src/constituents/MU2.ts @@ -0,0 +1,14 @@ +import { defineCompoundConstituent } from "./definition.js"; +import M2 from "./M2.js"; +import S2 from "./S2.js"; + +/** + * Shallow-water semi-diurnal (MU2 or μ2). + * Compound constituent: 2×M2 - S2 + * Lunar variational constituent generated in shallow-water environments. + * Amplitude typically <2% of M2; found in coastal shallow-water predictions. + */ +export default defineCompoundConstituent("MU2", [ + { constituent: M2, factor: 2 }, + { constituent: S2, factor: -1 }, +]); diff --git a/packages/tide-predictor/src/constituents/N2.ts b/packages/tide-predictor/src/constituents/N2.ts new file mode 100644 index 0000000..f075ac8 --- /dev/null +++ b/packages/tide-predictor/src/constituents/N2.ts @@ -0,0 +1,9 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunar elliptic semi-diurnal (N2). + * Primary lunar elliptic semi-diurnal constituent from Moon's orbital variations. + * Amplitude typically 5-15% of M2; third-largest semi-diurnal constituent. + */ +export default defineConstituent("N2", [2, -1, 0, 1, 0, 0, 0], nc.uM2, nc.fM2); diff --git a/packages/tide-predictor/src/constituents/N4.ts b/packages/tide-predictor/src/constituents/N4.ts new file mode 100644 index 0000000..b225fff --- /dev/null +++ b/packages/tide-predictor/src/constituents/N4.ts @@ -0,0 +1,10 @@ +import { defineCompoundConstituent } from "./definition.js"; +import N2 from "./N2.js"; + +/** + * Second overtide of N2; shallow-water quarter-diurnal harmonic. + * Amplitude ranges 0.25 to 2.25 times mean due to 18.613-year lunar node cycle. + * + * Note: Shallow-water constituent, typically found in tide predictions for coastal areas. + */ +export default defineCompoundConstituent("N4", [{ constituent: N2, factor: 2 }]); diff --git a/packages/tide-predictor/src/constituents/NU2.ts b/packages/tide-predictor/src/constituents/NU2.ts new file mode 100644 index 0000000..23d9257 --- /dev/null +++ b/packages/tide-predictor/src/constituents/NU2.ts @@ -0,0 +1,10 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunar elliptic semi-diurnal (NU2). + * Secondary lunar elliptic semi-diurnal from Moon's orbital variations. + * Amplitude typically 2-5% of M2; smaller than N2. + * Important in detailed semi-diurnal constituent analysis. + */ +export default defineConstituent("NU2", [2, -1, 2, -1, 0, 0, 0], nc.uM2, nc.fM2); diff --git a/packages/tide-predictor/src/constituents/O1.ts b/packages/tide-predictor/src/constituents/O1.ts new file mode 100644 index 0000000..34c30b0 --- /dev/null +++ b/packages/tide-predictor/src/constituents/O1.ts @@ -0,0 +1,9 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunar diurnal (O1). + * Primary lunar diurnal constituent; one-per-lunar-day oscillation. + * Amplitude varies 10-20% due to lunar node effects. + */ +export default defineConstituent("O1", [1, -1, 0, 0, 0, 0, 1], nc.uO1, nc.fO1); diff --git a/packages/tide-predictor/src/constituents/OO1.ts b/packages/tide-predictor/src/constituents/OO1.ts new file mode 100644 index 0000000..e202678 --- /dev/null +++ b/packages/tide-predictor/src/constituents/OO1.ts @@ -0,0 +1,10 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunar diurnal (OO1). + * Second-order lunar diurnal constituent from Moon's orbital eccentricity. + * Typically very small amplitude; <2% of O1 in most locations. + * Important for detailed harmonic analysis in some regions. + */ +export default defineConstituent("OO1", [1, 3, 0, 0, 0, 0, -1], nc.uOO1, nc.fOO1); diff --git a/packages/tide-predictor/src/constituents/P1.ts b/packages/tide-predictor/src/constituents/P1.ts new file mode 100644 index 0000000..8ea7cd3 --- /dev/null +++ b/packages/tide-predictor/src/constituents/P1.ts @@ -0,0 +1,10 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Solar diurnal (P1). + * Principal solar diurnal constituent; one-per-solar-day oscillation. + * Amplitude typically 1/3 of K1; varies with latitude. + * Forms key component of diurnal tidal analysis alongside K1 and O1. + */ +export default defineConstituent("P1", [1, 1, -2, 0, 0, 0, 1], nc.uZero, nc.fUnity); diff --git a/packages/tide-predictor/src/constituents/Q1.ts b/packages/tide-predictor/src/constituents/Q1.ts new file mode 100644 index 0000000..47bce7e --- /dev/null +++ b/packages/tide-predictor/src/constituents/Q1.ts @@ -0,0 +1,9 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Elliptic lunar diurnal (Q1). + * One solar day and one lunar day modulation interaction. + * Amplitude typically 2-5% of O1; important in diurnal constituent analysis. + */ +export default defineConstituent("Q1", [1, -2, 0, 1, 0, 0, 1], nc.uO1, nc.fO1); diff --git a/packages/tide-predictor/src/constituents/R2.ts b/packages/tide-predictor/src/constituents/R2.ts new file mode 100644 index 0000000..a0115d7 --- /dev/null +++ b/packages/tide-predictor/src/constituents/R2.ts @@ -0,0 +1,10 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Solar elliptic semi-diurnal (R2). + * Smaller solar elliptic semi-diurnal constituent from solar declination effects. + * Amplitude typically <2% of S2; smallest of the solar semi-diurnal group. + * Rarely significant except in very detailed analyses. + */ +export default defineConstituent("R2", [2, 2, -1, 0, 0, -1, 2], nc.uZero, nc.fUnity); diff --git a/packages/tide-predictor/src/constituents/R3.ts b/packages/tide-predictor/src/constituents/R3.ts new file mode 100644 index 0000000..f52db88 --- /dev/null +++ b/packages/tide-predictor/src/constituents/R3.ts @@ -0,0 +1,12 @@ +import { defineCompoundConstituent } from "./definition.js"; +import R2 from "./R2.js"; + +/** + * Solar elliptic terdiurnal; 1.5 times R2 (smaller solar elliptic semi-diurnal). + * + * Note: Generated only in shallow water; very small amplitude (<0.05 cm typical). + * Usually negligible except in extreme shallow-water environments. + * + * @see NOAA CO-OPS + */ +export default defineCompoundConstituent("R3", [{ constituent: R2, factor: 1.5 }]); diff --git a/packages/tide-predictor/src/constituents/RHO.ts b/packages/tide-predictor/src/constituents/RHO.ts new file mode 100644 index 0000000..336041c --- /dev/null +++ b/packages/tide-predictor/src/constituents/RHO.ts @@ -0,0 +1,8 @@ +import RHO1 from "./RHO1.js"; + +/** + * Alias for compatibility between NOAA and IHO + * + * @see RHO1 + */ +export default RHO1; diff --git a/packages/tide-predictor/src/constituents/RHO1.ts b/packages/tide-predictor/src/constituents/RHO1.ts new file mode 100644 index 0000000..aff5ffc --- /dev/null +++ b/packages/tide-predictor/src/constituents/RHO1.ts @@ -0,0 +1,15 @@ +import { defineCompoundConstituent } from "./definition.js"; +import NU2 from "./NU2.js"; +import K1 from "./K1.js"; + +/** + * Shallow-water diurnal (RHO1, ρ1). + * Compound constituent: NU2-K1 (ν2-K1) + * Derived from interaction of semi-diurnal NU2 and diurnal K1 constituents. + * Amplitude typically very small; rarely significant. + * Found only in shallow-water regions with strong tidal distortion. + */ +export default defineCompoundConstituent("RHO1", [ + { constituent: NU2, factor: 1 }, + { constituent: K1, factor: -1 }, +]); diff --git a/packages/tide-predictor/src/constituents/S1.ts b/packages/tide-predictor/src/constituents/S1.ts new file mode 100644 index 0000000..7ab07fa --- /dev/null +++ b/packages/tide-predictor/src/constituents/S1.ts @@ -0,0 +1,10 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Solar diurnal (S1). + * Secondary solar diurnal constituent related to solar declination inequality. + * Usually very small amplitude; typically dominated by K1 and P1 in diurnal analysis. + * Rarely included in routine harmonic analyses. + */ +export default defineConstituent("S1", [1, 1, -1, 0, 0, 0, 0], nc.uZero, nc.fUnity); diff --git a/packages/tide-predictor/src/constituents/S2.ts b/packages/tide-predictor/src/constituents/S2.ts new file mode 100644 index 0000000..5e71d5f --- /dev/null +++ b/packages/tide-predictor/src/constituents/S2.ts @@ -0,0 +1,11 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Solar semi-diurnal (S2). + * Principal solar semi-diurnal constituent; largest solar tide component. + * Amplitude typically 20-50% of M2; varies with geographic location and latitude. + * Ratio M2/S2 determines tidal form factor (diurnal vs semi-diurnal regimes). + * Base constituent for many compound shallow-water constituents. + */ +export default defineConstituent("S2", [2, 2, -2, 0, 0, 0, 0], nc.uZero, nc.fUnity); diff --git a/packages/tide-predictor/src/constituents/S3.ts b/packages/tide-predictor/src/constituents/S3.ts new file mode 100644 index 0000000..be45c68 --- /dev/null +++ b/packages/tide-predictor/src/constituents/S3.ts @@ -0,0 +1,12 @@ +import { defineCompoundConstituent } from "./definition.js"; +import S2 from "./S2.js"; + +/** + * Solar terdiurnal overtide; shallow-water only. + * + * Note: Generated only in shallow water; not found in deep ocean. + * Typical amplitude <1 cm except in extreme shallow-water environments. + * + * @see NOAA CO-OPS + */ +export default defineCompoundConstituent("S3", [{ constituent: S2, factor: 1.5 }]); diff --git a/packages/tide-predictor/src/constituents/S4.ts b/packages/tide-predictor/src/constituents/S4.ts new file mode 100644 index 0000000..586e05b --- /dev/null +++ b/packages/tide-predictor/src/constituents/S4.ts @@ -0,0 +1,11 @@ +import { defineCompoundConstituent } from "./definition.js"; +import S2 from "./S2.js"; + +/** + * Shallow-water quarter-diurnal (S4). + * Compound constituent: 2×S2 + * First overtide of S2; generated in shallow-water environments. + * Amplitude typically 1-3% of S2; smallest of the common quarter-diurnal constituents. + * Purely solar, so has fixed amplitude and phase at any location. + */ +export default defineCompoundConstituent("S4", [{ constituent: S2, factor: 2 }]); diff --git a/packages/tide-predictor/src/constituents/S6.ts b/packages/tide-predictor/src/constituents/S6.ts new file mode 100644 index 0000000..e0df0d7 --- /dev/null +++ b/packages/tide-predictor/src/constituents/S6.ts @@ -0,0 +1,11 @@ +import { defineCompoundConstituent } from "./definition.js"; +import S2 from "./S2.js"; + +/** + * Shallow-water sixth-diurnal (S6). + * Compound constituent: 3×S2 + * Second overtide of S2; generated in shallow-water environments. + * Amplitude typically 0.5-1% of S2; smallest of the common overtides. + * Rarely significant except in extreme shallow water or resonant systems. + */ +export default defineCompoundConstituent("S6", [{ constituent: S2, factor: 3 }]); diff --git a/packages/tide-predictor/src/constituents/SA.ts b/packages/tide-predictor/src/constituents/SA.ts new file mode 100644 index 0000000..c1a47b9 --- /dev/null +++ b/packages/tide-predictor/src/constituents/SA.ts @@ -0,0 +1,8 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Solar annual (Sa). + * Long-term constituent driven by solar declination variations over the year. + */ +export default defineConstituent("Sa", [0, 0, 1, 0, 0, 0, 0], nc.uZero, nc.fUnity); diff --git a/packages/tide-predictor/src/constituents/SGM.ts b/packages/tide-predictor/src/constituents/SGM.ts new file mode 100644 index 0000000..a8f9a69 --- /dev/null +++ b/packages/tide-predictor/src/constituents/SGM.ts @@ -0,0 +1,10 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Lunar diurnal variational constituent (σ1, sigma1). + * Derived from Moon's declination variations. + * + * Note: Often has small amplitude; closely related to K1 and O1 variations. + */ +export default defineConstituent("SGM", [1, -3, 2, 0, 0, 0, -1], nc.uO1, nc.fO1); diff --git a/packages/tide-predictor/src/constituents/SSA.ts b/packages/tide-predictor/src/constituents/SSA.ts new file mode 100644 index 0000000..d02262d --- /dev/null +++ b/packages/tide-predictor/src/constituents/SSA.ts @@ -0,0 +1,8 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Solar semi-annual (Ssa). + * Semi-annual constituent from solar declination with twice-yearly periodicity. + */ +export default defineConstituent("Ssa", [0, 0, 2, 0, 0, 0, 0], nc.uZero, nc.fUnity); diff --git a/packages/tide-predictor/src/constituents/T2.ts b/packages/tide-predictor/src/constituents/T2.ts new file mode 100644 index 0000000..222a611 --- /dev/null +++ b/packages/tide-predictor/src/constituents/T2.ts @@ -0,0 +1,10 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Solar elliptic semi-diurnal (T2). + * Larger solar elliptic semi-diurnal constituent from solar declination effects. + * Amplitude typically <5% of S2; increases at higher latitudes. + * Forms part of solar semi-diurnal analysis alongside S2 and R2. + */ +export default defineConstituent("T2", [2, 2, -3, 0, 0, 1, 0], nc.uZero, nc.fUnity); diff --git a/packages/tide-predictor/src/constituents/T3.ts b/packages/tide-predictor/src/constituents/T3.ts new file mode 100644 index 0000000..0a81399 --- /dev/null +++ b/packages/tide-predictor/src/constituents/T3.ts @@ -0,0 +1,12 @@ +import { defineCompoundConstituent } from "./definition.js"; +import T2 from "./T2.js"; + +/** + * Solar elliptic terdiurnal; 1.5 times T2 (larger solar elliptic semi-diurnal). + * + * Note: Generated only in shallow water; minimal amplitude (<0.1 cm typical). + * Rarely significant except in extreme shallow-water or enclosed basins. + * + * @see NOAA CO-OPS + */ +export default defineCompoundConstituent("T3", [{ constituent: T2, factor: 1.5 }]); diff --git a/packages/tide-predictor/src/constituents/Z0.ts b/packages/tide-predictor/src/constituents/Z0.ts new file mode 100644 index 0000000..a097ba8 --- /dev/null +++ b/packages/tide-predictor/src/constituents/Z0.ts @@ -0,0 +1,10 @@ +import { defineConstituent } from "./definition.js"; +import nc from "../node-corrections/index.js"; + +/** + * Mean sea level (Z0). + * Not a tidal constituent in the strict sense, but represents the mean sea level offset + * or the "zero" reference level used in tidal predictions. + * No astronomical forcing; typically determined from harmonic analysis of observed data. + */ +export default defineConstituent("Z0", [0, 0, 0, 0, 0, 0, 0], nc.uZero, nc.fUnity); diff --git a/packages/tide-predictor/src/constituents/compound-constituent.ts b/packages/tide-predictor/src/constituents/compound-constituent.ts deleted file mode 100644 index 0771cfe..0000000 --- a/packages/tide-predictor/src/constituents/compound-constituent.ts +++ /dev/null @@ -1,72 +0,0 @@ -import type { Constituent } from "./constituent.js"; -import type { AstroData } from "../astronomy/index.js"; - -export interface ConstituentMember { - constituent: Constituent; - factor: number; -} - -export interface CompoundConstituent { - name: string; - coefficients: number[]; - speed: (astro: AstroData) => number; - value: (astro: AstroData) => number; - u: (astro: AstroData) => number; - f: (astro: AstroData) => number; -} - -const compoundConstituentFactory = ( - name: string, - members: ConstituentMember[], -): CompoundConstituent => { - const coefficients: number[] = []; - members.forEach(({ constituent, factor }) => { - constituent.coefficients.forEach((coefficient, index) => { - if (typeof coefficients[index] === "undefined") { - coefficients[index] = 0; - } - coefficients[index] += coefficient * factor; - }); - }); - - const compoundConstituent: CompoundConstituent = { - name, - coefficients, - - speed: (astro: AstroData): number => { - let speed = 0; - members.forEach(({ constituent, factor }) => { - speed += constituent.speed(astro) * factor; - }); - return speed; - }, - - value: (astro: AstroData): number => { - let value = 0; - members.forEach(({ constituent, factor }) => { - value += constituent.value(astro) * factor; - }); - return value; - }, - - u: (astro: AstroData): number => { - let u = 0; - members.forEach(({ constituent, factor }) => { - u += constituent.u(astro) * factor; - }); - return u; - }, - - f: (astro: AstroData): number => { - const f: number[] = []; - members.forEach(({ constituent, factor }) => { - f.push(Math.pow(constituent.f(astro), Math.abs(factor))); - }); - return f.reduce((previous, value) => previous * value); - }, - }; - - return Object.freeze(compoundConstituent); -}; - -export default compoundConstituentFactory; diff --git a/packages/tide-predictor/src/constituents/constituent.ts b/packages/tide-predictor/src/constituents/constituent.ts deleted file mode 100644 index a5d50e8..0000000 --- a/packages/tide-predictor/src/constituents/constituent.ts +++ /dev/null @@ -1,76 +0,0 @@ -import nodeCorrections from "../node-corrections/index.js"; -import type { AstroData } from "../astronomy/index.js"; -import type { NodeCorrectionFunction } from "../node-corrections/index.js"; - -/** - * Computes the dot notation of two arrays - */ -const dotArray = (a: number[], b: number[]): number => { - const results: number[] = []; - a.forEach((value, index) => { - results.push(value * b[index]); - }); - return results.reduce((total, value) => total + value); -}; - -const astronimicDoodsonNumber = (astro: AstroData): AstroData[keyof AstroData][] => { - return [astro["T+h-s"], astro.s, astro.h, astro.p, astro.N, astro.pp, astro["90"]]; -}; - -const astronomicSpeed = (astro: AstroData): number[] => { - const results: number[] = []; - astronimicDoodsonNumber(astro).forEach((number) => { - results.push(number.speed); - }); - return results; -}; - -const astronomicValues = (astro: AstroData): number[] => { - const results: number[] = []; - astronimicDoodsonNumber(astro).forEach((number) => { - results.push(number.value); - }); - return results; -}; - -export interface Constituent { - name: string; - coefficients: number[]; - value: (astro: AstroData) => number; - speed: (astro: AstroData) => number; - u: NodeCorrectionFunction; - f: NodeCorrectionFunction; -} - -const constituentFactory = ( - name: string, - coefficients: number[], - u?: NodeCorrectionFunction, - f?: NodeCorrectionFunction, -): Constituent => { - if (!coefficients) { - throw new Error("Coefficient must be defined for a constituent"); - } - - const constituent: Constituent = { - name, - coefficients, - - value: (astro: AstroData): number => { - return dotArray(coefficients, astronomicValues(astro)); - }, - - speed(astro: AstroData): number { - return dotArray(coefficients, astronomicSpeed(astro)); - }, - - u: typeof u !== "undefined" ? u : nodeCorrections.uZero, - - f: typeof f !== "undefined" ? f : nodeCorrections.fUnity, - }; - - return Object.freeze(constituent); -}; - -export default constituentFactory; -export { astronimicDoodsonNumber, astronomicSpeed, astronomicValues }; diff --git a/packages/tide-predictor/src/constituents/definition.ts b/packages/tide-predictor/src/constituents/definition.ts new file mode 100644 index 0000000..4f56397 --- /dev/null +++ b/packages/tide-predictor/src/constituents/definition.ts @@ -0,0 +1,127 @@ +import type { AstroData } from "../astronomy/index.js"; +import nodeCorrections, { type NodeCorrectionFunction } from "../node-corrections/index.js"; + +export interface Constituent { + name: string; + coefficients: number[]; + value: (astro: AstroData) => number; + speed: (astro: AstroData) => number; + u: NodeCorrectionFunction; + f: NodeCorrectionFunction; +} + +export function defineConstituent( + name: string, + coefficients: number[], + u?: NodeCorrectionFunction, + f?: NodeCorrectionFunction, +): Constituent { + if (!coefficients) { + throw new Error("Coefficient must be defined for a constituent"); + } + + return Object.freeze({ + name, + coefficients, + + value: (astro: AstroData): number => { + return dotArray(coefficients, astronomicValues(astro)); + }, + + speed(astro: AstroData): number { + return dotArray(coefficients, astronomicSpeed(astro)); + }, + + u: typeof u !== "undefined" ? u : nodeCorrections.uZero, + + f: typeof f !== "undefined" ? f : nodeCorrections.fUnity, + }); +} + +export interface ConstituentMember { + constituent: Constituent; + factor: number; +} + +export function defineCompoundConstituent(name: string, members: ConstituentMember[]): Constituent { + const coefficients: number[] = []; + members.forEach(({ constituent, factor }) => { + constituent.coefficients.forEach((coefficient, index) => { + if (typeof coefficients[index] === "undefined") { + coefficients[index] = 0; + } + coefficients[index] += coefficient * factor; + }); + }); + + return Object.freeze({ + name, + coefficients, + + speed: (astro: AstroData): number => { + let speed = 0; + members.forEach(({ constituent, factor }) => { + speed += constituent.speed(astro) * factor; + }); + return speed; + }, + + value: (astro: AstroData): number => { + let value = 0; + members.forEach(({ constituent, factor }) => { + value += constituent.value(astro) * factor; + }); + return value; + }, + + u: (astro: AstroData): number => { + let u = 0; + members.forEach(({ constituent, factor }) => { + u += constituent.u(astro) * factor; + }); + return u; + }, + + f: (astro: AstroData): number => { + const f: number[] = []; + members.forEach(({ constituent, factor }) => { + f.push(Math.pow(constituent.f(astro), Math.abs(factor))); + }); + return f.reduce((previous, value) => previous * value); + }, + }); +} + +/** + * Computes the dot notation of two arrays + */ +function dotArray(a: number[], b: number[]): number { + const results: number[] = []; + a.forEach((value, index) => { + results.push(value * b[index]); + }); + return results.reduce((total, value) => total + value); +} + +export function astronimicDoodsonNumber(astro: AstroData): AstroData[keyof AstroData][] { + return [astro["T+h-s"], astro.s, astro.h, astro.p, astro.N, astro.pp, astro["90"]]; +} + +export function astronomicSpeed(astro: AstroData): number[] { + const results: number[] = []; + astronimicDoodsonNumber(astro).forEach((number) => { + results.push(number.speed); + }); + return results; +} + +export function astronomicValues(astro: AstroData): number[] { + const results: number[] = []; + astronimicDoodsonNumber(astro).forEach((number) => { + results.push(number.value); + }); + return results; +} + +// Silence TS warning for empty module +export default {}; diff --git a/packages/tide-predictor/src/constituents/index.ts b/packages/tide-predictor/src/constituents/index.ts index bf3d625..7055c60 100644 --- a/packages/tide-predictor/src/constituents/index.ts +++ b/packages/tide-predictor/src/constituents/index.ts @@ -1,144 +1,25 @@ -import constituent from "./constituent.js"; -import compoundConstituent from "./compound-constituent.js"; -import nc from "../node-corrections/index.js"; -import type { Constituent } from "./constituent.js"; -import type { CompoundConstituent } from "./compound-constituent.js"; +import { type Constituent } from "./definition"; -export interface Constituents { - Z0: Constituent; - SA: Constituent; - SSA: Constituent; - MM: Constituent; - MF: Constituent; - Q1: Constituent; - O1: Constituent; - K1: Constituent; - J1: Constituent; - M1: Constituent; - P1: Constituent; - S1: Constituent; - OO1: Constituent; - "2N2": Constituent; - N2: Constituent; - NU2: Constituent; - M2: Constituent; - LAM2: Constituent; - L2: Constituent; - T2: Constituent; - S2: Constituent; - R2: Constituent; - K2: Constituent; - M3: Constituent; - MSF: CompoundConstituent; - "2Q1": CompoundConstituent; - RHO: CompoundConstituent; - MU2: CompoundConstituent; - "2SM2": CompoundConstituent; - "2MK3": CompoundConstituent; - MK3: CompoundConstituent; - MN4: CompoundConstituent; - M4: CompoundConstituent; - MS4: CompoundConstituent; - S4: CompoundConstituent; - M6: CompoundConstituent; - S6: CompoundConstituent; - M8: CompoundConstituent; - [key: string]: Constituent | CompoundConstituent; -} - -const constituents: Partial = {}; - -// Long Term -constituents.Z0 = constituent("Z0", [0, 0, 0, 0, 0, 0, 0], nc.uZero, nc.fUnity); -constituents.SA = constituent("Sa", [0, 0, 1, 0, 0, 0, 0], nc.uZero, nc.fUnity); -constituents.SSA = constituent("Ssa", [0, 0, 2, 0, 0, 0, 0], nc.uZero, nc.fUnity); -constituents.MM = constituent("MM", [0, 1, 0, -1, 0, 0, 0], nc.uZero, nc.fMm); -constituents.MF = constituent("MF", [0, 2, 0, 0, 0, 0, 0], nc.uMf, nc.fMf); -// Diurnals -constituents.Q1 = constituent("Q1", [1, -2, 0, 1, 0, 0, 1], nc.uO1, nc.fO1); -constituents.O1 = constituent("O1", [1, -1, 0, 0, 0, 0, 1], nc.uO1, nc.fO1); -constituents.K1 = constituent("K1", [1, 1, 0, 0, 0, 0, -1], nc.uK1, nc.fK1); -constituents.J1 = constituent("J1", [1, 2, 0, -1, 0, 0, -1], nc.uJ1, nc.fJ1); -constituents.M1 = constituent("M1", [1, 0, 0, 0, 0, 0, 1], nc.uM1, nc.fM1); -constituents.P1 = constituent("P1", [1, 1, -2, 0, 0, 0, 1], nc.uZero, nc.fUnity); -constituents.S1 = constituent("S1", [1, 1, -1, 0, 0, 0, 0], nc.uZero, nc.fUnity); -constituents.OO1 = constituent("OO1", [1, 3, 0, 0, 0, 0, -1], nc.uOO1, nc.fOO1); -// Semi diurnals -constituents["2N2"] = constituent("2N2", [2, -2, 0, 2, 0, 0, 0], nc.uM2, nc.fM2); -constituents.N2 = constituent("N2", [2, -1, 0, 1, 0, 0, 0], nc.uM2, nc.fM2); -constituents.NU2 = constituent("NU2", [2, -1, 2, -1, 0, 0, 0], nc.uM2, nc.fM2); -constituents.M2 = constituent("M2", [2, 0, 0, 0, 0, 0, 0], nc.uM2, nc.fM2); -constituents.LAM2 = constituent("LAM2", [2, 1, -2, 1, 0, 0, 2], nc.uM2, nc.fM2); -constituents.L2 = constituent("L2", [2, 1, 0, -1, 0, 0, 2], nc.uL2, nc.fL2); -constituents.T2 = constituent("T2", [2, 2, -3, 0, 0, 1, 0], nc.uZero, nc.fUnity); -constituents.S2 = constituent("S2", [2, 2, -2, 0, 0, 0, 0], nc.uZero, nc.fUnity); -constituents.R2 = constituent("R2", [2, 2, -1, 0, 0, -1, 2], nc.uZero, nc.fUnity); -constituents.K2 = constituent("K2", [2, 2, 0, 0, 0, 0, 0], nc.uK2, nc.fK2); -// Third diurnal -constituents.M3 = constituent( - "M3", - [3, 0, 0, 0, 0, 0, 0], - (a) => { - return nc.uModd(a, 3); - }, - (a) => { - return nc.fModd(a, 3); - }, -); -// Compound -constituents.MSF = compoundConstituent("MSF", [ - { constituent: constituents.S2!, factor: 1 }, - { constituent: constituents.M2!, factor: -1 }, -]); +// Dynamically import all constituent files +const constituentModules = import.meta.glob("./*.ts", { + eager: true, + import: "default", +}); -// Diurnal -constituents["2Q1"] = compoundConstituent("2Q1", [ - { constituent: constituents.N2!, factor: 1 }, - { constituent: constituents.J1!, factor: -1 }, -]); -constituents.RHO = compoundConstituent("RHO", [ - { constituent: constituents.NU2!, factor: 1 }, - { constituent: constituents.K1!, factor: -1 }, -]); +const constituents: Record = {}; -// Semi-Diurnal +// Extract constituent name from file path and populate the constituents object +for (const [path, module] of Object.entries(constituentModules)) { + // Skip the index file itself + if (path.includes("index.ts")) continue; -constituents.MU2 = compoundConstituent("MU2", [ - { constituent: constituents.M2!, factor: 2 }, - { constituent: constituents.S2!, factor: -1 }, -]); -constituents["2SM2"] = compoundConstituent("2SM2", [ - { constituent: constituents.S2!, factor: 2 }, - { constituent: constituents.M2!, factor: -1 }, -]); + // Extract filename without extension and .js suffix + const name = path.split("/").pop()?.replace(/\..*$/, "") ?? ""; -// Third-Diurnal -constituents["2MK3"] = compoundConstituent("2MK3", [ - { constituent: constituents.M2!, factor: 1 }, - { constituent: constituents.O1!, factor: 1 }, -]); -constituents.MK3 = compoundConstituent("MK3", [ - { constituent: constituents.M2!, factor: 1 }, - { constituent: constituents.K1!, factor: 1 }, -]); + // Skip module for definition.ts + if (name === "definition") continue; -// Quarter-Diurnal -constituents.MN4 = compoundConstituent("MN4", [ - { constituent: constituents.M2!, factor: 1 }, - { constituent: constituents.N2!, factor: 1 }, -]); -constituents.M4 = compoundConstituent("M4", [{ constituent: constituents.M2!, factor: 2 }]); -constituents.MS4 = compoundConstituent("MS4", [ - { constituent: constituents.M2!, factor: 1 }, - { constituent: constituents.S2!, factor: 1 }, -]); -constituents.S4 = compoundConstituent("S4", [{ constituent: constituents.S2!, factor: 2 }]); - -// Sixth-Diurnal -constituents.M6 = compoundConstituent("M6", [{ constituent: constituents.M2!, factor: 3 }]); -constituents.S6 = compoundConstituent("S6", [{ constituent: constituents.S2!, factor: 3 }]); - -// Eighth-Diurnals -constituents.M8 = compoundConstituent("M8", [{ constituent: constituents.M2!, factor: 4 }]); + constituents[name] = module; +} -export default constituents as Constituents; +export default constituents; diff --git a/packages/tide-predictor/src/harmonics/index.ts b/packages/tide-predictor/src/harmonics/index.ts index 50c9e65..6e75509 100644 --- a/packages/tide-predictor/src/harmonics/index.ts +++ b/packages/tide-predictor/src/harmonics/index.ts @@ -1,7 +1,7 @@ import prediction from "./prediction.js"; import constituentModels from "../constituents/index.js"; import { d2r } from "../astronomy/constants.js"; -import type { HarmonicConstituent, InternalHarmonicConstituent, Prediction } from "./prediction.js"; +import type { HarmonicConstituent, Prediction } from "./prediction.js"; export type * from "./prediction.js"; @@ -51,7 +51,7 @@ const harmonicsFactory = ({ harmonicConstituents, offset }: HarmonicsOptions): H if (!Array.isArray(harmonicConstituents)) { throw new Error("Harmonic constituents are not an array"); } - const constituents: InternalHarmonicConstituent[] = []; + const constituents: HarmonicConstituent[] = []; harmonicConstituents.forEach((constituent) => { if (typeof constituent.name === "undefined") { throw new Error("Harmonic constituents must have a name property"); @@ -59,7 +59,6 @@ const harmonicsFactory = ({ harmonicConstituents, offset }: HarmonicsOptions): H if (constituentModels[constituent.name] !== undefined) { constituents.push({ ...constituent, - _model: constituentModels[constituent.name], phase: d2r * constituent.phase, }); } @@ -68,7 +67,6 @@ const harmonicsFactory = ({ harmonicConstituents, offset }: HarmonicsOptions): H if (offset !== false) { constituents.push({ name: "Z0", - _model: constituentModels.Z0, phase: 0, amplitude: offset, }); diff --git a/packages/tide-predictor/src/harmonics/prediction.ts b/packages/tide-predictor/src/harmonics/prediction.ts index 7443a4e..e3bf407 100644 --- a/packages/tide-predictor/src/harmonics/prediction.ts +++ b/packages/tide-predictor/src/harmonics/prediction.ts @@ -1,7 +1,6 @@ import astro from "../astronomy/index.js"; import { d2r } from "../astronomy/constants.js"; -import type { Constituent } from "../constituents/constituent.js"; -import type { CompoundConstituent } from "../constituents/compound-constituent.js"; +import constituentModels from "../constituents/index.js"; export interface Timeline { items: Date[]; @@ -16,10 +15,6 @@ export interface HarmonicConstituent { description?: string; } -export interface InternalHarmonicConstituent extends HarmonicConstituent { - _model: Constituent | CompoundConstituent; -} - export interface TimelinePoint { time: Date; hour: number; @@ -106,7 +101,7 @@ const getExtremeLabel = (label: "high" | "low", highLowLabels?: ExtremeLabels): interface PredictionFactoryParams { timeline: Timeline; - constituents: InternalHarmonicConstituent[]; + constituents: HarmonicConstituent[]; start: Date; } @@ -220,8 +215,11 @@ const predictionFactory = ({ const u: Record[] = []; const f: Record[] = []; constituents.forEach((constituent) => { - const value = constituent._model.value(baseAstro); - const speed = constituent._model.speed(baseAstro); + const model = constituentModels[constituent.name]; + if (!model) return; + + const value = model.value(baseAstro); + const speed = model.speed(baseAstro); baseValue[constituent.name] = d2r * value; baseSpeed[constituent.name] = d2r * speed; }); @@ -230,11 +228,14 @@ const predictionFactory = ({ const fItem: Record = {}; const itemAstro = astro(time); constituents.forEach((constituent) => { - const constituentU = modulus(constituent._model.u(itemAstro), 360); + const model = constituentModels[constituent.name]; + if (!model) return; + const constituentU = modulus(model.u(itemAstro), 360); uItem[constituent.name] = d2r * constituentU; - fItem[constituent.name] = modulus(constituent._model.f(itemAstro), 360); + fItem[constituent.name] = modulus(model.f(itemAstro), 360); }); + u.push(uItem); f.push(fItem); }); diff --git a/packages/tide-predictor/src/node-corrections/index.ts b/packages/tide-predictor/src/node-corrections/index.ts index e887ea6..25bbbc9 100644 --- a/packages/tide-predictor/src/node-corrections/index.ts +++ b/packages/tide-predictor/src/node-corrections/index.ts @@ -3,33 +3,7 @@ import type { AstroData } from "../astronomy/index.js"; export type NodeCorrectionFunction = (a: AstroData, ...args: unknown[]) => number; -export interface NodeCorrections { - fUnity: () => number; - fMm: (a: AstroData) => number; - fMf: (a: AstroData) => number; - fO1: (a: AstroData) => number; - fJ1: (a: AstroData) => number; - fOO1: (a: AstroData) => number; - fM2: (a: AstroData) => number; - fK1: (a: AstroData) => number; - fL2: (a: AstroData) => number; - fK2: (a: AstroData) => number; - fM1: (a: AstroData) => number; - fModd: (a: AstroData, n: number) => number; - uZero: () => number; - uMf: (a: AstroData) => number; - uO1: (a: AstroData) => number; - uJ1: (a: AstroData) => number; - uOO1: (a: AstroData) => number; - uM2: (a: AstroData) => number; - uK1: (a: AstroData) => number; - uL2: (a: AstroData) => number; - uK2: (a: AstroData) => number; - uM1: (a: AstroData) => number; - uModd: (a: AstroData, n: number) => number; -} - -const corrections: NodeCorrections = { +const corrections = { fUnity(): number { return 1; }, diff --git a/packages/tide-predictor/test/constituents/compound-constituent.test.ts b/packages/tide-predictor/test/constituents/compound-constituent.test.ts index d553ceb..00fd4d4 100644 --- a/packages/tide-predictor/test/constituents/compound-constituent.test.ts +++ b/packages/tide-predictor/test/constituents/compound-constituent.test.ts @@ -1,20 +1,19 @@ import { describe, it, expect } from "vitest"; -import compoundConstituent from "../../src/constituents/compound-constituent.js"; -import Constituent from "../../src/constituents/constituent.js"; +import { defineCompoundConstituent, defineConstituent } from "../../src/constituents/definition.js"; import astro from "../../src/astronomy/index.js"; const sampleTime = new Date("2019-10-04T10:15:40.010Z"); const testAstro = astro(sampleTime); // This is a made-up doodson number for a test coefficient -const testConstituentA = Constituent("testa", [1, 1, -1, 0, 0, 0, 1]); -const testConstituentB = Constituent("testb", [0, 1, -1, 0, 0, 0, 1]); +const testConstituentA = defineConstituent("testa", [1, 1, -1, 0, 0, 0, 1]); +const testConstituentB = defineConstituent("testb", [0, 1, -1, 0, 0, 0, 1]); -const compoundTest = compoundConstituent("test compound", [ +const compoundTest = defineCompoundConstituent("test compound", [ { constituent: testConstituentA, factor: 1 }, { constituent: testConstituentB, factor: -1 }, ]); -describe("compund constituent", () => { +describe("compound constituent", () => { it("it calculates compound coefficients", () => { expect(compoundTest.coefficients).toEqual([1, 0, 0, 0, 0, 0, 0]); }); diff --git a/packages/tide-predictor/test/constituents/constituent.test.ts b/packages/tide-predictor/test/constituents/constituent.test.ts index 25f4c7f..4ac864a 100644 --- a/packages/tide-predictor/test/constituents/constituent.test.ts +++ b/packages/tide-predictor/test/constituents/constituent.test.ts @@ -1,22 +1,23 @@ import { describe, it, expect } from "vitest"; -import constituent, { +import { astronimicDoodsonNumber, astronomicSpeed, astronomicValues, -} from "../../src/constituents/constituent.js"; + defineConstituent, +} from "../../src/constituents/definition.js"; import astro from "../../src/astronomy/index.js"; const sampleTime = new Date("2019-10-04T10:15:40.010Z"); const testAstro = astro(sampleTime); // This is a made-up doodson number for a test coefficient -const testConstituent = constituent("test", [1, 1, -1, 0, 0, 0, 1]); +const testConstituent = defineConstituent("test", [1, 1, -1, 0, 0, 0, 1]); describe("constituent", () => { it("it throws error if missing coefficients", () => { expect(() => { // @ts-expect-error: Testing invalid input - constituent("fail"); + defineConstituent("fail"); }).toThrow("Coefficient must be defined for a constituent"); }); diff --git a/packages/tide-predictor/test/constituents/index.test.ts b/packages/tide-predictor/test/constituents/index.test.ts index 25819d1..47e4780 100644 --- a/packages/tide-predictor/test/constituents/index.test.ts +++ b/packages/tide-predictor/test/constituents/index.test.ts @@ -24,4 +24,125 @@ describe("Base constituent definitions", () => { expect(constituents.M3.u(testAstro)).toBeCloseTo(-3.11587643567, 4); expect(constituents.M3.f(testAstro)).toBeCloseTo(1.01283073119, 4); }); + + it("has correct properties for LAMBDA2 (alias of LAM2)", () => { + expect(constituents.LAMBDA2).toBeDefined(); + expect(constituents.LAMBDA2.speed(testAstro)).toBeCloseTo(29.455626, 2); + expect(constituents.LAMBDA2.u(testAstro)).toBeCloseTo(constituents.M2.u(testAstro), 2); + expect(constituents.LAMBDA2.f(testAstro)).toBeCloseTo(constituents.M2.f(testAstro), 3); + }); + + it("has correct properties for RHO1 (alias of RHO)", () => { + expect(constituents.RHO1).toBeDefined(); + const expectedSpeed = constituents.NU2.speed(testAstro) - constituents.K1.speed(testAstro); + expect(constituents.RHO1.speed(testAstro)).toBeCloseTo(expectedSpeed, 2); + }); + + it("has correct properties for EP2 (lunar elliptic semi-diurnal)", () => { + expect(constituents.EP2).toBeDefined(); + expect(constituents.EP2.speed(testAstro)).toBeCloseTo(27.4238338, 7); + expect(constituents.EP2.u(testAstro)).toBeCloseTo(constituents.M2.u(testAstro), 2); + expect(constituents.EP2.f(testAstro)).toBeCloseTo(constituents.M2.f(testAstro), 3); + }); + + it("has correct properties for MA2 (lunar variational semi-diurnal, mu2)", () => { + expect(constituents.MA2).toBeDefined(); + expect(constituents.MA2.speed(testAstro)).toBeCloseTo(28.943036, 6); + expect(constituents.MA2.u(testAstro)).toBeCloseTo(constituents.M2.u(testAstro), 2); + expect(constituents.MA2.f(testAstro)).toBeCloseTo(constituents.M2.f(testAstro), 3); + }); + + it("has correct properties for MB2 (lunar elliptic parameter variation)", () => { + expect(constituents.MB2).toBeDefined(); + expect(constituents.MB2.speed(testAstro)).toBeCloseTo(29.025173, 6); + expect(constituents.MB2.u(testAstro)).toBeCloseTo(constituents.M2.u(testAstro), 2); + expect(constituents.MB2.f(testAstro)).toBeCloseTo(constituents.M2.f(testAstro), 3); + }); + + it("has correct properties for SGM (lunar diurnal variational, sigma1)", () => { + expect(constituents.SGM).toBeDefined(); + expect(constituents.SGM.speed(testAstro)).toBeCloseTo(12.9271398); + expect(constituents.SGM.u(testAstro)).toBeCloseTo(constituents.O1.u(testAstro), 2); + expect(constituents.SGM.f(testAstro)).toBeCloseTo(constituents.O1.f(testAstro), 3); + }); + + // New shallow-water compound constituents + it("has correct properties for MSQM (lunar-solar compound)", () => { + expect(constituents.MSQM).toBeDefined(); + const expectedSpeed = + constituents.M2.speed(testAstro) + + constituents.S2.speed(testAstro) + + constituents.K1.speed(testAstro); + expect(constituents.MSQM.speed(testAstro)).toBeCloseTo(expectedSpeed, 2); + }); + + it("has correct properties for MTM (lunar-solar M2-T2 interaction)", () => { + expect(constituents.MTM).toBeDefined(); + const expectedSpeed = constituents.M2.speed(testAstro) + constituents.T2.speed(testAstro); + expect(constituents.MTM.speed(testAstro)).toBeCloseTo(expectedSpeed, 2); + }); + + it("has correct properties for MKS2 (three-way M2-K1-S2 interaction)", () => { + expect(constituents.MKS2).toBeDefined(); + const expectedSpeed = + constituents.M2.speed(testAstro) + + constituents.K1.speed(testAstro) - + constituents.S2.speed(testAstro); + expect(constituents.MKS2.speed(testAstro)).toBeCloseTo(expectedSpeed, 2); + }); + + it("has correct properties for N4 (N2 overtide)", () => { + expect(constituents.N4).toBeDefined(); + const expectedSpeed = 2 * constituents.N2.speed(testAstro); + expect(constituents.N4.speed(testAstro)).toBeCloseTo(expectedSpeed, 2); + }); + + it("has correct properties for S3 (solar terdiurnal)", () => { + expect(constituents.S3).toBeDefined(); + expect(constituents.S3.speed(testAstro)).toBeCloseTo(45.0, 2); + expect(constituents.S3.f(testAstro)).toBeCloseTo(1.0, 3); // Solar, no nodal modulation + expect(constituents.S3.u(testAstro)).toBeCloseTo(0.0, 3); + }); + + it("has correct properties for T3 (solar elliptic terdiurnal)", () => { + expect(constituents.T3).toBeDefined(); + expect(constituents.T3.speed(testAstro)).toBeCloseTo(44.9364, 2); + expect(constituents.T3.f(testAstro)).toBeCloseTo(1.0, 3); // Solar, no nodal modulation + expect(constituents.T3.u(testAstro)).toBeCloseTo(0.0, 3); + }); + + it("has correct properties for R3 (solar elliptic terdiurnal)", () => { + expect(constituents.R3).toBeDefined(); + expect(constituents.R3.speed(testAstro)).toBeCloseTo(45.062, 2); + expect(constituents.R3.f(testAstro)).toBeCloseTo(1.0, 3); // Solar, no nodal modulation + expect(constituents.R3.u(testAstro)).toBeCloseTo(0.0, 3); + }); + + it("has correct properties for 3L2 (triple L2)", () => { + expect(constituents["3L2"]).toBeDefined(); + const expectedSpeed = 3 * constituents.L2.speed(testAstro); + expect(constituents["3L2"].speed(testAstro)).toBeCloseTo(expectedSpeed, 2); + }); + + it("has correct properties for 3N2 (triple N2)", () => { + expect(constituents["3N2"]).toBeDefined(); + const expectedSpeed = 3 * constituents.N2.speed(testAstro); + expect(constituents["3N2"].speed(testAstro)).toBeCloseTo(expectedSpeed, 2); + }); + + it("has correct properties for 2MS6 (quarter-diurnal M2-S2 interaction)", () => { + expect(constituents["2MS6"]).toBeDefined(); + expect(constituents["2MS6"].speed(testAstro)).toBeCloseTo(87.9682085, 7); + }); + + it("has correct properties for 2MK5 (fifth-diurnal M2-K1 interaction)", () => { + expect(constituents["2MK5"]).toBeDefined(); + const expectedSpeed = 2 * constituents.M2.speed(testAstro) + constituents.K1.speed(testAstro); + expect(constituents["2MK5"].speed(testAstro)).toBeCloseTo(expectedSpeed, 2); + }); + + it("has correct properties for 2MO5 (fifth-diurnal M2-O1 interaction)", () => { + expect(constituents["2MO5"]).toBeDefined(); + expect(constituents["2MO5"].speed(testAstro)).toBeCloseTo(71.911244, 6); + }); }); diff --git a/tsconfig.json b/tsconfig.json index cac6e0e..d5566bd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,6 +11,7 @@ "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "allowImportingTsExtensions": false, - "noEmit": true + "noEmit": true, + "types": ["vite/client"] } }