Skip to content

Commit

Permalink
feat: add functions to convert to and from doodson numbers
Browse files Browse the repository at this point in the history
feat: add option to output Cartwright numbers
fix: test solve is in class
  • Loading branch information
tsutterley committed Jan 12, 2024
1 parent 5c7fec4 commit f33587e
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 24 deletions.
4 changes: 4 additions & 0 deletions doc/source/api_reference/arguments.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ Calling Sequence
.. autofunction:: pyTMD.arguments._arguments_table

.. autofunction:: pyTMD.arguments._minor_table

.. autofunction:: pyTMD.arguments._to_doodson_number

.. autofunction:: pyTMD.arguments._from_doodson_number
105 changes: 83 additions & 22 deletions pyTMD/arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -672,23 +672,29 @@ def minor_arguments(
return (u, f, arg)

def doodson_number(
constituents: list | np.ndarray,
constituents: str | list | np.ndarray,
**kwargs
):
"""
Calculates the Doodson number for tidal constituents [1]_
Calculates the Doodson or Cartwright number for
tidal constituents [1]_
Parameters
----------
constituents: list
tidal constituent IDs
constituents: str, list or np.ndarray
tidal constituent ID(s)
corrections: str, default 'OTIS'
use arguments from OTIS/ATLAS or GOT models
formalism: str, default 'Doodson'
constituent identifier formalism
- ``'Doodson'``
- ``'Cartwright'``
Returns
-------
DO: dict
Doodson number for each constituent
numbers: float or dict
Doodson or Cartwright number for each constituent
References
----------
Expand All @@ -700,6 +706,7 @@ def doodson_number(
"""
# set default keyword arguments
kwargs.setdefault('corrections', 'OTIS')
kwargs.setdefault('formalism', 'Doodson')

# constituents array (not all are included in tidal program)
cindex = ['sa', 'ssa', 'mm', 'msf', 'mf', 'mt', 'alpha1', '2q1', 'sigma1',
Expand All @@ -710,21 +717,32 @@ def doodson_number(
's6', 's7', 's8', 'm8', 'mks2', 'msqm', 'mtm', 'n4', 'eps2', 'z0']
# get the table of coefficients
coefficients = _arguments_table(**kwargs)
# output dictionary with doodson numbers
DO = {}
# for each input constituent
for i,c in enumerate(constituents):
if isinstance(constituents, str):
# map between given constituents and supported in tidal program
j, = [j for j,val in enumerate(cindex) if (val == c)]
# remove phase dependence "k"
coef = coefficients[:-1,j]
# add 5 to values following Doodson convention (prevent negatives)
coef[1:] += 5
# convert to single number and round off floating point errors
value = np.sum([v*10**(2-o) for o,v in enumerate(coef)])
DO[c] = np.round(value, decimals=3)
# return the doodson number
return DO
j, = [j for j,val in enumerate(cindex) if (val == constituents)]
# extract identifier in formalism
if (kwargs['formalism'] == 'Cartwright'):
# extract Cartwright number
numbers = np.array(coefficients[:6,j])

Check warning on line 726 in pyTMD/arguments.py

View check run for this annotation

Codecov / codecov/patch

pyTMD/arguments.py#L726

Added line #L726 was not covered by tests
elif (kwargs['formalism'] == 'Doodson'):
# convert from coefficients to Doodson number
numbers = _to_doodson_number(coefficients[:,j])
else:
# output dictionary with Doodson numbers
numbers = {}
# for each input constituent
for i,c in enumerate(constituents):
# map between given constituents and supported in tidal program
j, = [j for j,val in enumerate(cindex) if (val == c)]
# convert from coefficients to Doodson number
if (kwargs['formalism'] == 'Cartwright'):
# extract Cartwright number
numbers[c] = np.array(coefficients[:6,j])
elif (kwargs['formalism'] == 'Doodson'):
# convert from coefficients to Doodson number
numbers[c] = _to_doodson_number(coefficients[:,j])
# return the Doodson or Cartwright number
return numbers

def _arguments_table(**kwargs):
"""
Expand All @@ -738,7 +756,7 @@ def _arguments_table(**kwargs):
Returns
-------
coef: np.ndarray
Spectral lines for each constituent
Doodson coefficients (Cartwright numbers) for each constituent
References
----------
Expand Down Expand Up @@ -838,7 +856,7 @@ def _minor_table(**kwargs):
Returns
-------
coef: np.ndarray
Spectral lines for each constituent
Doodson coefficients (Cartwright numbers) for each constituent
References
----------
Expand Down Expand Up @@ -882,3 +900,46 @@ def _minor_table(**kwargs):
coef[:,19] = [2.0, 3.0, 0.0, 0.0, 0.0, -1.0, 0.0] # eta2
# return the coefficient table
return coef

def _to_doodson_number(coef: list | np.ndarray):
"""
Converts Cartwright numbers into a Doodson number
Parameters
----------
coef: list or np.ndarray
Doodson coefficients (Cartwright numbers) for constituent
Returns
-------
DO: float
Doodson number for constituent
"""
# assert length and verify array
coef = np.array(coef[:6])
# add 5 to values following Doodson convention (prevent negatives)
coef[1:] += 5
# convert to single number and round off floating point errors
DO = np.sum([v*10**(2-o) for o,v in enumerate(coef)])
return np.round(DO, decimals=3)

def _from_doodson_number(DO: float | np.ndarray):
"""
Converts Doodson numbers into Cartwright numbers
Parameters
----------
DO: float or np.ndarray
Doodson number for constituent
Returns
-------
coef: np.ndarray
Doodson coefficients (Cartwright numbers) for constituent
"""
# convert from Doodson number to Cartwright numbers
# multiply by 1000 to prevent floating point errors
coef = np.array([np.mod(1e3*DO, 10**(6-o))//10**(5-o) for o in range(6)])
# remove 5 from values following Doodson convention
coef[1:] -= 5
return coef
19 changes: 18 additions & 1 deletion test/test_arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
import pytest
import numpy as np
import pyTMD.astro
from pyTMD.arguments import doodson_number, _arguments_table, _minor_table
from pyTMD.arguments import (
doodson_number,
_arguments_table,
_minor_table,
_to_doodson_number,
_from_doodson_number
)

@pytest.mark.parametrize("corrections", ['OTIS', 'GOT'])
def test_arguments(corrections):
Expand Down Expand Up @@ -199,6 +205,17 @@ def test_doodson():
exp['m8'] = 855.555
# get observed values for constituents
obs = doodson_number(exp.keys())
cartwright = doodson_number(exp.keys(), formalism='Cartwright')
# check values
for key,val in exp.items():
assert val == obs[key]
# check values when entered as string
test = doodson_number(key)
assert val == test
# check conversion to and from Doodson numbers
doodson = _to_doodson_number(cartwright[key])
# check values when entered as Cartwright
assert val == doodson
# check values when entered as Doodson
coefficients = _from_doodson_number(val)
assert np.all(cartwright[key] == coefficients)
2 changes: 1 addition & 1 deletion test/test_download_and_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ def test_tidal_ellipse(self):
assert np.all(np.abs(difference) < eps)

# PURPOSE: Tests solving for harmonic constants
def test_solve():
def test_solve(self):
# get model parameters
model = pyTMD.io.model(filepath).elevation('CATS2008')

Expand Down

0 comments on commit f33587e

Please sign in to comment.