pyTMD 2.2.4__tar.gz → 2.2.6__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.
Files changed (66) hide show
  1. {pytmd-2.2.4 → pytmd-2.2.6}/.gitignore +2 -0
  2. {pytmd-2.2.4 → pytmd-2.2.6}/CITATION.cff +2 -2
  3. {pytmd-2.2.4 → pytmd-2.2.6}/PKG-INFO +1 -1
  4. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/astro.py +3 -3
  5. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/compute.py +32 -16
  6. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/compute_tide_corrections.py +1 -1
  7. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/crs.py +1 -1
  8. pytmd-2.2.6/pyTMD/ellipse.py +196 -0
  9. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/io/GOT.py +8 -8
  10. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/io/OTIS.py +58 -15
  11. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/io/model.py +113 -40
  12. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/predict.py +20 -12
  13. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/solve/constants.py +38 -10
  14. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/utilities.py +2 -598
  15. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD.egg-info/PKG-INFO +1 -1
  16. {pytmd-2.2.4 → pytmd-2.2.6}/scripts/aviso_fes_tides.py +16 -4
  17. {pytmd-2.2.4 → pytmd-2.2.6}/scripts/gsfc_got_tides.py +6 -6
  18. pytmd-2.2.6/version.txt +1 -0
  19. pytmd-2.2.4/pyTMD/ellipse.py +0 -143
  20. pytmd-2.2.4/version.txt +0 -1
  21. {pytmd-2.2.4 → pytmd-2.2.6}/CODE_OF_CONDUCT.rst +0 -0
  22. {pytmd-2.2.4 → pytmd-2.2.6}/CONTRIBUTORS.rst +0 -0
  23. {pytmd-2.2.4 → pytmd-2.2.6}/LICENSE +0 -0
  24. {pytmd-2.2.4 → pytmd-2.2.6}/MANIFEST.in +0 -0
  25. {pytmd-2.2.4 → pytmd-2.2.6}/README.rst +0 -0
  26. {pytmd-2.2.4 → pytmd-2.2.6}/providers/AVISO.json +0 -0
  27. {pytmd-2.2.4 → pytmd-2.2.6}/providers/ESR.json +0 -0
  28. {pytmd-2.2.4 → pytmd-2.2.6}/providers/GSFC.json +0 -0
  29. {pytmd-2.2.4 → pytmd-2.2.6}/providers/README.rst +0 -0
  30. {pytmd-2.2.4 → pytmd-2.2.6}/providers/TPXO.json +0 -0
  31. {pytmd-2.2.4 → pytmd-2.2.6}/providers/_model_to_database.py +0 -0
  32. {pytmd-2.2.4 → pytmd-2.2.6}/providers/_providers_to_database.py +0 -0
  33. {pytmd-2.2.4 → pytmd-2.2.6}/providers/_update_providers.py +0 -0
  34. {pytmd-2.2.4 → pytmd-2.2.6}/providers/providers.json +0 -0
  35. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/__init__.py +0 -0
  36. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/arguments.py +2 -2
  37. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/data/ce1973_tab1.txt +0 -0
  38. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/data/ct1971_tab5.txt +0 -0
  39. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/data/d1921_tab.txt +0 -0
  40. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/data/database.json +0 -0
  41. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/data/doodson.json +0 -0
  42. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/data/opoleloadcoefcmcor.txt.gz +0 -0
  43. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/data/tab5.2e.txt +0 -0
  44. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/data/tab5.3a.txt +0 -0
  45. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/data/tab5.3b.txt +0 -0
  46. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/interpolate.py +0 -0
  47. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/io/ATLAS.py +0 -0
  48. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/io/FES.py +0 -0
  49. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/io/IERS.py +0 -0
  50. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/io/__init__.py +0 -0
  51. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/io/constituents.py +0 -0
  52. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/math.py +0 -0
  53. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/solve/__init__.py +0 -0
  54. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/spatial.py +0 -0
  55. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/tools.py +0 -0
  56. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD/version.py +0 -0
  57. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD.egg-info/SOURCES.txt +0 -0
  58. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD.egg-info/dependency_links.txt +0 -0
  59. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD.egg-info/requires.txt +0 -0
  60. {pytmd-2.2.4 → pytmd-2.2.6}/pyTMD.egg-info/top_level.txt +0 -0
  61. {pytmd-2.2.4 → pytmd-2.2.6}/pyproject.toml +0 -0
  62. {pytmd-2.2.4 → pytmd-2.2.6}/scripts/arcticdata_tides.py +0 -0
  63. {pytmd-2.2.4 → pytmd-2.2.6}/scripts/reduce_OTIS_files.py +0 -0
  64. {pytmd-2.2.4 → pytmd-2.2.6}/scripts/verify_box_tpxo.py +0 -0
  65. {pytmd-2.2.4 → pytmd-2.2.6}/setup.cfg +0 -0
  66. {pytmd-2.2.4 → pytmd-2.2.6}/setup.py +0 -0
@@ -42,6 +42,8 @@ wheels/
42
42
  *.egg
43
43
  .pytest_cache
44
44
  pythonenv*/
45
+ venv/
46
+ *build-commands.txt
45
47
  setup-miniconda-patched-environment.yml
46
48
  # OS generated files #
47
49
  ######################
@@ -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.4"
40
- date-released: "2025-05-07"
39
+ version: "2.2.6"
40
+ date-released: "2025-07-24"
41
41
  keywords:
42
42
  - Ocean Tides
43
43
  - Load Tides
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyTMD
3
- Version: 2.2.4
3
+ Version: 2.2.6
4
4
  Summary: Python-based tidal prediction software for estimating ocean, load, solid Earth and pole tides
5
5
  Author: Tyler Sutterley
6
6
  Author-email: tsutterl@uw.edu
@@ -49,7 +49,7 @@ UPDATE HISTORY:
49
49
  Updated 08/2020: change time variable names to not overwrite functions
50
50
  Updated 07/2020: added function docstrings
51
51
  Updated 07/2018: added option ASTRO5 to use coefficients from Richard Ray
52
- for use with the GSFC Global Ocean Tides (GOT) model
52
+ for use with the Goddard Ocean Tides (GOT) model
53
53
  added longitude of solar perigee (Ps) as an additional output
54
54
  Updated 09/2017: added option MEEUS to use additional coefficients
55
55
  from Meeus Astronomical Algorithms to calculate mean longitudes
@@ -503,7 +503,7 @@ def mean_obliquity(MJD: np.ndarray):
503
503
  return atr*polynomial_sum(epsilon0, T)
504
504
 
505
505
  def equation_of_time(MJD: np.ndarray):
506
- """Approximate calcuation of the difference between apparent and
506
+ """Approximate calculation of the difference between apparent and
507
507
  mean solar times :cite:p:`Meeus:1991vh,Urban:2013vl`
508
508
 
509
509
  Parameters
@@ -975,7 +975,7 @@ def _icrs_rotation_matrix(
975
975
  include_polar_motion: bool = True
976
976
  ):
977
977
  """
978
- Rotation matrix for tranforming from the
978
+ Rotation matrix for transforming from the
979
979
  International Celestial Reference System (ICRS)
980
980
  to the International Terrestrial Reference System (ITRS)
981
981
  :cite:p:`Capitaine:2003fx,Capitaine:2003fw,Petit:2010tp`
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python
2
2
  u"""
3
3
  compute.py
4
- Written by Tyler Sutterley (12/2024)
4
+ Written by Tyler Sutterley (07/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,8 @@ PROGRAM DEPENDENCIES:
62
62
  interpolate.py: interpolation routines for spatial data
63
63
 
64
64
  UPDATE HISTORY:
65
+ Updated 07/2025: mask mean pole values prior to valid epoch of convention
66
+ Updated 05/2025: added option to select constituents to read from model
65
67
  Updated 12/2024: moved check points function as compute.tide_masks
66
68
  Updated 11/2024: expose buffer distance for cropping tide model data
67
69
  Updated 10/2024: compute delta times based on corrections type
@@ -89,7 +91,7 @@ UPDATE HISTORY:
89
91
  Updated 06/2024: use np.clongdouble instead of np.longcomplex
90
92
  Updated 04/2024: use wrapper to importlib for optional dependencies
91
93
  Updated 02/2024: changed class name for ellipsoid parameters to datum
92
- Updated 01/2024: made the inferrence of minor constituents an option
94
+ Updated 01/2024: made the inference of minor constituents an option
93
95
  refactored lunisolar ephemerides functions
94
96
  renamed module to compute and added tidal currents function
95
97
  Updated 12/2023: use new crs class for coordinate reprojection
@@ -230,6 +232,7 @@ def tide_elevations(
230
232
  EXTRAPOLATE: bool = False,
231
233
  CUTOFF: int | float = 10.0,
232
234
  CORRECTIONS: str | None = None,
235
+ CONSTITUENTS: list | None = None,
233
236
  INFER_MINOR: bool = True,
234
237
  MINOR_CONSTITUENTS: list | None = None,
235
238
  APPEND_NODE: bool = False,
@@ -297,6 +300,8 @@ def tide_elevations(
297
300
  Set to ``np.inf`` to extrapolate for all points
298
301
  CORRECTIONS: str or None, default None
299
302
  Nodal correction type, default based on model
303
+ CONSTITUENTS: list or None, default None
304
+ Specify constituents to read from model
300
305
  INFER_MINOR: bool, default True
301
306
  Infer the height values for minor tidal constituents
302
307
  MINOR_CONSTITUENTS: list or None, default None
@@ -366,7 +371,8 @@ def tide_elevations(
366
371
  nt = len(ts)
367
372
 
368
373
  # read tidal constants and interpolate to grid points
369
- amp, ph, c = model.extract_constants(lon, lat, type=model.type,
374
+ amp, ph, c = model.extract_constants(lon, lat,
375
+ type=model.type, constituents=CONSTITUENTS,
370
376
  crop=CROP, bounds=BOUNDS, buffer=BUFFER, method=METHOD,
371
377
  extrapolate=EXTRAPOLATE, cutoff=CUTOFF,
372
378
  append_node=APPEND_NODE, apply_flexure=APPLY_FLEXURE)
@@ -395,7 +401,7 @@ def tide_elevations(
395
401
  for i in range(nt):
396
402
  TIDE = pyTMD.predict.map(ts.tide[i], hc, c,
397
403
  deltat=deltat[i], corrections=nodal_corrections)
398
- # calculate values for minor constituents by inferrence
404
+ # calculate values for minor constituents by inference
399
405
  if INFER_MINOR:
400
406
  MINOR = pyTMD.predict.infer_minor(ts.tide[i], hc, c,
401
407
  deltat=deltat[i], corrections=nodal_corrections,
@@ -410,7 +416,7 @@ def tide_elevations(
410
416
  tide.mask = np.any(hc.mask,axis=1)
411
417
  tide.data[:] = pyTMD.predict.drift(ts.tide, hc, c,
412
418
  deltat=deltat, corrections=nodal_corrections)
413
- # calculate values for minor constituents by inferrence
419
+ # calculate values for minor constituents by inference
414
420
  if INFER_MINOR:
415
421
  minor = pyTMD.predict.infer_minor(ts.tide, hc, c,
416
422
  deltat=deltat, corrections=nodal_corrections,
@@ -424,7 +430,7 @@ def tide_elevations(
424
430
  HC = hc[s,None,:]
425
431
  TIDE = pyTMD.predict.time_series(ts.tide, HC, c,
426
432
  deltat=deltat, corrections=nodal_corrections)
427
- # calculate values for minor constituents by inferrence
433
+ # calculate values for minor constituents by inference
428
434
  if INFER_MINOR:
429
435
  MINOR = pyTMD.predict.infer_minor(ts.tide, HC, c,
430
436
  deltat=deltat, corrections=nodal_corrections,
@@ -458,6 +464,7 @@ def tide_currents(
458
464
  EXTRAPOLATE: bool = False,
459
465
  CUTOFF: int | float = 10.0,
460
466
  CORRECTIONS: str | None = None,
467
+ CONSTITUENTS: list | None = None,
461
468
  INFER_MINOR: bool = True,
462
469
  MINOR_CONSTITUENTS: list | None = None,
463
470
  FILL_VALUE: float = np.nan,
@@ -523,6 +530,8 @@ def tide_currents(
523
530
  Set to ``np.inf`` to extrapolate for all points
524
531
  CORRECTIONS: str or None, default None
525
532
  Nodal correction type, default based on model
533
+ CONSTITUENTS: list or None, default None
534
+ Specify constituents to read from model
526
535
  INFER_MINOR: bool, default True
527
536
  Infer the height values for minor tidal constituents
528
537
  MINOR_CONSTITUENTS: list or None, default None
@@ -595,7 +604,8 @@ def tide_currents(
595
604
  # iterate over u and v currents
596
605
  for t in model.type:
597
606
  # read tidal constants and interpolate to grid points
598
- amp, ph, c = model.extract_constants(lon, lat, type=t,
607
+ amp, ph, c = model.extract_constants(lon, lat,
608
+ type=t, constituents=CONSTITUENTS,
599
609
  crop=CROP, bounds=BOUNDS, buffer=BUFFER, method=METHOD,
600
610
  extrapolate=EXTRAPOLATE, cutoff=CUTOFF)
601
611
  # calculate complex phase in radians for Euler's
@@ -623,7 +633,7 @@ def tide_currents(
623
633
  for i in range(nt):
624
634
  TIDE = pyTMD.predict.map(ts.tide[i], hc, c,
625
635
  deltat=deltat[i], corrections=nodal_corrections)
626
- # calculate values for minor constituents by inferrence
636
+ # calculate values for minor constituents by inference
627
637
  if INFER_MINOR:
628
638
  MINOR = pyTMD.predict.infer_minor(ts.tide[i], hc, c,
629
639
  deltat=deltat[i], corrections=nodal_corrections,
@@ -638,7 +648,7 @@ def tide_currents(
638
648
  tide[t].mask = np.any(hc.mask,axis=1)
639
649
  tide[t].data[:] = pyTMD.predict.drift(ts.tide, hc, c,
640
650
  deltat=deltat, corrections=nodal_corrections)
641
- # calculate values for minor constituents by inferrence
651
+ # calculate values for minor constituents by inference
642
652
  if INFER_MINOR:
643
653
  minor = pyTMD.predict.infer_minor(ts.tide, hc, c,
644
654
  deltat=deltat, corrections=nodal_corrections,
@@ -652,7 +662,7 @@ def tide_currents(
652
662
  HC = hc[s,None,:]
653
663
  TIDE = pyTMD.predict.time_series(ts.tide, HC, c,
654
664
  deltat=deltat, corrections=nodal_corrections)
655
- # calculate values for minor constituents by inferrence
665
+ # calculate values for minor constituents by inference
656
666
  if INFER_MINOR:
657
667
  MINOR = pyTMD.predict.infer_minor(ts.tide, HC, c,
658
668
  deltat=deltat, corrections=nodal_corrections,
@@ -1055,9 +1065,10 @@ def LPT_displacements(
1055
1065
  )
1056
1066
  # calculate components of load pole tides
1057
1067
  S = np.einsum('ti...,tji...->tj...', dxi, R)
1068
+ smask = np.reshape(np.any(dxi.mask, axis=1), (ny,nx))
1058
1069
  # reshape to output dimensions
1059
1070
  Srad.data[:,:,i] = np.reshape(S[:,2], (ny,nx))
1060
- Srad.mask[:,:,i] = np.isnan(Srad.data[:,:,i])
1071
+ Srad.mask[:,:,i] = np.isnan(Srad.data[:,:,i]) | smask
1061
1072
  elif (TYPE == 'drift'):
1062
1073
  # calculate load pole tides in cartesian coordinates
1063
1074
  XYZ = np.c_[X, Y, Z]
@@ -1071,10 +1082,11 @@ def LPT_displacements(
1071
1082
  )
1072
1083
  # calculate components of load pole tides
1073
1084
  S = np.einsum('ti...,tji...->tj...', dxi, R)
1085
+ smask = np.any(dxi.mask, axis=1)
1074
1086
  # reshape to output dimensions
1075
1087
  Srad = np.ma.zeros((nt), fill_value=FILL_VALUE)
1076
1088
  Srad.data[:] = S[:,2].copy()
1077
- Srad.mask = np.isnan(Srad.data)
1089
+ Srad.mask = np.isnan(Srad.data) | smask
1078
1090
  elif (TYPE == 'time series'):
1079
1091
  nstation = len(x)
1080
1092
  Srad = np.ma.zeros((nstation,nt), fill_value=FILL_VALUE)
@@ -1093,9 +1105,10 @@ def LPT_displacements(
1093
1105
  )
1094
1106
  # calculate components of load pole tides
1095
1107
  S = np.einsum('ti...,ji...->tj...', dxi, R[s,:,:])
1108
+ smask = np.any(dxi.mask, axis=1)
1096
1109
  # reshape to output dimensions
1097
1110
  Srad.data[s,:] = S[:,2].copy()
1098
- Srad.mask[s,:] = np.isnan(Srad.data[s,:])
1111
+ Srad.mask[s,:] = np.isnan(Srad.data[s,:]) | smask
1099
1112
 
1100
1113
  # replace invalid data with fill values
1101
1114
  Srad.data[Srad.mask] = Srad.fill_value
@@ -1279,9 +1292,10 @@ def OPT_displacements(
1279
1292
  )
1280
1293
  # calculate components of ocean pole tides
1281
1294
  U = np.einsum('ti...,tji...->tj...', dxi, Rinv)
1295
+ umask = np.reshape(np.any(dxi.mask, axis=1), (ny,nx))
1282
1296
  # reshape to output dimensions
1283
1297
  Urad.data[:,:,i] = np.reshape(U[:,2], (ny,nx))
1284
- Urad.mask[:,:,i] = np.isnan(Urad.data[:,:,i])
1298
+ Urad.mask[:,:,i] = np.isnan(Urad.data[:,:,i]) | umask
1285
1299
  elif (TYPE == 'drift'):
1286
1300
  # calculate ocean pole tides in cartesian coordinates
1287
1301
  XYZ = np.c_[X, Y, Z]
@@ -1297,10 +1311,11 @@ def OPT_displacements(
1297
1311
  )
1298
1312
  # calculate components of ocean pole tides
1299
1313
  U = np.einsum('ti...,tji...->tj...', dxi, Rinv)
1314
+ umask = np.any(dxi.mask, axis=1)
1300
1315
  # convert to masked array
1301
1316
  Urad = np.ma.zeros((nt), fill_value=FILL_VALUE)
1302
1317
  Urad.data[:] = U[:,2].copy()
1303
- Urad.mask = np.isnan(Urad.data)
1318
+ Urad.mask = np.isnan(Urad.data) | umask
1304
1319
  elif (TYPE == 'time series'):
1305
1320
  nstation = len(x)
1306
1321
  Urad = np.ma.zeros((nstation,nt), fill_value=FILL_VALUE)
@@ -1322,9 +1337,10 @@ def OPT_displacements(
1322
1337
  )
1323
1338
  # calculate components of ocean pole tides
1324
1339
  U = np.einsum('ti...,ji...->tj...', dxi, Rinv[s,:,:])
1340
+ umask = np.any(dxi.mask, axis=1)
1325
1341
  # reshape to output dimensions
1326
1342
  Urad.data[s,:] = U[:,2].copy()
1327
- Urad.mask[s,:] = np.isnan(Urad.data[s,:])
1343
+ Urad.mask[s,:] = np.isnan(Urad.data[s,:]) | umask
1328
1344
 
1329
1345
  # replace invalid data with fill values
1330
1346
  Urad.data[Urad.mask] = Urad.fill_value
@@ -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 inferrence of minor constituents an option
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
@@ -189,7 +189,7 @@ class crs:
189
189
  PROJECTION: int, str or dict
190
190
  Coordinate Reference System
191
191
  """
192
- # coordinate reference system dictoinary
192
+ # coordinate reference system dictionary
193
193
  try:
194
194
  CRS = pyproj.CRS.from_user_input(PROJECTION)
195
195
  except (ValueError, pyproj.exceptions.CRSError):
@@ -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)
@@ -3,10 +3,10 @@ u"""
3
3
  GOT.py
4
4
  Written by Tyler Sutterley (11/2024)
5
5
 
6
- Reads files for Richard Ray's Global Ocean Tide (GOT) models and makes initial
7
- calculations to run the tide program
8
- Includes functions to extract tidal harmonic constants out of a tidal model for
9
- given locations
6
+ Reads files for Richard Ray's Goddard Ocean Tide (GOT) models and makes
7
+ initial calculations to run the tide program
8
+ Includes functions to extract tidal harmonic constants out of a tidal
9
+ model for given locations
10
10
 
11
11
  INPUTS:
12
12
  ilon: longitude to interpolate
@@ -123,7 +123,7 @@ def extract_constants(
123
123
  **kwargs
124
124
  ):
125
125
  """
126
- Reads files for Richard Ray's Global Ocean Tide (GOT) models
126
+ Reads files for Richard Ray's Goddard Ocean Tide (GOT) models
127
127
 
128
128
  Makes initial calculations to run the tide program
129
129
 
@@ -315,7 +315,7 @@ def read_constants(
315
315
  **kwargs
316
316
  ):
317
317
  """
318
- Reads files for Richard Ray's Global Ocean Tide (GOT) models
318
+ Reads files for Richard Ray's Goddard Ocean Tide (GOT) models
319
319
 
320
320
  Parameters
321
321
  ----------
@@ -540,7 +540,7 @@ def read_ascii_file(
540
540
  **kwargs
541
541
  ):
542
542
  """
543
- Read Richard Ray's Global Ocean Tide (GOT) model file
543
+ Read Richard Ray's Goddard Ocean Tide (GOT) model file
544
544
 
545
545
  Parameters
546
546
  ----------
@@ -625,7 +625,7 @@ def read_netcdf_file(
625
625
  **kwargs
626
626
  ):
627
627
  """
628
- Read Richard Ray's Global Ocean Tide (GOT) netCDF4 model file
628
+ Read Richard Ray's Goddard Ocean Tide (GOT) netCDF4 model file
629
629
 
630
630
  Parameters
631
631
  ----------