pyTMD 2.2.4__tar.gz → 2.2.5__tar.gz
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.
- {pytmd-2.2.4 → pytmd-2.2.5}/.gitignore +2 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/CITATION.cff +2 -2
- {pytmd-2.2.4 → pytmd-2.2.5}/PKG-INFO +1 -1
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/compute.py +19 -10
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/compute_tide_corrections.py +1 -1
- pytmd-2.2.5/pyTMD/ellipse.py +196 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/io/OTIS.py +58 -15
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/io/model.py +112 -39
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/solve/constants.py +38 -10
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD.egg-info/PKG-INFO +1 -1
- pytmd-2.2.5/version.txt +1 -0
- pytmd-2.2.4/pyTMD/ellipse.py +0 -143
- pytmd-2.2.4/version.txt +0 -1
- {pytmd-2.2.4 → pytmd-2.2.5}/CODE_OF_CONDUCT.rst +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/CONTRIBUTORS.rst +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/LICENSE +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/MANIFEST.in +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/README.rst +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/providers/AVISO.json +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/providers/ESR.json +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/providers/GSFC.json +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/providers/README.rst +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/providers/TPXO.json +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/providers/_model_to_database.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/providers/_providers_to_database.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/providers/_update_providers.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/providers/providers.json +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/__init__.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/arguments.py +2 -2
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/astro.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/crs.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/data/ce1973_tab1.txt +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/data/ct1971_tab5.txt +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/data/d1921_tab.txt +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/data/database.json +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/data/doodson.json +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/data/opoleloadcoefcmcor.txt.gz +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/data/tab5.2e.txt +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/data/tab5.3a.txt +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/data/tab5.3b.txt +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/interpolate.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/io/ATLAS.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/io/FES.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/io/GOT.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/io/IERS.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/io/__init__.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/io/constituents.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/math.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/predict.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/solve/__init__.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/spatial.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/tools.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/utilities.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD/version.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD.egg-info/SOURCES.txt +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD.egg-info/dependency_links.txt +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD.egg-info/requires.txt +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyTMD.egg-info/top_level.txt +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/pyproject.toml +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/scripts/arcticdata_tides.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/scripts/aviso_fes_tides.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/scripts/gsfc_got_tides.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/scripts/reduce_OTIS_files.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/scripts/verify_box_tpxo.py +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/setup.cfg +0 -0
- {pytmd-2.2.4 → pytmd-2.2.5}/setup.py +0 -0
|
@@ -36,8 +36,8 @@ url: 'https://pytmd.readthedocs.io'
|
|
|
36
36
|
repository: 'https://pypi.org/project/pyTMD'
|
|
37
37
|
repository-artifact: 'https://anaconda.org/conda-forge/pytmd'
|
|
38
38
|
doi: "10.5281/zenodo.5555395"
|
|
39
|
-
version: "2.2.
|
|
40
|
-
date-released: "2025-
|
|
39
|
+
version: "2.2.5"
|
|
40
|
+
date-released: "2025-06-23"
|
|
41
41
|
keywords:
|
|
42
42
|
- Ocean Tides
|
|
43
43
|
- Load Tides
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
2
|
u"""
|
|
3
3
|
compute.py
|
|
4
|
-
Written by Tyler Sutterley (
|
|
4
|
+
Written by Tyler Sutterley (05/2025)
|
|
5
5
|
Calculates tidal elevations for correcting elevation or imagery data
|
|
6
6
|
Calculates tidal currents at locations and times
|
|
7
7
|
|
|
@@ -62,6 +62,7 @@ PROGRAM DEPENDENCIES:
|
|
|
62
62
|
interpolate.py: interpolation routines for spatial data
|
|
63
63
|
|
|
64
64
|
UPDATE HISTORY:
|
|
65
|
+
Updated 05/2025: added option to select constituents to read from model
|
|
65
66
|
Updated 12/2024: moved check points function as compute.tide_masks
|
|
66
67
|
Updated 11/2024: expose buffer distance for cropping tide model data
|
|
67
68
|
Updated 10/2024: compute delta times based on corrections type
|
|
@@ -89,7 +90,7 @@ UPDATE HISTORY:
|
|
|
89
90
|
Updated 06/2024: use np.clongdouble instead of np.longcomplex
|
|
90
91
|
Updated 04/2024: use wrapper to importlib for optional dependencies
|
|
91
92
|
Updated 02/2024: changed class name for ellipsoid parameters to datum
|
|
92
|
-
Updated 01/2024: made the
|
|
93
|
+
Updated 01/2024: made the inference of minor constituents an option
|
|
93
94
|
refactored lunisolar ephemerides functions
|
|
94
95
|
renamed module to compute and added tidal currents function
|
|
95
96
|
Updated 12/2023: use new crs class for coordinate reprojection
|
|
@@ -230,6 +231,7 @@ def tide_elevations(
|
|
|
230
231
|
EXTRAPOLATE: bool = False,
|
|
231
232
|
CUTOFF: int | float = 10.0,
|
|
232
233
|
CORRECTIONS: str | None = None,
|
|
234
|
+
CONSTITUENTS: list | None = None,
|
|
233
235
|
INFER_MINOR: bool = True,
|
|
234
236
|
MINOR_CONSTITUENTS: list | None = None,
|
|
235
237
|
APPEND_NODE: bool = False,
|
|
@@ -297,6 +299,8 @@ def tide_elevations(
|
|
|
297
299
|
Set to ``np.inf`` to extrapolate for all points
|
|
298
300
|
CORRECTIONS: str or None, default None
|
|
299
301
|
Nodal correction type, default based on model
|
|
302
|
+
CONSTITUENTS: list or None, default None
|
|
303
|
+
Specify constituents to read from model
|
|
300
304
|
INFER_MINOR: bool, default True
|
|
301
305
|
Infer the height values for minor tidal constituents
|
|
302
306
|
MINOR_CONSTITUENTS: list or None, default None
|
|
@@ -366,7 +370,8 @@ def tide_elevations(
|
|
|
366
370
|
nt = len(ts)
|
|
367
371
|
|
|
368
372
|
# read tidal constants and interpolate to grid points
|
|
369
|
-
amp, ph, c = model.extract_constants(lon, lat,
|
|
373
|
+
amp, ph, c = model.extract_constants(lon, lat,
|
|
374
|
+
type=model.type, constituents=CONSTITUENTS,
|
|
370
375
|
crop=CROP, bounds=BOUNDS, buffer=BUFFER, method=METHOD,
|
|
371
376
|
extrapolate=EXTRAPOLATE, cutoff=CUTOFF,
|
|
372
377
|
append_node=APPEND_NODE, apply_flexure=APPLY_FLEXURE)
|
|
@@ -395,7 +400,7 @@ def tide_elevations(
|
|
|
395
400
|
for i in range(nt):
|
|
396
401
|
TIDE = pyTMD.predict.map(ts.tide[i], hc, c,
|
|
397
402
|
deltat=deltat[i], corrections=nodal_corrections)
|
|
398
|
-
# calculate values for minor constituents by
|
|
403
|
+
# calculate values for minor constituents by inference
|
|
399
404
|
if INFER_MINOR:
|
|
400
405
|
MINOR = pyTMD.predict.infer_minor(ts.tide[i], hc, c,
|
|
401
406
|
deltat=deltat[i], corrections=nodal_corrections,
|
|
@@ -410,7 +415,7 @@ def tide_elevations(
|
|
|
410
415
|
tide.mask = np.any(hc.mask,axis=1)
|
|
411
416
|
tide.data[:] = pyTMD.predict.drift(ts.tide, hc, c,
|
|
412
417
|
deltat=deltat, corrections=nodal_corrections)
|
|
413
|
-
# calculate values for minor constituents by
|
|
418
|
+
# calculate values for minor constituents by inference
|
|
414
419
|
if INFER_MINOR:
|
|
415
420
|
minor = pyTMD.predict.infer_minor(ts.tide, hc, c,
|
|
416
421
|
deltat=deltat, corrections=nodal_corrections,
|
|
@@ -424,7 +429,7 @@ def tide_elevations(
|
|
|
424
429
|
HC = hc[s,None,:]
|
|
425
430
|
TIDE = pyTMD.predict.time_series(ts.tide, HC, c,
|
|
426
431
|
deltat=deltat, corrections=nodal_corrections)
|
|
427
|
-
# calculate values for minor constituents by
|
|
432
|
+
# calculate values for minor constituents by inference
|
|
428
433
|
if INFER_MINOR:
|
|
429
434
|
MINOR = pyTMD.predict.infer_minor(ts.tide, HC, c,
|
|
430
435
|
deltat=deltat, corrections=nodal_corrections,
|
|
@@ -458,6 +463,7 @@ def tide_currents(
|
|
|
458
463
|
EXTRAPOLATE: bool = False,
|
|
459
464
|
CUTOFF: int | float = 10.0,
|
|
460
465
|
CORRECTIONS: str | None = None,
|
|
466
|
+
CONSTITUENTS: list | None = None,
|
|
461
467
|
INFER_MINOR: bool = True,
|
|
462
468
|
MINOR_CONSTITUENTS: list | None = None,
|
|
463
469
|
FILL_VALUE: float = np.nan,
|
|
@@ -523,6 +529,8 @@ def tide_currents(
|
|
|
523
529
|
Set to ``np.inf`` to extrapolate for all points
|
|
524
530
|
CORRECTIONS: str or None, default None
|
|
525
531
|
Nodal correction type, default based on model
|
|
532
|
+
CONSTITUENTS: list or None, default None
|
|
533
|
+
Specify constituents to read from model
|
|
526
534
|
INFER_MINOR: bool, default True
|
|
527
535
|
Infer the height values for minor tidal constituents
|
|
528
536
|
MINOR_CONSTITUENTS: list or None, default None
|
|
@@ -595,7 +603,8 @@ def tide_currents(
|
|
|
595
603
|
# iterate over u and v currents
|
|
596
604
|
for t in model.type:
|
|
597
605
|
# read tidal constants and interpolate to grid points
|
|
598
|
-
amp, ph, c = model.extract_constants(lon, lat,
|
|
606
|
+
amp, ph, c = model.extract_constants(lon, lat,
|
|
607
|
+
type=t, constituents=CONSTITUENTS,
|
|
599
608
|
crop=CROP, bounds=BOUNDS, buffer=BUFFER, method=METHOD,
|
|
600
609
|
extrapolate=EXTRAPOLATE, cutoff=CUTOFF)
|
|
601
610
|
# calculate complex phase in radians for Euler's
|
|
@@ -623,7 +632,7 @@ def tide_currents(
|
|
|
623
632
|
for i in range(nt):
|
|
624
633
|
TIDE = pyTMD.predict.map(ts.tide[i], hc, c,
|
|
625
634
|
deltat=deltat[i], corrections=nodal_corrections)
|
|
626
|
-
# calculate values for minor constituents by
|
|
635
|
+
# calculate values for minor constituents by inference
|
|
627
636
|
if INFER_MINOR:
|
|
628
637
|
MINOR = pyTMD.predict.infer_minor(ts.tide[i], hc, c,
|
|
629
638
|
deltat=deltat[i], corrections=nodal_corrections,
|
|
@@ -638,7 +647,7 @@ def tide_currents(
|
|
|
638
647
|
tide[t].mask = np.any(hc.mask,axis=1)
|
|
639
648
|
tide[t].data[:] = pyTMD.predict.drift(ts.tide, hc, c,
|
|
640
649
|
deltat=deltat, corrections=nodal_corrections)
|
|
641
|
-
# calculate values for minor constituents by
|
|
650
|
+
# calculate values for minor constituents by inference
|
|
642
651
|
if INFER_MINOR:
|
|
643
652
|
minor = pyTMD.predict.infer_minor(ts.tide, hc, c,
|
|
644
653
|
deltat=deltat, corrections=nodal_corrections,
|
|
@@ -652,7 +661,7 @@ def tide_currents(
|
|
|
652
661
|
HC = hc[s,None,:]
|
|
653
662
|
TIDE = pyTMD.predict.time_series(ts.tide, HC, c,
|
|
654
663
|
deltat=deltat, corrections=nodal_corrections)
|
|
655
|
-
# calculate values for minor constituents by
|
|
664
|
+
# calculate values for minor constituents by inference
|
|
656
665
|
if INFER_MINOR:
|
|
657
666
|
MINOR = pyTMD.predict.infer_minor(ts.tide, HC, c,
|
|
658
667
|
deltat=deltat, corrections=nodal_corrections,
|
|
@@ -59,7 +59,7 @@ PROGRAM DEPENDENCIES:
|
|
|
59
59
|
interpolate.py: interpolation routines for spatial data
|
|
60
60
|
|
|
61
61
|
UPDATE HISTORY:
|
|
62
|
-
Updated 01/2024: made the
|
|
62
|
+
Updated 01/2024: made the inference of minor constituents an option
|
|
63
63
|
refactored lunisolar ephemerides functions
|
|
64
64
|
deprecated in favor of refactored compute.py module
|
|
65
65
|
Updated 12/2023: use new crs class for coordinate reprojection
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
u"""
|
|
3
|
+
ellipse.py
|
|
4
|
+
Written by Tyler Sutterley (05/2025)
|
|
5
|
+
Expresses the amplitudes and phases for the u and v components in terms of
|
|
6
|
+
four ellipse parameters using Foreman's formula
|
|
7
|
+
|
|
8
|
+
CALLING SEQUENCE:
|
|
9
|
+
umajor,uminor,uincl,uphase = pyTMD.ellipse.ellipse(u,v)
|
|
10
|
+
|
|
11
|
+
INPUTS:
|
|
12
|
+
u: zonal current (EW)
|
|
13
|
+
v: meridional current (NS)
|
|
14
|
+
|
|
15
|
+
OUTPUTS:
|
|
16
|
+
major: amplitude of the semimajor semi-axis
|
|
17
|
+
minor: amplitude of the semiminor semi-axis
|
|
18
|
+
incl: angle of inclination of the northern semimajor semi-axis
|
|
19
|
+
phase: phase lag of the maximum current behind the maximum tidal potential
|
|
20
|
+
of the individual constituent
|
|
21
|
+
|
|
22
|
+
REFERENCE:
|
|
23
|
+
M. G. G. Foreman and R. F. Henry, "The harmonic analysis of tidal model time
|
|
24
|
+
series", Advances in Water Resources, 12(3), 109-120, (1989).
|
|
25
|
+
https://doi.org/10.1016/0309-1708(89)90017-1
|
|
26
|
+
|
|
27
|
+
UPDATE HISTORY:
|
|
28
|
+
Updated 06/2025: added function to calculate x and y coordinates of ellipse
|
|
29
|
+
Updated 01/2024: added inverse function to get currents from parameters
|
|
30
|
+
use complex algebra to calculate tidal ellipse parameters
|
|
31
|
+
Updated 09/2023: renamed to ellipse.py (from tidal_ellipse.py)
|
|
32
|
+
Updated 03/2023: add basic variable typing to function inputs
|
|
33
|
+
Updated 04/2022: updated docstrings to numpy documentation format
|
|
34
|
+
Written 07/2020
|
|
35
|
+
"""
|
|
36
|
+
from __future__ import annotations
|
|
37
|
+
|
|
38
|
+
import numpy as np
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
"ellipse",
|
|
42
|
+
"inverse",
|
|
43
|
+
"_xy"
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
def ellipse(u: np.ndarray, v: np.ndarray):
|
|
47
|
+
"""
|
|
48
|
+
Expresses the amplitudes and phases for the u and v components in terms of
|
|
49
|
+
four ellipse parameters using Foreman's formula :cite:p:`Foreman:1989dt`
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
u: np.ndarray
|
|
54
|
+
zonal current (EW)
|
|
55
|
+
v: np.ndarray
|
|
56
|
+
meridional current (NS)
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
major: np.ndarray
|
|
61
|
+
amplitude of the semi-major axis
|
|
62
|
+
minor: np.ndarray
|
|
63
|
+
amplitude of the semi-minor axis
|
|
64
|
+
incl: np.ndarray
|
|
65
|
+
angle of inclination of the northern semi-major axis
|
|
66
|
+
phase: np.ndarray
|
|
67
|
+
phase lag of the maximum current behind the maximum tidal potential
|
|
68
|
+
"""
|
|
69
|
+
# validate inputs
|
|
70
|
+
u = np.atleast_1d(u)
|
|
71
|
+
v = np.atleast_1d(v)
|
|
72
|
+
# wp, wm: complex radius of positively and negatively rotating vectors
|
|
73
|
+
wp = (u + 1j*v)/2.0
|
|
74
|
+
wm = np.conj(u - 1j*v)/2.0
|
|
75
|
+
# ap, am: amplitudes of positively and negatively rotating vectors
|
|
76
|
+
ap = np.abs(wp)
|
|
77
|
+
am = np.abs(wm)
|
|
78
|
+
# ep, em: phases of positively and negatively rotating vectors
|
|
79
|
+
ep = np.angle(wp, deg=True)
|
|
80
|
+
em = np.angle(wm, deg=True)
|
|
81
|
+
# determine the amplitudes of the semimajor and semiminor axes
|
|
82
|
+
# using Foreman's formula
|
|
83
|
+
major = (ap + am)
|
|
84
|
+
minor = (ap - am)
|
|
85
|
+
# determine the inclination and phase using Foreman's formula
|
|
86
|
+
incl = (em + ep)/2.0
|
|
87
|
+
phase = (em - ep)/2.0
|
|
88
|
+
# adjust orientation of ellipse
|
|
89
|
+
k = (incl//180.0)
|
|
90
|
+
incl -= 180.0*k
|
|
91
|
+
phase += 180.0*k
|
|
92
|
+
phase = np.mod(phase, 360.0)
|
|
93
|
+
# return values
|
|
94
|
+
return (major, minor, incl, phase)
|
|
95
|
+
|
|
96
|
+
def inverse(
|
|
97
|
+
major: np.ndarray,
|
|
98
|
+
minor: np.ndarray,
|
|
99
|
+
incl: np.ndarray,
|
|
100
|
+
phase: np.ndarray
|
|
101
|
+
):
|
|
102
|
+
"""
|
|
103
|
+
Calculates currents u, v using the four tidal ellipse
|
|
104
|
+
parameters from Foreman's formula :cite:p:`Foreman:1989dt`
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
major: np.ndarray
|
|
109
|
+
amplitude of the semi-major axis
|
|
110
|
+
minor: np.ndarray
|
|
111
|
+
amplitude of the semi-minor axis
|
|
112
|
+
incl: np.ndarray
|
|
113
|
+
angle of inclination of the northern semi-major axis
|
|
114
|
+
phase: np.ndarray
|
|
115
|
+
phase lag of the maximum current behind the maximum tidal potential
|
|
116
|
+
|
|
117
|
+
Returns
|
|
118
|
+
-------
|
|
119
|
+
u: np.ndarray
|
|
120
|
+
zonal current (EW)
|
|
121
|
+
v: np.ndarray
|
|
122
|
+
meridional current (NS)
|
|
123
|
+
"""
|
|
124
|
+
# validate inputs
|
|
125
|
+
major = np.atleast_1d(major)
|
|
126
|
+
minor = np.atleast_1d(minor)
|
|
127
|
+
# convert inclination and phase to radians
|
|
128
|
+
incl = np.atleast_1d(incl)*np.pi/180.0
|
|
129
|
+
phase = np.atleast_1d(phase)*np.pi/180.0
|
|
130
|
+
# ep, em: phases of positively and negatively rotating vectors
|
|
131
|
+
ep = (incl - phase)
|
|
132
|
+
em = (incl + phase)
|
|
133
|
+
# ap, am: amplitudes of positively and negatively rotating vectors
|
|
134
|
+
ap = (major + minor)/2.0
|
|
135
|
+
am = (major - minor)/2.0
|
|
136
|
+
# wp, wm: complex radius of positively and negatively rotating vectors
|
|
137
|
+
wp = ap * np.exp(1j*ep)
|
|
138
|
+
wm = am * np.exp(1j*em)
|
|
139
|
+
# calculate complex currents
|
|
140
|
+
u = wp + np.conj(wm)
|
|
141
|
+
v = -1j*(wp - np.conj(wm))
|
|
142
|
+
# return values
|
|
143
|
+
return (u, v)
|
|
144
|
+
|
|
145
|
+
def _xy(
|
|
146
|
+
major: float | np.ndarray,
|
|
147
|
+
minor: float | np.ndarray,
|
|
148
|
+
incl: float | np.ndarray,
|
|
149
|
+
**kwargs
|
|
150
|
+
):
|
|
151
|
+
"""
|
|
152
|
+
Calculates the x and y coordinates of the tidal ellipse
|
|
153
|
+
|
|
154
|
+
Parameters
|
|
155
|
+
----------
|
|
156
|
+
major: np.ndarray
|
|
157
|
+
amplitude of the semi-major axis
|
|
158
|
+
minor: np.ndarray
|
|
159
|
+
amplitude of the semi-minor axis
|
|
160
|
+
incl: np.ndarray
|
|
161
|
+
angle of inclination of the northern semi-major axis
|
|
162
|
+
phase: np.ndarray or None, default None
|
|
163
|
+
phase lag of the maximum current behind the maximum tidal potential
|
|
164
|
+
xy: tuple, default (0.0, 0.0)
|
|
165
|
+
center of the ellipse (x, y)
|
|
166
|
+
N: int or None, default None
|
|
167
|
+
number of points to calculate along the ellipse
|
|
168
|
+
|
|
169
|
+
Returns
|
|
170
|
+
-------
|
|
171
|
+
x: np.ndarray
|
|
172
|
+
x coordinates of the tidal ellipse
|
|
173
|
+
y: np.ndarray
|
|
174
|
+
y coordinates of the tidal ellipse
|
|
175
|
+
"""
|
|
176
|
+
# set default number of points
|
|
177
|
+
kwargs.setdefault('phase', None)
|
|
178
|
+
kwargs.setdefault('xy', (0.0, 0.0))
|
|
179
|
+
kwargs.setdefault('N', 1000)
|
|
180
|
+
# validate inputs
|
|
181
|
+
phi = incl*np.pi/180.0
|
|
182
|
+
# calculate the angle of the ellipse
|
|
183
|
+
if kwargs['phase'] is not None:
|
|
184
|
+
# use the phase lag and inclination
|
|
185
|
+
th = (kwargs['phase'] + incl)*np.pi/180.0
|
|
186
|
+
else:
|
|
187
|
+
# use a full rotation
|
|
188
|
+
th = np.linspace(0, 2*np.pi, kwargs['N'])
|
|
189
|
+
# calculate x and y coordinates
|
|
190
|
+
x = kwargs['xy'][0] + \
|
|
191
|
+
major*np.cos(th)*np.cos(phi) - \
|
|
192
|
+
minor*np.sin(th)*np.sin(phi)
|
|
193
|
+
y = kwargs['xy'][1] + \
|
|
194
|
+
major*np.cos(th)*np.sin(phi) + \
|
|
195
|
+
minor*np.sin(th)*np.cos(phi)
|
|
196
|
+
return (x, y)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
2
|
u"""
|
|
3
3
|
OTIS.py
|
|
4
|
-
Written by Tyler Sutterley (
|
|
4
|
+
Written by Tyler Sutterley (05/2025)
|
|
5
5
|
|
|
6
6
|
Reads files for a tidal model and makes initial calculations to run tide program
|
|
7
7
|
Includes functions to extract tidal harmonic constants from OTIS tide models for
|
|
@@ -36,6 +36,8 @@ OPTIONS:
|
|
|
36
36
|
ATLAS: reading a global solution with localized solutions
|
|
37
37
|
TMD3: combined global or local netCDF4 solution
|
|
38
38
|
OTIS: combined global or local solution
|
|
39
|
+
constituents: list or None, default None
|
|
40
|
+
Specify constituents to read from model
|
|
39
41
|
apply_flexure: apply ice flexure scaling factor to constituents
|
|
40
42
|
|
|
41
43
|
OUTPUTS:
|
|
@@ -58,6 +60,7 @@ PROGRAM DEPENDENCIES:
|
|
|
58
60
|
interpolate.py: interpolation routines for spatial data
|
|
59
61
|
|
|
60
62
|
UPDATE HISTORY:
|
|
63
|
+
Updated 05/2025: added option to select constituents to read from model
|
|
61
64
|
Updated 12/2024: released version of TMD3 has different variable names
|
|
62
65
|
Updated 11/2024: expose buffer distance for cropping tide model data
|
|
63
66
|
Updated 10/2024: save latitude and longitude to output constituent object
|
|
@@ -210,6 +213,8 @@ def extract_constants(
|
|
|
210
213
|
- ``'ATLAS'``: reading a global solution with localized solutions
|
|
211
214
|
- ``'OTIS'``: combined global or local solution
|
|
212
215
|
- ``'TMD3'``: combined global or local netCDF4 solution
|
|
216
|
+
constituents: list or None, default None
|
|
217
|
+
Specify constituents to read from model
|
|
213
218
|
crop: bool, default False
|
|
214
219
|
Crop tide model data to (buffered) bounds
|
|
215
220
|
bounds: list or NoneType, default None
|
|
@@ -245,6 +250,7 @@ def extract_constants(
|
|
|
245
250
|
# set default keyword arguments
|
|
246
251
|
kwargs.setdefault('type', 'z')
|
|
247
252
|
kwargs.setdefault('grid', 'OTIS')
|
|
253
|
+
kwargs.setdefault('constituents', None)
|
|
248
254
|
kwargs.setdefault('crop', False)
|
|
249
255
|
kwargs.setdefault('bounds', None)
|
|
250
256
|
kwargs.setdefault('buffer', None)
|
|
@@ -394,12 +400,19 @@ def extract_constants(
|
|
|
394
400
|
elif kwargs['type'] in ('z','V','U'):
|
|
395
401
|
unit_conv = 1.0
|
|
396
402
|
|
|
397
|
-
# read
|
|
403
|
+
# read list of constituents
|
|
398
404
|
if isinstance(model_file,list):
|
|
399
|
-
|
|
400
|
-
nc = len(
|
|
405
|
+
cons = [read_constituents(m)[0].pop() for m in model_file]
|
|
406
|
+
nc = len(cons)
|
|
401
407
|
else:
|
|
402
|
-
|
|
408
|
+
cons,nc = read_constituents(model_file, grid=kwargs['grid'])
|
|
409
|
+
# reduce number of constituents
|
|
410
|
+
if kwargs['constituents'] is not None:
|
|
411
|
+
# verify that constituents is a list
|
|
412
|
+
if isinstance(kwargs['constituents'], str):
|
|
413
|
+
kwargs['constituents'] = [kwargs['constituents']]
|
|
414
|
+
# number of constituents to read
|
|
415
|
+
nc = len(kwargs['constituents'])
|
|
403
416
|
|
|
404
417
|
# number of output data points
|
|
405
418
|
npts = len(D)
|
|
@@ -407,8 +420,17 @@ def extract_constants(
|
|
|
407
420
|
amplitude.mask = np.zeros((npts,nc), dtype=bool)
|
|
408
421
|
ph = np.ma.zeros((npts,nc))
|
|
409
422
|
ph.mask = np.zeros((npts,nc), dtype=bool)
|
|
423
|
+
constituents = []
|
|
410
424
|
# read and interpolate each constituent
|
|
411
|
-
for
|
|
425
|
+
for j in range(nc):
|
|
426
|
+
# if reading a specific constituent
|
|
427
|
+
if (kwargs['constituents'] is not None):
|
|
428
|
+
c = kwargs['constituents'][j]
|
|
429
|
+
else:
|
|
430
|
+
c = cons[j]
|
|
431
|
+
# find index of constituent in list
|
|
432
|
+
i = cons.index(c)
|
|
433
|
+
# read constituent for type
|
|
412
434
|
if (kwargs['type'] == 'z'):
|
|
413
435
|
# read z constituent from elevation file
|
|
414
436
|
if (kwargs['grid'] == 'ATLAS'):
|
|
@@ -503,13 +525,15 @@ def extract_constants(
|
|
|
503
525
|
is_geographic=is_geographic)
|
|
504
526
|
# convert units
|
|
505
527
|
# amplitude and phase of the constituent
|
|
506
|
-
amplitude.data[:,
|
|
507
|
-
amplitude.mask[:,
|
|
508
|
-
ph.data[:,
|
|
509
|
-
ph.mask[:,
|
|
528
|
+
amplitude.data[:,j] = np.abs(hci.data)/unit_conv
|
|
529
|
+
amplitude.mask[:,j] = np.copy(hci.mask)
|
|
530
|
+
ph.data[:,j] = np.arctan2(-np.imag(hci), np.real(hci))
|
|
531
|
+
ph.mask[:,j] = np.copy(hci.mask)
|
|
510
532
|
# update mask to invalidate points outside model domain
|
|
511
|
-
ph.mask[:,
|
|
512
|
-
amplitude.mask[:,
|
|
533
|
+
ph.mask[:,j] |= invalid
|
|
534
|
+
amplitude.mask[:,j] |= invalid
|
|
535
|
+
# append constituent to list
|
|
536
|
+
constituents.append(c)
|
|
513
537
|
|
|
514
538
|
# convert phase to degrees
|
|
515
539
|
phase = ph*180.0/np.pi
|
|
@@ -552,6 +576,8 @@ def read_constants(
|
|
|
552
576
|
- ``'ATLAS'``: reading a global solution with localized solutions
|
|
553
577
|
- ``'OTIS'``: combined global or local solution
|
|
554
578
|
- ``'TMD3'``: combined global or local netCDF4 solution
|
|
579
|
+
constituents: list or None, default None
|
|
580
|
+
Specify constituents to read from model
|
|
555
581
|
crop: bool, default False
|
|
556
582
|
Crop tide model data to (buffered) bounds
|
|
557
583
|
bounds: list or NoneType, default None
|
|
@@ -569,6 +595,7 @@ def read_constants(
|
|
|
569
595
|
# set default keyword arguments
|
|
570
596
|
kwargs.setdefault('type', 'z')
|
|
571
597
|
kwargs.setdefault('grid', 'OTIS')
|
|
598
|
+
kwargs.setdefault('constituents', None)
|
|
572
599
|
kwargs.setdefault('crop', False)
|
|
573
600
|
kwargs.setdefault('bounds', None)
|
|
574
601
|
kwargs.setdefault('buffer', 0)
|
|
@@ -664,18 +691,34 @@ def read_constants(
|
|
|
664
691
|
gridx, gridy = np.meshgrid(xi, yi)
|
|
665
692
|
lon, lat = crs.transform(gridx, gridy, direction='INVERSE')
|
|
666
693
|
|
|
667
|
-
# read
|
|
694
|
+
# read list of constituents
|
|
668
695
|
if isinstance(model_file, list):
|
|
669
696
|
cons = [read_constituents(m)[0].pop() for m in model_file]
|
|
697
|
+
nc = len(cons)
|
|
670
698
|
else:
|
|
671
|
-
cons,
|
|
699
|
+
cons,nc = read_constituents(model_file, grid=kwargs['grid'])
|
|
700
|
+
# reduce number of constituents
|
|
701
|
+
if kwargs['constituents'] is not None:
|
|
702
|
+
# verify that constituents is a list
|
|
703
|
+
if isinstance(kwargs['constituents'], str):
|
|
704
|
+
kwargs['constituents'] = [kwargs['constituents']]
|
|
705
|
+
# number of constituents to read
|
|
706
|
+
nc = len(kwargs['constituents'])
|
|
707
|
+
|
|
672
708
|
# save output constituents and coordinate reference system
|
|
673
709
|
constituents = pyTMD.io.constituents(x=xi, y=yi,
|
|
674
710
|
bathymetry=bathymetry.data, mask=mask, crs=crs,
|
|
675
711
|
longitude=lon, latitude=lat)
|
|
676
712
|
|
|
677
713
|
# read each model constituent
|
|
678
|
-
for
|
|
714
|
+
for j in range(nc):
|
|
715
|
+
# if reading a specific constituent
|
|
716
|
+
if (kwargs['constituents'] is not None):
|
|
717
|
+
c = kwargs['constituents'][j]
|
|
718
|
+
else:
|
|
719
|
+
c = cons[j]
|
|
720
|
+
# find index of constituent in list
|
|
721
|
+
i = cons.index(c)
|
|
679
722
|
if (kwargs['type'] == 'z'):
|
|
680
723
|
# read constituent from elevation file
|
|
681
724
|
if (kwargs['grid'] == 'ATLAS'):
|