diff --git a/MANIFEST.in b/MANIFEST.in index 437b119e..e2fbbeb1 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,4 +6,3 @@ prune run* prune test* exclude *.cfg exclude *.yml -include requirements.txt diff --git a/doc/source/api_reference/io/model.rst b/doc/source/api_reference/io/model.rst index 8706dc8b..546765ed 100644 --- a/doc/source/api_reference/io/model.rst +++ b/doc/source/api_reference/io/model.rst @@ -11,5 +11,7 @@ Retrieves tide model parameters for named tide models and from model definition General Attributes and Methods ============================== +.. autofunction:: pyTMD.io.load_database + .. autoclass:: pyTMD.io.model :members: diff --git a/doc/source/getting_started/Getting-Started.rst b/doc/source/getting_started/Getting-Started.rst index 4f4961f8..3fd0de2b 100644 --- a/doc/source/getting_started/Getting-Started.rst +++ b/doc/source/getting_started/Getting-Started.rst @@ -24,7 +24,7 @@ Directories ``pyTMD`` uses a tree structure for storing the tidal constituent data. This structure was chosen based on the different formats of each tide model. -Presently, the following models and their directories parameterized within ``pyTMD``. +Presently, the following models and their directories are parameterized within ``pyTMD`` in a JSON database. - Circum-Antarctic Tidal Simulations [Padman2008]_ @@ -133,15 +133,9 @@ elevations or currents (zonal and meridional) for each point. Definition Files ################ -For models not parameterized within ``pyTMD``, the parameters can be set with a model definition file. -There are two types of definition files currently supported by ``pyTMD`` (``ascii`` and ``json``). - -The tab-delimited text format (``ascii``) consists of two separate columns (parameter names and parameter values). -These definition files are read line by line to fill a python dictionary mapping the names with their values. -For parameters consisting of lists, the parameter values can be separated by spaces or commas. -For FES-type models of currents, the two lists of model files (``u`` and ``v``) should be separated in the list with a semi-colon. - -The JSON format (``json``) directly maps the parameter names with their values stored in the appropriate data type (strings, lists, numbers, booleans, etc). +For models within the ``pyTMD`` database, the parameters can be set with a model definition file in JSON format. +The JSON definition files follow a similar structure as the main ``pyTMD`` database, but for individual entries. +The JSON format directly maps the parameter names with their values stored in the appropriate data type (strings, lists, numbers, booleans, etc). For FES-type models of currents, the two lists of model files (``u`` and ``v``) are stored in a name-value pair objects (similar to a python dictionary). While still human readable, the JSON format is both interoperable and more easily machine readable. diff --git a/notebooks/Check Tide Map.ipynb b/notebooks/Check Tide Map.ipynb index ce62e5da..a2b3141e 100644 --- a/notebooks/Check Tide Map.ipynb +++ b/notebooks/Check Tide Map.ipynb @@ -114,7 +114,7 @@ " format=TMDwidgets.atlas.value,\n", " compressed=TMDwidgets.compress.value\n", " ).elevation(TMDwidgets.model.value)\n", - " \n", + "\n", "# read tidal constants and interpolate to grid points\n", "if model.format in ('OTIS','ATLAS-compact','TMD3'):\n", " # if reading a single OTIS solution\n", diff --git a/notebooks/Plot Antarctic Tidal Currents.ipynb b/notebooks/Plot Antarctic Tidal Currents.ipynb index 169c7b62..39a4049e 100644 --- a/notebooks/Plot Antarctic Tidal Currents.ipynb +++ b/notebooks/Plot Antarctic Tidal Currents.ipynb @@ -97,7 +97,7 @@ "outputs": [], "source": [ "# available model list\n", - "model_list = sorted(pyTMD.io.model.global_current() + pyTMD.io.model.antarctic_current())\n", + "model_list = sorted(pyTMD.io.model.ocean_current())\n", "# display widgets for setting directory and model\n", "TMDwidgets = pyTMD.tools.widgets()\n", "TMDwidgets.model.options = model_list\n", @@ -287,7 +287,7 @@ "ttl = fig.suptitle(None, y=0.97, fontsize=13)\n", "# adjust subplot within figure\n", "fig.subplots_adjust(left=0.02,right=0.98,bottom=0.1,top=0.98,wspace=0.04)\n", - " \n", + "\n", "# animate each map\n", "def animate_maps(hour):\n", " # set map data iterating over u and v currents\n", diff --git a/notebooks/Plot Antarctic Tide Range.ipynb b/notebooks/Plot Antarctic Tide Range.ipynb index 473546eb..f63b2a2f 100644 --- a/notebooks/Plot Antarctic Tide Range.ipynb +++ b/notebooks/Plot Antarctic Tide Range.ipynb @@ -86,7 +86,7 @@ "outputs": [], "source": [ "# available model list\n", - "model_list = sorted(pyTMD.io.model.global_ocean() + pyTMD.io.model.antarctic_ocean())\n", + "model_list = sorted(pyTMD.io.model.ocean_elevation())\n", "# display widgets for setting directory and model\n", "TMDwidgets = pyTMD.tools.widgets()\n", "TMDwidgets.model.options = model_list\n", @@ -165,7 +165,7 @@ "source": [ "def infer_minor_amplitudes(zmajor,constituents):\n", " # number of constituents\n", - " npts,nc = np.shape(zmajor) \n", + " npts,nc = np.shape(zmajor)\n", " cindex = ['q1','o1','p1','k1','n2','m2','s2','k2']\n", " # re-order zmajor to correspond to cindex\n", " z8 = np.ma.zeros((npts,8))\n", diff --git a/notebooks/Plot Arctic Ocean Map.ipynb b/notebooks/Plot Arctic Ocean Map.ipynb index 9bb32073..a94c45c9 100644 --- a/notebooks/Plot Arctic Ocean Map.ipynb +++ b/notebooks/Plot Arctic Ocean Map.ipynb @@ -99,7 +99,7 @@ "outputs": [], "source": [ "# available model list\n", - "model_list = sorted(pyTMD.io.model.global_ocean() + pyTMD.io.model.arctic_ocean())\n", + "model_list = sorted(pyTMD.io.model.ocean_elevation())\n", "# display widgets for setting directory and model\n", "TMDwidgets = pyTMD.tools.widgets()\n", "TMDwidgets.model.options = model_list\n", @@ -207,12 +207,12 @@ " c = model.constituents\n", " # delta time (TT - UT1)\n", " DELTAT = ts.tt_ut1\n", - " \n", + "\n", "# calculate complex phase in radians for Euler's\n", "cph = -1j*ph*np.pi/180.0\n", "# calculate constituent oscillation\n", "hc = amp*np.exp(cph)\n", - " \n", + "\n", "# allocate for tide map calculated every hour\n", "tide_cm = np.ma.zeros((ny,nx,24))\n", "for hour in range(24):\n", @@ -280,7 +280,7 @@ "ax.spines['geo'].set_capstyle('projecting')\n", "# adjust subplot within figure\n", "fig.subplots_adjust(left=0.02,right=0.98,bottom=0.05,top=0.95)\n", - " \n", + "\n", "# animate each map\n", "def animate_maps(hour):\n", " # set map data\n", diff --git a/notebooks/Plot Ross Ice Shelf Map.ipynb b/notebooks/Plot Ross Ice Shelf Map.ipynb index cecf4841..a659c1d3 100644 --- a/notebooks/Plot Ross Ice Shelf Map.ipynb +++ b/notebooks/Plot Ross Ice Shelf Map.ipynb @@ -99,7 +99,7 @@ "outputs": [], "source": [ "# available model list\n", - "model_list = sorted(pyTMD.io.model.global_ocean() + pyTMD.io.model.antarctic_ocean())\n", + "model_list = sorted(pyTMD.io.model.ocean_elevation())\n", "# display widgets for setting directory and model\n", "TMDwidgets = pyTMD.tools.widgets()\n", "TMDwidgets.model.options = model_list\n", @@ -207,12 +207,12 @@ " c = model.constituents\n", " # delta time (TT - UT1)\n", " DELTAT = ts.tt_ut1\n", - " \n", + "\n", "# calculate complex phase in radians for Euler's\n", "cph = -1j*ph*np.pi/180.0\n", "# calculate constituent oscillation\n", "hc = amp*np.exp(cph)\n", - " \n", + "\n", "# allocate for tide map calculated every hour\n", "tide_cm = np.ma.zeros((ny,nx,24))\n", "for hour in range(24):\n", @@ -280,7 +280,7 @@ "ax.spines['geo'].set_capstyle('projecting')\n", "# adjust subplot within figure\n", "fig.subplots_adjust(left=0.02,right=0.98,bottom=0.05,top=0.98)\n", - " \n", + "\n", "# animate each map\n", "def animate_maps(hour):\n", " # set map data\n", diff --git a/providers/AVISO.json b/providers/AVISO.json new file mode 100644 index 00000000..4c8b2489 --- /dev/null +++ b/providers/AVISO.json @@ -0,0 +1,451 @@ +{ + "current": { + "FES2014": { + "constituents": [ + "2n2", + "eps2", + "j1", + "k1", + "k2", + "l2", + "lambda2", + "m2", + "m3", + "m4", + "m6", + "m8", + "mf", + "mks2", + "mm", + "mn4", + "ms4", + "msf", + "msqm", + "mtm", + "mu2", + "n2", + "n4", + "nu2", + "o1", + "p1", + "q1", + "r2", + "s1", + "s2", + "s4", + "sa", + "ssa", + "t2" + ], + "format": "FES-netcdf", + "model_file": { + "u": [ + "fes2014/eastward_velocity/2n2.nc", + "fes2014/eastward_velocity/eps2.nc", + "fes2014/eastward_velocity/j1.nc", + "fes2014/eastward_velocity/k1.nc", + "fes2014/eastward_velocity/k2.nc", + "fes2014/eastward_velocity/l2.nc", + "fes2014/eastward_velocity/la2.nc", + "fes2014/eastward_velocity/m2.nc", + "fes2014/eastward_velocity/m3.nc", + "fes2014/eastward_velocity/m4.nc", + "fes2014/eastward_velocity/m6.nc", + "fes2014/eastward_velocity/m8.nc", + "fes2014/eastward_velocity/mf.nc", + "fes2014/eastward_velocity/mks2.nc", + "fes2014/eastward_velocity/mm.nc", + "fes2014/eastward_velocity/mn4.nc", + "fes2014/eastward_velocity/ms4.nc", + "fes2014/eastward_velocity/msf.nc", + "fes2014/eastward_velocity/msqm.nc", + "fes2014/eastward_velocity/mtm.nc", + "fes2014/eastward_velocity/mu2.nc", + "fes2014/eastward_velocity/n2.nc", + "fes2014/eastward_velocity/n4.nc", + "fes2014/eastward_velocity/nu2.nc", + "fes2014/eastward_velocity/o1.nc", + "fes2014/eastward_velocity/p1.nc", + "fes2014/eastward_velocity/q1.nc", + "fes2014/eastward_velocity/r2.nc", + "fes2014/eastward_velocity/s1.nc", + "fes2014/eastward_velocity/s2.nc", + "fes2014/eastward_velocity/s4.nc", + "fes2014/eastward_velocity/sa.nc", + "fes2014/eastward_velocity/ssa.nc", + "fes2014/eastward_velocity/t2.nc" + ], + "v": [ + "fes2014/northward_velocity/2n2.nc", + "fes2014/northward_velocity/eps2.nc", + "fes2014/northward_velocity/j1.nc", + "fes2014/northward_velocity/k1.nc", + "fes2014/northward_velocity/k2.nc", + "fes2014/northward_velocity/l2.nc", + "fes2014/northward_velocity/la2.nc", + "fes2014/northward_velocity/m2.nc", + "fes2014/northward_velocity/m3.nc", + "fes2014/northward_velocity/m4.nc", + "fes2014/northward_velocity/m6.nc", + "fes2014/northward_velocity/m8.nc", + "fes2014/northward_velocity/mf.nc", + "fes2014/northward_velocity/mks2.nc", + "fes2014/northward_velocity/mm.nc", + "fes2014/northward_velocity/mn4.nc", + "fes2014/northward_velocity/ms4.nc", + "fes2014/northward_velocity/msf.nc", + "fes2014/northward_velocity/msqm.nc", + "fes2014/northward_velocity/mtm.nc", + "fes2014/northward_velocity/mu2.nc", + "fes2014/northward_velocity/n2.nc", + "fes2014/northward_velocity/n4.nc", + "fes2014/northward_velocity/nu2.nc", + "fes2014/northward_velocity/o1.nc", + "fes2014/northward_velocity/p1.nc", + "fes2014/northward_velocity/q1.nc", + "fes2014/northward_velocity/r2.nc", + "fes2014/northward_velocity/s1.nc", + "fes2014/northward_velocity/s2.nc", + "fes2014/northward_velocity/s4.nc", + "fes2014/northward_velocity/sa.nc", + "fes2014/northward_velocity/ssa.nc", + "fes2014/northward_velocity/t2.nc" + ] + }, + "name": "FES2014", + "reference": "https://www.aviso.altimetry.fr/en/data/productsauxiliary-products/global-tide-fes.html", + "scale": 1.0, + "type": [ + "u", + "v" + ], + "version": "FES2014" + } + }, + "elevation": { + "FES2014": { + "constituents": [ + "2n2", + "eps2", + "j1", + "k1", + "k2", + "l2", + "lambda2", + "m2", + "m3", + "m4", + "m6", + "m8", + "mf", + "mks2", + "mm", + "mn4", + "ms4", + "msf", + "msqm", + "mtm", + "mu2", + "n2", + "n4", + "nu2", + "o1", + "p1", + "q1", + "r2", + "s1", + "s2", + "s4", + "sa", + "ssa", + "t2" + ], + "format": "FES-netcdf", + "model_file": [ + "fes2014/ocean_tide/2n2.nc", + "fes2014/ocean_tide/eps2.nc", + "fes2014/ocean_tide/j1.nc", + "fes2014/ocean_tide/k1.nc", + "fes2014/ocean_tide/k2.nc", + "fes2014/ocean_tide/l2.nc", + "fes2014/ocean_tide/la2.nc", + "fes2014/ocean_tide/m2.nc", + "fes2014/ocean_tide/m3.nc", + "fes2014/ocean_tide/m4.nc", + "fes2014/ocean_tide/m6.nc", + "fes2014/ocean_tide/m8.nc", + "fes2014/ocean_tide/mf.nc", + "fes2014/ocean_tide/mks2.nc", + "fes2014/ocean_tide/mm.nc", + "fes2014/ocean_tide/mn4.nc", + "fes2014/ocean_tide/ms4.nc", + "fes2014/ocean_tide/msf.nc", + "fes2014/ocean_tide/msqm.nc", + "fes2014/ocean_tide/mtm.nc", + "fes2014/ocean_tide/mu2.nc", + "fes2014/ocean_tide/n2.nc", + "fes2014/ocean_tide/n4.nc", + "fes2014/ocean_tide/nu2.nc", + "fes2014/ocean_tide/o1.nc", + "fes2014/ocean_tide/p1.nc", + "fes2014/ocean_tide/q1.nc", + "fes2014/ocean_tide/r2.nc", + "fes2014/ocean_tide/s1.nc", + "fes2014/ocean_tide/s2.nc", + "fes2014/ocean_tide/s4.nc", + "fes2014/ocean_tide/sa.nc", + "fes2014/ocean_tide/ssa.nc", + "fes2014/ocean_tide/t2.nc" + ], + "name": "FES2014", + "reference": "https://www.aviso.altimetry.fr/en/data/products/auxiliary-products/global-tide-fes.html", + "scale": 0.01, + "type": "z", + "variable": "tide_ocean", + "version": "FES2014" + }, + "FES2014_load": { + "constituents": [ + "2n2", + "eps2", + "j1", + "k1", + "k2", + "l2", + "lambda2", + "m2", + "m3", + "m4", + "m6", + "m8", + "mf", + "mks2", + "mm", + "mn4", + "ms4", + "msf", + "msqm", + "mtm", + "mu2", + "n2", + "n4", + "nu2", + "o1", + "p1", + "q1", + "r2", + "s1", + "s2", + "s4", + "sa", + "ssa", + "t2" + ], + "format": "FES-netcdf", + "model_file": [ + "fes2014/load_tide/2n2.nc", + "fes2014/load_tide/eps2.nc", + "fes2014/load_tide/j1.nc", + "fes2014/load_tide/k1.nc", + "fes2014/load_tide/k2.nc", + "fes2014/load_tide/l2.nc", + "fes2014/load_tide/la2.nc", + "fes2014/load_tide/m2.nc", + "fes2014/load_tide/m3.nc", + "fes2014/load_tide/m4.nc", + "fes2014/load_tide/m6.nc", + "fes2014/load_tide/m8.nc", + "fes2014/load_tide/mf.nc", + "fes2014/load_tide/mks2.nc", + "fes2014/load_tide/mm.nc", + "fes2014/load_tide/mn4.nc", + "fes2014/load_tide/ms4.nc", + "fes2014/load_tide/msf.nc", + "fes2014/load_tide/msqm.nc", + "fes2014/load_tide/mtm.nc", + "fes2014/load_tide/mu2.nc", + "fes2014/load_tide/n2.nc", + "fes2014/load_tide/n4.nc", + "fes2014/load_tide/nu2.nc", + "fes2014/load_tide/o1.nc", + "fes2014/load_tide/p1.nc", + "fes2014/load_tide/q1.nc", + "fes2014/load_tide/r2.nc", + "fes2014/load_tide/s1.nc", + "fes2014/load_tide/s2.nc", + "fes2014/load_tide/s4.nc", + "fes2014/load_tide/sa.nc", + "fes2014/load_tide/ssa.nc", + "fes2014/load_tide/t2.nc" + ], + "name": "FES2014_load", + "reference": "https://www.aviso.altimetry.fr/en/data/products/auxiliary-products/global-tide-fes.html", + "scale": 0.01, + "type": "z", + "variable": "tide_load", + "version": "FES2014" + }, + "FES2022": { + "constituents": [ + "2n2", + "eps2", + "j1", + "k1", + "k2", + "l2", + "lambda2", + "m2", + "m3", + "m4", + "m6", + "m8", + "mf", + "mks2", + "mm", + "mn4", + "ms4", + "msf", + "msqm", + "mtm", + "mu2", + "n2", + "n4", + "nu2", + "o1", + "p1", + "q1", + "r2", + "s1", + "s2", + "s4", + "sa", + "ssa", + "t2" + ], + "format": "FES-netcdf", + "model_file": [ + "fes2022b/ocean_tide/2n2_fes2022.nc", + "fes2022b/ocean_tide/eps2_fes2022.nc", + "fes2022b/ocean_tide/j1_fes2022.nc", + "fes2022b/ocean_tide/k1_fes2022.nc", + "fes2022b/ocean_tide/k2_fes2022.nc", + "fes2022b/ocean_tide/l2_fes2022.nc", + "fes2022b/ocean_tide/lambda2_fes2022.nc", + "fes2022b/ocean_tide/m2_fes2022.nc", + "fes2022b/ocean_tide/m3_fes2022.nc", + "fes2022b/ocean_tide/m4_fes2022.nc", + "fes2022b/ocean_tide/m6_fes2022.nc", + "fes2022b/ocean_tide/m8_fes2022.nc", + "fes2022b/ocean_tide/mf_fes2022.nc", + "fes2022b/ocean_tide/mks2_fes2022.nc", + "fes2022b/ocean_tide/mm_fes2022.nc", + "fes2022b/ocean_tide/mn4_fes2022.nc", + "fes2022b/ocean_tide/ms4_fes2022.nc", + "fes2022b/ocean_tide/msf_fes2022.nc", + "fes2022b/ocean_tide/msqm_fes2022.nc", + "fes2022b/ocean_tide/mtm_fes2022.nc", + "fes2022b/ocean_tide/mu2_fes2022.nc", + "fes2022b/ocean_tide/n2_fes2022.nc", + "fes2022b/ocean_tide/n4_fes2022.nc", + "fes2022b/ocean_tide/nu2_fes2022.nc", + "fes2022b/ocean_tide/o1_fes2022.nc", + "fes2022b/ocean_tide/p1_fes2022.nc", + "fes2022b/ocean_tide/q1_fes2022.nc", + "fes2022b/ocean_tide/r2_fes2022.nc", + "fes2022b/ocean_tide/s1_fes2022.nc", + "fes2022b/ocean_tide/s2_fes2022.nc", + "fes2022b/ocean_tide/s4_fes2022.nc", + "fes2022b/ocean_tide/sa_fes2022.nc", + "fes2022b/ocean_tide/ssa_fes2022.nc", + "fes2022b/ocean_tide/t2_fes2022.nc" + ], + "name": "FES2022", + "reference": "https://doi.org/10.24400/527896/A01-2024.004", + "scale": 0.01, + "type": "z", + "variable": "tide_ocean", + "version": "FES2022" + }, + "FES2022_load": { + "constituents": [ + "2n2", + "eps2", + "j1", + "k1", + "k2", + "l2", + "lambda2", + "m2", + "m3", + "m4", + "m6", + "m8", + "mf", + "mks2", + "mm", + "mn4", + "ms4", + "msf", + "msqm", + "mtm", + "mu2", + "n2", + "n4", + "nu2", + "o1", + "p1", + "q1", + "r2", + "s1", + "s2", + "s4", + "sa", + "ssa", + "t2" + ], + "format": "FES-netcdf", + "model_file": [ + "fes2022b/load_tide/2n2_fes2022.nc", + "fes2022b/load_tide/eps2_fes2022.nc", + "fes2022b/load_tide/j1_fes2022.nc", + "fes2022b/load_tide/k1_fes2022.nc", + "fes2022b/load_tide/k2_fes2022.nc", + "fes2022b/load_tide/l2_fes2022.nc", + "fes2022b/load_tide/lambda2_fes2022.nc", + "fes2022b/load_tide/m2_fes2022.nc", + "fes2022b/load_tide/m3_fes2022.nc", + "fes2022b/load_tide/m4_fes2022.nc", + "fes2022b/load_tide/m6_fes2022.nc", + "fes2022b/load_tide/m8_fes2022.nc", + "fes2022b/load_tide/mf_fes2022.nc", + "fes2022b/load_tide/mks2_fes2022.nc", + "fes2022b/load_tide/mm_fes2022.nc", + "fes2022b/load_tide/mn4_fes2022.nc", + "fes2022b/load_tide/ms4_fes2022.nc", + "fes2022b/load_tide/msf_fes2022.nc", + "fes2022b/load_tide/msqm_fes2022.nc", + "fes2022b/load_tide/mtm_fes2022.nc", + "fes2022b/load_tide/mu2_fes2022.nc", + "fes2022b/load_tide/n2_fes2022.nc", + "fes2022b/load_tide/n4_fes2022.nc", + "fes2022b/load_tide/nu2_fes2022.nc", + "fes2022b/load_tide/o1_fes2022.nc", + "fes2022b/load_tide/p1_fes2022.nc", + "fes2022b/load_tide/q1_fes2022.nc", + "fes2022b/load_tide/r2_fes2022.nc", + "fes2022b/load_tide/s1_fes2022.nc", + "fes2022b/load_tide/s2_fes2022.nc", + "fes2022b/load_tide/s4_fes2022.nc", + "fes2022b/load_tide/sa_fes2022.nc", + "fes2022b/load_tide/ssa_fes2022.nc", + "fes2022b/load_tide/t2_fes2022.nc" + ], + "name": "FES2022_load", + "reference": "https://doi.org/10.24400/527896/A01-2024.004", + "scale": 0.01, + "type": "z", + "variable": "tide_load", + "version": "FES2022" + } + } +} \ No newline at end of file diff --git a/providers/ESR.json b/providers/ESR.json new file mode 100644 index 00000000..298bedfa --- /dev/null +++ b/providers/ESR.json @@ -0,0 +1,215 @@ +{ + "current": { + "AODTM-5": { + "format": "OTIS", + "grid_file": "aodtm5_tmd/grid_Arc5km", + "model_file": { + "u": "aodtm5_tmd/UV0_Arc5km" + }, + "name": "AODTM-5", + "projection": "PSNorth", + "reference": "https://www.esr.org/research/polar-tide-models/list-of-polar-tide-models/aodtm-5/", + "type": [ + "u", + "v" + ] + }, + "AOTIM-5": { + "format": "OTIS", + "grid_file": "aotim5_tmd/grid_Arc5km", + "model_file": { + "u": "aotim5_tmd/UV_Arc5km" + }, + "name": "AOTIM-5", + "projection": "PSNorth", + "reference": "https://www.esr.org/research/polar-tide-models/list-of-polar-tide-models/aotim-5/", + "type": [ + "u", + "v" + ] + }, + "AOTIM-5-2018": { + "format": "OTIS", + "grid_file": "Arc5km2018/grid_Arc5km2018", + "model_file": { + "u": "Arc5km2018/UV_Arc5km2018" + }, + "name": "AOTIM-5-2018", + "projection": "PSNorth", + "reference": "https://www.esr.org/research/polar-tide-models/list-of-polar-tide-models/aotim-5/", + "type": [ + "u", + "v" + ], + "version": "2018" + }, + "Arc2kmTM": { + "format": "OTIS", + "grid_file": "Arc2kmTM/grid_Arc2kmTM_v1", + "model_file": { + "u": "Arc2kmTM/UV_Arc2kmTM_v1" + }, + "name": "Arc2kmTM", + "projection": "3413", + "reference": "https://doi.org/10.18739/A2D21RK6K", + "type": [ + "u", + "v" + ], + "version": "v1" + }, + "CATS0201": { + "format": "OTIS", + "grid_file": "cats0201_tmd/grid_CATS", + "model_file": { + "u": "cats0201_tmd/UV0_CATS02_01" + }, + "name": "CATS0201", + "projection": "4326", + "reference": "https://mail.esr.org/polar_tide_models/Model_CATS0201.html", + "type": [ + "u", + "v" + ] + }, + "CATS2008": { + "format": "OTIS", + "grid_file": "CATS2008/grid_CATS2008", + "model_file": { + "u": "CATS2008/uv.CATS2008.out" + }, + "name": "CATS2008", + "projection": "CATS2008", + "type": [ + "u", + "v" + ] + }, + "Gr1km-v2": { + "format": "OTIS", + "grid_file": "greenlandTMD_v2/grid_Greenland8.v2", + "model_file": { + "u": "greenlandTMD_v2/u_Greenland8_rot.v2" + }, + "name": "Gr1km-v2", + "projection": "3413", + "reference": "https://doi.org/10.1002/2016RG000546", + "type": [ + "u", + "v" + ], + "version": "v2" + }, + "Gr1kmTM": { + "format": "OTIS", + "grid_file": "Gr1kmTM/grid_Gr1kmTM_v1", + "model_file": { + "u": "Gr1kmTM/UV_Gr1kmTM_v1" + }, + "name": "Gr1kmTM", + "projection": "3413", + "reference": "https://doi.org/10.18739/A2B853K18", + "type": [ + "u", + "v" + ], + "version": "v1" + } + }, + "elevation": { + "AODTM-5": { + "format": "OTIS", + "grid_file": "aodtm5_tmd/grid_Arc5km", + "model_file": "aodtm5_tmd/h0_Arc5km.oce", + "name": "AODTM-5", + "projection": "PSNorth", + "reference": "https://www.esr.org/research/polar-tide-models/list-of-polar-tide-models/aodtm-5/", + "type": "z", + "variable": "tide_ocean" + }, + "AOTIM-5": { + "format": "OTIS", + "grid_file": "aotim5_tmd/grid_Arc5km", + "model_file": "aotim5_tmd/h_Arc5km.oce", + "name": "AOTIM-5", + "projection": "PSNorth", + "reference": "https://www.esr.org/research/polar-tide-models/list-of-polar-tide-models/aotim-5/", + "type": "z", + "variable": "tide_ocean" + }, + "AOTIM-5-2018": { + "format": "OTIS", + "grid_file": "Arc5km2018/grid_Arc5km2018", + "model_file": "Arc5km2018/h_Arc5km2018", + "name": "AOTIM-5-2018", + "projection": "PSNorth", + "reference": "https://www.esr.org/research/polar-tide-models/list-of-polar-tide-models/aotim-5/", + "type": "z", + "variable": "tide_ocean", + "version": "2018" + }, + "Arc2kmTM": { + "format": "OTIS", + "grid_file": "Arc2kmTM/grid_Arc2kmTM_v1", + "model_file": "Arc2kmTM/h_Arc2kmTM_v1", + "name": "Arc2kmTM", + "projection": "3413", + "reference": "https://doi.org/10.18739/A2D21RK6K", + "type": "z", + "variable": "tide_ocean", + "version": "v1" + }, + "CATS0201": { + "format": "OTIS", + "grid_file": "cats0201_tmd/grid_CATS", + "model_file": "cats0201_tmd/h0_CATS02_01", + "name": "CATS0201", + "projection": "4326", + "reference": "https://mail.esr.org/polar_tide_models/Model_CATS0201.html", + "type": "z", + "variable": "tide_ocean" + }, + "CATS2008": { + "format": "OTIS", + "grid_file": "CATS2008/grid_CATS2008", + "model_file": "CATS2008/hf.CATS2008.out", + "name": "CATS2008", + "projection": "CATS2008", + "reference": "https://doi.org/10.15784/601235", + "type": "z", + "variable": "tide_ocean" + }, + "CATS2008_load": { + "format": "OTIS", + "grid_file": "CATS2008a_SPOTL_Load/grid_CATS2008a_opt", + "model_file": "CATS2008a_SPOTL_Load/h_CATS2008a_SPOTL_load", + "name": "CATS2008_load", + "projection": "CATS2008", + "reference": "https://doi.org/10.15784/601235", + "type": "z", + "variable": "tide_load" + }, + "Gr1km-v2": { + "format": "OTIS", + "grid_file": "greenlandTMD_v2/grid_Greenland8.v2", + "model_file": "greenlandTMD_v2/h_Greenland8.v2", + "name": "Gr1km-v2", + "projection": "3413", + "reference": "https://doi.org/10.1002/2016RG000546", + "type": "z", + "variable": "tide_ocean", + "version": "v2" + }, + "Gr1kmTM": { + "format": "OTIS", + "grid_file": "Gr1kmTM/grid_Gr1kmTM_v1", + "model_file": "Gr1kmTM/h_Gr1kmTM_v1", + "name": "Gr1kmTM", + "projection": "3413", + "reference": "https://doi.org/10.18739/A2B853K18", + "type": "z", + "variable": "tide_ocean", + "version": "v1" + } + } +} \ No newline at end of file diff --git a/providers/GSFC.json b/providers/GSFC.json new file mode 100644 index 00000000..8fefd895 --- /dev/null +++ b/providers/GSFC.json @@ -0,0 +1,184 @@ +{ + "elevation": { + "GOT4.10": { + "format": "GOT-ascii", + "model_file": [ + "GOT4.10c/grids_oceantide/q1.d", + "GOT4.10c/grids_oceantide/o1.d", + "GOT4.10c/grids_oceantide/p1.d", + "GOT4.10c/grids_oceantide/k1.d", + "GOT4.10c/grids_oceantide/n2.d", + "GOT4.10c/grids_oceantide/m2.d", + "GOT4.10c/grids_oceantide/s2.d", + "GOT4.10c/grids_oceantide/k2.d", + "GOT4.10c/grids_oceantide/s1.d", + "GOT4.10c/grids_oceantide/m4.d" + ], + "name": "GOT4.10", + "reference": "https://ntrs.nasa.gov/citations/19990089548", + "scale": 0.01, + "type": "z", + "variable": "tide_ocean", + "version": "4.10" + }, + "GOT4.10_load": { + "format": "GOT-ascii", + "model_file": [ + "GOT4.10c/grids_loadtide/q1load.d", + "GOT4.10c/grids_loadtide/o1load.d", + "GOT4.10c/grids_loadtide/p1load.d", + "GOT4.10c/grids_loadtide/k1load.d", + "GOT4.10c/grids_loadtide/n2load.d", + "GOT4.10c/grids_loadtide/m2load.d", + "GOT4.10c/grids_loadtide/s2load.d", + "GOT4.10c/grids_loadtide/k2load.d", + "GOT4.10c/grids_loadtide/s1load.d", + "GOT4.10c/grids_loadtide/m4load.d" + ], + "name": "GOT4.10_load", + "reference": "https://ntrs.nasa.gov/citations/19990089548", + "scale": 0.001, + "type": "z", + "variable": "tide_load", + "version": "4.10" + }, + "GOT4.7": { + "format": "GOT-ascii", + "model_file": [ + "GOT4.7/grids_oceantide/q1.d", + "GOT4.7/grids_oceantide/o1.d", + "GOT4.7/grids_oceantide/p1.d", + "GOT4.7/grids_oceantide/k1.d", + "GOT4.7/grids_oceantide/n2.d", + "GOT4.7/grids_oceantide/m2.d", + "GOT4.7/grids_oceantide/s2.d", + "GOT4.7/grids_oceantide/k2.d", + "GOT4.7/grids_oceantide/s1.d", + "GOT4.7/grids_oceantide/m4.d" + ], + "name": "GOT4.7", + "reference": "https://ntrs.nasa.gov/citations/19990089548", + "scale": 0.01, + "type": "z", + "variable": "tide_ocean", + "version": "4.7" + }, + "GOT4.7_load": { + "format": "GOT-ascii", + "model_file": [ + "GOT4.7/grids_loadtide/q1load.d", + "GOT4.7/grids_loadtide/o1load.d", + "GOT4.7/grids_loadtide/p1load.d", + "GOT4.7/grids_loadtide/k1load.d", + "GOT4.7/grids_loadtide/n2load.d", + "GOT4.7/grids_loadtide/m2load.d", + "GOT4.7/grids_loadtide/s2load.d", + "GOT4.7/grids_loadtide/k2load.d", + "GOT4.7/grids_loadtide/s1load.d", + "GOT4.7/grids_loadtide/m4load.d" + ], + "name": "GOT4.7_load", + "reference": "https://ntrs.nasa.gov/citations/19990089548", + "scale": 0.001, + "type": "z", + "variable": "tide_load", + "version": "4.7" + }, + "GOT4.8": { + "format": "GOT-ascii", + "model_file": [ + "got4.8/grids_oceantide/q1.d", + "got4.8/grids_oceantide/o1.d", + "got4.8/grids_oceantide/p1.d", + "got4.8/grids_oceantide/k1.d", + "got4.8/grids_oceantide/n2.d", + "got4.8/grids_oceantide/m2.d", + "got4.8/grids_oceantide/s2.d", + "got4.8/grids_oceantide/k2.d", + "got4.8/grids_oceantide/s1.d", + "got4.8/grids_oceantide/m4.d" + ], + "name": "GOT4.8", + "reference": "https://ntrs.nasa.gov/citations/19990089548", + "scale": 0.01, + "type": "z", + "variable": "tide_ocean", + "version": "4.8" + }, + "GOT4.8_load": { + "format": "GOT-ascii", + "model_file": [ + "got4.8/grids_loadtide/q1load.d", + "got4.8/grids_loadtide/o1load.d", + "got4.8/grids_loadtide/p1load.d", + "got4.8/grids_loadtide/k1load.d", + "got4.8/grids_loadtide/n2load.d", + "got4.8/grids_loadtide/m2load.d", + "got4.8/grids_loadtide/s2load.d", + "got4.8/grids_loadtide/k2load.d", + "got4.8/grids_loadtide/s1load.d", + "got4.8/grids_loadtide/m4load.d" + ], + "name": "GOT4.8_load", + "reference": "https://ntrs.nasa.gov/citations/19990089548", + "scale": 0.001, + "type": "z", + "variable": "tide_load", + "version": "4.8" + }, + "GOT5.5": { + "format": "GOT-netcdf", + "model_file": [ + "GOT5.5/ocean_tides/2n2.nc", + "GOT5.5/ocean_tides/j1.nc", + "GOT5.5/ocean_tides/k1.nc", + "GOT5.5/ocean_tides/k2.nc", + "GOT5.5/ocean_tides/m2.nc", + "GOT5.5/ocean_tides/m4.nc", + "GOT5.5/ocean_tides/ms4.nc", + "GOT5.5/ocean_tides/mu2.nc", + "GOT5.5/ocean_tides/n2.nc", + "GOT5.5/ocean_tides/o1.nc", + "GOT5.5/ocean_tides/oo1.nc", + "GOT5.5/ocean_tides/p1.nc", + "GOT5.5/ocean_tides/q1.nc", + "GOT5.5/ocean_tides/s1.nc", + "GOT5.5/ocean_tides/s2.nc", + "GOT5.5/ocean_tides/sig1.nc" + ], + "name": "GOT5.5", + "reference": "https://ntrs.nasa.gov/citations/19990089548", + "scale": 0.01, + "type": "z", + "variable": "tide_ocean", + "version": "5.5" + }, + "GOT5.5_load": { + "format": "GOT-netcdf", + "model_file": [ + "GOT5.5/load_tides/2n2load.nc", + "GOT5.5/load_tides/k1load.nc", + "GOT5.5/load_tides/m2load.nc", + "GOT5.5/load_tides/ms4load.nc", + "GOT5.5/load_tides/n2load.nc", + "GOT5.5/load_tides/oo1load.nc", + "GOT5.5/load_tides/q1load.nc", + "GOT5.5/load_tides/s2load.nc", + "GOT5.5/load_tides/j1load.nc", + "GOT5.5/load_tides/k2load.nc", + "GOT5.5/load_tides/m4load.nc", + "GOT5.5/load_tides/mu2load.nc", + "GOT5.5/load_tides/o1load.nc", + "GOT5.5/load_tides/p1load.nc", + "GOT5.5/load_tides/s1load.nc", + "GOT5.5/load_tides/sig1load.nc" + ], + "name": "GOT5.5_load", + "reference": "https://ntrs.nasa.gov/citations/19990089548", + "scale": 0.001, + "type": "z", + "variable": "tide_load", + "version": "5.5" + } + } +} \ No newline at end of file diff --git a/providers/README.rst b/providers/README.rst new file mode 100644 index 00000000..93c170f2 --- /dev/null +++ b/providers/README.rst @@ -0,0 +1,8 @@ +=============== +pyTMD Providers +=============== + +- ``AVISO``: `AVISO Global Ocean Tide Models `_ +- ``ESR``: `Earth and Space Research Polar Tide Models `` +- ``GSFC``: `NASA Goddard Space Flight Center Global Tide Models `_ +- ``TPXO``: `Oregon State University TPXO Tide Models `_ diff --git a/providers/TPXO.json b/providers/TPXO.json new file mode 100644 index 00000000..e3d6fcca --- /dev/null +++ b/providers/TPXO.json @@ -0,0 +1,877 @@ +{ + "current": { + "TPXO10-atlas-v2": { + "format": "OTIS", + "grid_file": "TPXO10_atlas_v2/grid_tpxo10atlas_v2", + "model_file": { + "u": [ + "TPXO10_atlas_v2/u_2n2_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/u_k1_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/u_k2_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/u_m2_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/u_m4_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/u_mf_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/u_mm_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/u_mn4_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/u_ms4_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/u_n2_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/u_o1_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/u_p1_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/u_q1_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/u_s1_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/u_s2_tpxo10_atlas_30_v2" + ] + }, + "name": "TPXO10-atlas-v2", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo10-atlas", + "scale": 0.0001, + "type": [ + "u", + "v" + ], + "version": "v2" + }, + "TPXO10-atlas-v2-nc": { + "format": "ATLAS-netcdf", + "grid_file": "TPXO10_atlas_v2/grid_tpxo10atlas_v2.nc", + "model_file": { + "u": [ + "TPXO10_atlas_v2/u_2n2_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_k1_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_k2_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_m2_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_m4_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_mf_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_mm_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_mn4_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_ms4_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_n2_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_o1_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_p1_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_q1_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_s1_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_s2_tpxo10_atlas_30_v2.nc" + ], + "v": [ + "TPXO10_atlas_v2/u_2n2_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_k1_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_k2_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_m2_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_m4_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_mf_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_mm_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_mn4_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_ms4_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_n2_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_o1_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_p1_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_q1_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_s1_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/u_s2_tpxo10_atlas_30_v2.nc" + ] + }, + "name": "TPXO10-atlas-v2", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo10-atlas", + "scale": 0.0001, + "type": [ + "u", + "v" + ], + "version": "v2" + }, + "TPXO7.2": { + "format": "OTIS", + "grid_file": "TPXO7.2_tmd/grid_tpxo7.2", + "model_file": { + "u": "TPXO7.2_tmd/u_tpxo7.2" + }, + "name": "TPXO7.2", + "projection": "4326", + "reference": "http://volkov.oce.orst.edu/tides/global.html", + "type": [ + "u", + "v" + ], + "version": "7.2" + }, + "TPXO8-atlas": { + "format": "ATLAS-compact", + "grid_file": "tpxo8_atlas/grid_tpxo8atlas_30_v1", + "model_file": { + "u": "tpxo8_atlas/uv.tpxo8_atlas_30_v1" + }, + "name": "TPXO8-atlas", + "projection": "4326", + "reference": "http://volkov.oce.orst.edu/tides/tpxo8_atlas.html", + "type": [ + "u", + "v" + ], + "version": "8" + }, + "TPXO9-atlas": { + "format": "OTIS", + "grid_file": "TPXO9_atlas/grid_tpxo9_atlas", + "model_file": { + "u": [ + "TPXO9_atlas/u_q1_tpxo9_atlas_30", + "TPXO9_atlas/u_o1_tpxo9_atlas_30", + "TPXO9_atlas/u_p1_tpxo9_atlas_30", + "TPXO9_atlas/u_k1_tpxo9_atlas_30", + "TPXO9_atlas/u_n2_tpxo9_atlas_30", + "TPXO9_atlas/u_m2_tpxo9_atlas_30", + "TPXO9_atlas/u_s2_tpxo9_atlas_30", + "TPXO9_atlas/u_k2_tpxo9_atlas_30", + "TPXO9_atlas/u_m4_tpxo9_atlas_30", + "TPXO9_atlas/u_ms4_tpxo9_atlas_30", + "TPXO9_atlas/u_mn4_tpxo9_atlas_30", + "TPXO9_atlas/u_2n2_tpxo9_atlas_30" + ] + }, + "name": "TPXO9-atlas", + "projection": "4326", + "reference": "http://volkov.oce.orst.edu/tides/tpxo9_atlas.html", + "scale": 0.0001, + "type": [ + "u", + "v" + ], + "version": "v1" + }, + "TPXO9-atlas-nc": { + "format": "ATLAS-netcdf", + "grid_file": "TPXO9_atlas/grid_tpxo9_atlas.nc", + "model_file": { + "u": [ + "TPXO9_atlas/u_q1_tpxo9_atlas_30.nc", + "TPXO9_atlas/u_o1_tpxo9_atlas_30.nc", + "TPXO9_atlas/u_p1_tpxo9_atlas_30.nc", + "TPXO9_atlas/u_k1_tpxo9_atlas_30.nc", + "TPXO9_atlas/u_n2_tpxo9_atlas_30.nc", + "TPXO9_atlas/u_m2_tpxo9_atlas_30.nc", + "TPXO9_atlas/u_s2_tpxo9_atlas_30.nc", + "TPXO9_atlas/u_k2_tpxo9_atlas_30.nc", + "TPXO9_atlas/u_m4_tpxo9_atlas_30.nc", + "TPXO9_atlas/u_ms4_tpxo9_atlas_30.nc", + "TPXO9_atlas/u_mn4_tpxo9_atlas_30.nc", + "TPXO9_atlas/u_2n2_tpxo9_atlas_30.nc" + ], + "v": [ + "TPXO9_atlas/v_q1_tpxo9_atlas_30.nc", + "TPXO9_atlas/v_o1_tpxo9_atlas_30.nc", + "TPXO9_atlas/v_p1_tpxo9_atlas_30.nc", + "TPXO9_atlas/v_k1_tpxo9_atlas_30.nc", + "TPXO9_atlas/v_n2_tpxo9_atlas_30.nc", + "TPXO9_atlas/v_m2_tpxo9_atlas_30.nc", + "TPXO9_atlas/v_s2_tpxo9_atlas_30.nc", + "TPXO9_atlas/v_k2_tpxo9_atlas_30.nc", + "TPXO9_atlas/v_m4_tpxo9_atlas_30.nc", + "TPXO9_atlas/v_ms4_tpxo9_atlas_30.nc", + "TPXO9_atlas/v_mn4_tpxo9_atlas_30.nc", + "TPXO9_atlas/v_2n2_tpxo9_atlas_30.nc" + ] + }, + "name": "TPXO9-atlas", + "projection": "4326", + "reference": "http://volkov.oce.orst.edu/tides/tpxo9_atlas.html", + "scale": 0.0001, + "type": [ + "u", + "v" + ], + "version": "v1" + }, + "TPXO9-atlas-v2": { + "format": "OTIS", + "grid_file": "TPXO9_atlas_v2/grid_tpxo9_atlas_30_v2", + "model_file": { + "u": [ + "TPXO9_atlas_v2/u_q1_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/u_o1_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/u_p1_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/u_k1_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/u_n2_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/u_m2_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/u_s2_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/u_k2_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/u_m4_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/u_ms4_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/u_mn4_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/u_2n2_tpxo9_atlas_30_v2" + ] + }, + "name": "TPXO9-atlas-v2", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo9-atlas", + "scale": 0.0001, + "type": [ + "u", + "v" + ], + "version": "v2" + }, + "TPXO9-atlas-v2-nc": { + "format": "ATLAS-netcdf", + "grid_file": "TPXO9_atlas_v2/grid_tpxo9_atlas_30_v2.nc", + "model_file": { + "u": [ + "TPXO9_atlas_v2/u_q1_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/u_o1_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/u_p1_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/u_k1_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/u_n2_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/u_m2_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/u_s2_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/u_k2_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/u_m4_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/u_ms4_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/u_mn4_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/u_2n2_tpxo9_atlas_30_v2.nc" + ], + "v": [ + "TPXO9_atlas_v2/v_q1_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/v_o1_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/v_p1_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/v_k1_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/v_n2_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/v_m2_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/v_s2_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/v_k2_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/v_m4_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/v_ms4_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/v_mn4_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/v_2n2_tpxo9_atlas_30_v2.nc" + ] + }, + "name": "TPXO9-atlas-v2", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo9-atlas", + "scale": 0.0001, + "type": [ + "u", + "v" + ], + "version": "v2" + }, + "TPXO9-atlas-v3": { + "format": "OTIS", + "grid_file": "TPXO9_atlas_v3/grid_tpxo9_atlas_30_v3", + "model_file": { + "u": [ + "TPXO9_atlas_v3/u_q1_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/u_o1_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/u_p1_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/u_k1_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/u_n2_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/u_m2_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/u_s2_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/u_k2_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/u_m4_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/u_ms4_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/u_mn4_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/u_2n2_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/u_mf_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/u_mm_tpxo9_atlas_30_v3" + ] + }, + "name": "TPXO9-atlas-v3", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo9-atlas", + "scale": 0.0001, + "type": [ + "u", + "v" + ], + "version": "v3" + }, + "TPXO9-atlas-v3-nc": { + "format": "ATLAS-netcdf", + "grid_file": "TPXO9_atlas_v3/grid_tpxo9_atlas_30_v3.nc", + "model_file": { + "u": [ + "TPXO9_atlas_v3/u_q1_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/u_o1_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/u_p1_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/u_k1_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/u_n2_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/u_m2_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/u_s2_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/u_k2_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/u_m4_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/u_ms4_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/u_mn4_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/u_2n2_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/u_mf_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/u_mm_tpxo9_atlas_30_v3.nc" + ], + "v": [ + "TPXO9_atlas_v3/v_q1_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/v_o1_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/v_p1_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/v_k1_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/v_n2_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/v_m2_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/v_s2_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/v_k2_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/v_m4_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/v_ms4_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/v_mn4_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/v_2n2_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/v_mf_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/v_mm_tpxo9_atlas_30_v3.nc" + ] + }, + "name": "TPXO9-atlas-v3", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo9-atlas", + "scale": 0.0001, + "type": [ + "u", + "v" + ], + "version": "v3" + }, + "TPXO9-atlas-v4": { + "format": "OTIS", + "grid_file": "TPXO9_atlas_v4/grid_tpxo9_atlas_30_v4", + "model_file": { + "u": [ + "TPXO9_atlas_v4/u_q1_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/u_o1_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/u_p1_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/u_k1_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/u_n2_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/u_m2_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/u_s2_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/u_k2_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/u_m4_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/u_ms4_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/u_mn4_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/u_2n2_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/u_mf_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/u_mm_tpxo9_atlas_30_v4" + ] + }, + "name": "TPXO9-atlas-v4", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo9-atlas", + "scale": 0.0001, + "type": [ + "u", + "v" + ], + "version": "v4" + }, + "TPXO9-atlas-v4-nc": { + "format": "ATLAS-netcdf", + "grid_file": "TPXO9_atlas_v4/grid_tpxo9_atlas_30_v4.nc", + "model_file": { + "u": [ + "TPXO9_atlas_v4/u_q1_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/u_o1_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/u_p1_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/u_k1_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/u_n2_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/u_m2_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/u_s2_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/u_k2_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/u_m4_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/u_ms4_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/u_mn4_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/u_2n2_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/u_mf_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/u_mm_tpxo9_atlas_30_v4.nc" + ], + "v": [ + "TPXO9_atlas_v4/v_q1_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/v_o1_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/v_p1_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/v_k1_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/v_n2_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/v_m2_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/v_s2_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/v_k2_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/v_m4_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/v_ms4_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/v_mn4_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/v_2n2_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/v_mf_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/v_mm_tpxo9_atlas_30_v4.nc" + ] + }, + "name": "TPXO9-atlas-v4", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo9-atlas", + "scale": 0.0001, + "type": [ + "u", + "v" + ], + "version": "v4" + }, + "TPXO9-atlas-v5": { + "format": "OTIS", + "grid_file": "TPXO9_atlas_v5/grid_tpxo9_atlas_30_v5", + "model_file": { + "u": [ + "TPXO9_atlas_v5/u_q1_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/u_o1_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/u_p1_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/u_k1_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/u_n2_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/u_m2_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/u_s1_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/u_s2_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/u_k2_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/u_m4_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/u_ms4_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/u_mn4_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/u_2n2_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/u_mf_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/u_mm_tpxo9_atlas_30_v5" + ] + }, + "name": "TPXO9-atlas-v5", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo9-atlas", + "scale": 0.0001, + "type": [ + "u", + "v" + ], + "version": "v5" + }, + "TPXO9-atlas-v5-nc": { + "format": "ATLAS-netcdf", + "grid_file": "TPXO9_atlas_v5/grid_tpxo9_atlas_30_v5.nc", + "model_file": { + "u": [ + "TPXO9_atlas_v5/u_q1_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_o1_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_p1_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_k1_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_n2_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_m2_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_s1_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_s2_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_k2_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_m4_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_ms4_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_mn4_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_2n2_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_mf_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_mm_tpxo9_atlas_30_v5.nc" + ], + "v": [ + "TPXO9_atlas_v5/u_q1_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_o1_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_p1_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_k1_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_n2_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_m2_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_s1_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_s2_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_k2_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_m4_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_ms4_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_mn4_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_2n2_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_mf_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/u_mm_tpxo9_atlas_30_v5.nc" + ] + }, + "name": "TPXO9-atlas-v5", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo9-atlas", + "scale": 0.0001, + "type": [ + "u", + "v" + ], + "version": "v5" + }, + "TPXO9.1": { + "format": "OTIS", + "grid_file": "TPXO9.1/grid_tpxo9", + "model_file": { + "u": "TPXO9.1/u_tpxo9.v1" + }, + "name": "TPXO9.1", + "projection": "4326", + "reference": "http://volkov.oce.orst.edu/tides/global.html", + "type": [ + "u", + "v" + ], + "version": "9.1" + } + }, + "elevation": { + "TPXO10-atlas-v2": { + "format": "OTIS", + "grid_file": "TPXO10_atlas_v2/grid_tpxo10atlas_v2", + "model_file": [ + "TPXO10_atlas_v2/h_2n2_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/h_k1_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/h_k2_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/h_m2_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/h_m4_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/h_mf_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/h_mm_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/h_mn4_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/h_ms4_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/h_n2_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/h_o1_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/h_p1_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/h_q1_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/h_s1_tpxo10_atlas_30_v2", + "TPXO10_atlas_v2/h_s2_tpxo10_atlas_30_v2" + ], + "name": "TPXO10-atlas-v2", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo10-atlas", + "scale": 0.001, + "type": "z", + "variable": "tide_ocean", + "version": "v2" + }, + "TPXO10-atlas-v2-nc": { + "format": "ATLAS-netcdf", + "grid_file": "TPXO10_atlas_v2/grid_tpxo10atlas_v2.nc", + "model_file": [ + "TPXO10_atlas_v2/h_2n2_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/h_k1_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/h_k2_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/h_m2_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/h_m4_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/h_mf_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/h_mm_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/h_mn4_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/h_ms4_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/h_n2_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/h_o1_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/h_p1_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/h_q1_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/h_s1_tpxo10_atlas_30_v2.nc", + "TPXO10_atlas_v2/h_s2_tpxo10_atlas_30_v2.nc" + ], + "name": "TPXO10-atlas-v2", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo10-atlas", + "scale": 0.001, + "type": "z", + "variable": "tide_ocean", + "version": "v2" + }, + "TPXO7.2": { + "format": "OTIS", + "grid_file": "TPXO7.2_tmd/grid_tpxo7.2", + "model_file": "TPXO7.2_tmd/h_tpxo7.2", + "name": "TPXO7.2", + "projection": "4326", + "reference": "http://volkov.oce.orst.edu/tides/global.html", + "type": "z", + "variable": "tide_ocean", + "version": "7.2" + }, + "TPXO7.2_load": { + "format": "OTIS", + "grid_file": "TPXO7.2_load/grid_tpxo6.2", + "model_file": "TPXO7.2_load/h_tpxo7.2_load", + "name": "TPXO7.2_load", + "projection": "4326", + "reference": "http://volkov.oce.orst.edu/tides/global.html", + "type": "z", + "variable": "tide_load", + "version": "7.2" + }, + "TPXO8-atlas": { + "format": "ATLAS-compact", + "grid_file": "tpxo8_atlas/grid_tpxo8atlas_30_v1", + "model_file": "tpxo8_atlas/hf.tpxo8_atlas_30_v1", + "name": "TPXO8-atlas", + "projection": "4326", + "reference": "http://volkov.oce.orst.edu/tides/tpxo8_atlas.html", + "type": "z", + "variable": "tide_ocean", + "version": "8" + }, + "TPXO9-atlas": { + "format": "OTIS", + "grid_file": "TPXO9_atlas/grid_tpxo9_atlas", + "model_file": [ + "TPXO9_atlas/h_q1_tpxo9_atlas_30", + "TPXO9_atlas/h_o1_tpxo9_atlas_30", + "TPXO9_atlas/h_p1_tpxo9_atlas_30", + "TPXO9_atlas/h_k1_tpxo9_atlas_30", + "TPXO9_atlas/h_n2_tpxo9_atlas_30", + "TPXO9_atlas/h_m2_tpxo9_atlas_30", + "TPXO9_atlas/h_s2_tpxo9_atlas_30", + "TPXO9_atlas/h_k2_tpxo9_atlas_30", + "TPXO9_atlas/h_m4_tpxo9_atlas_30", + "TPXO9_atlas/h_ms4_tpxo9_atlas_30", + "TPXO9_atlas/h_mn4_tpxo9_atlas_30", + "TPXO9_atlas/h_2n2_tpxo9_atlas_30" + ], + "name": "TPXO9-atlas", + "projection": "4326", + "reference": "http://volkov.oce.orst.edu/tides/tpxo9_atlas.html", + "scale": 0.001, + "type": "z", + "variable": "tide_ocean", + "version": "v1" + }, + "TPXO9-atlas-nc": { + "format": "ATLAS-netcdf", + "grid_file": "TPXO9_atlas/grid_tpxo9_atlas.nc", + "model_file": [ + "TPXO9_atlas/h_q1_tpxo9_atlas_30.nc", + "TPXO9_atlas/h_o1_tpxo9_atlas_30.nc", + "TPXO9_atlas/h_p1_tpxo9_atlas_30.nc", + "TPXO9_atlas/h_k1_tpxo9_atlas_30.nc", + "TPXO9_atlas/h_n2_tpxo9_atlas_30.nc", + "TPXO9_atlas/h_m2_tpxo9_atlas_30.nc", + "TPXO9_atlas/h_s2_tpxo9_atlas_30.nc", + "TPXO9_atlas/h_k2_tpxo9_atlas_30.nc", + "TPXO9_atlas/h_m4_tpxo9_atlas_30.nc", + "TPXO9_atlas/h_ms4_tpxo9_atlas_30.nc", + "TPXO9_atlas/h_mn4_tpxo9_atlas_30.nc", + "TPXO9_atlas/h_2n2_tpxo9_atlas_30.nc" + ], + "name": "TPXO9-atlas", + "projection": "4326", + "reference": "http://volkov.oce.orst.edu/tides/tpxo9_atlas.html", + "scale": 0.001, + "type": "z", + "variable": "tide_ocean", + "version": "v1" + }, + "TPXO9-atlas-v2": { + "format": "OTIS", + "grid_file": "TPXO9_atlas_v2/grid_tpxo9_atlas_30_v2", + "model_file": [ + "TPXO9_atlas_v2/h_q1_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/h_o1_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/h_p1_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/h_k1_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/h_n2_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/h_m2_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/h_s2_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/h_k2_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/h_m4_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/h_ms4_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/h_mn4_tpxo9_atlas_30_v2", + "TPXO9_atlas_v2/h_2n2_tpxo9_atlas_30_v2" + ], + "name": "TPXO9-atlas-v2", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo9-atlas", + "scale": 0.001, + "type": "z", + "variable": "tide_ocean", + "version": "v2" + }, + "TPXO9-atlas-v2-nc": { + "format": "ATLAS-netcdf", + "grid_file": "TPXO9_atlas_v2/grid_tpxo9_atlas_30_v2.nc", + "model_file": [ + "TPXO9_atlas_v2/h_q1_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/h_o1_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/h_p1_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/h_k1_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/h_n2_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/h_m2_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/h_s2_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/h_k2_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/h_m4_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/h_ms4_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/h_mn4_tpxo9_atlas_30_v2.nc", + "TPXO9_atlas_v2/h_2n2_tpxo9_atlas_30_v2.nc" + ], + "name": "TPXO9-atlas-v2", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo9-atlas", + "scale": 0.001, + "type": "z", + "variable": "tide_ocean", + "version": "v2" + }, + "TPXO9-atlas-v3": { + "format": "OTIS", + "grid_file": "TPXO9_atlas_v3/grid_tpxo9_atlas_30_v3", + "model_file": [ + "TPXO9_atlas_v3/h_q1_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/h_o1_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/h_p1_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/h_k1_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/h_n2_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/h_m2_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/h_s2_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/h_k2_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/h_m4_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/h_ms4_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/h_mn4_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/h_2n2_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/h_mf_tpxo9_atlas_30_v3", + "TPXO9_atlas_v3/h_mm_tpxo9_atlas_30_v3" + ], + "name": "TPXO9-atlas-v3", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo9-atlas", + "scale": 0.001, + "type": "z", + "variable": "tide_ocean", + "version": "v3" + }, + "TPXO9-atlas-v3-nc": { + "format": "ATLAS-netcdf", + "grid_file": "TPXO9_atlas_v3/grid_tpxo9_atlas_30_v3.nc", + "model_file": [ + "TPXO9_atlas_v3/h_q1_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/h_o1_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/h_p1_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/h_k1_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/h_n2_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/h_m2_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/h_s2_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/h_k2_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/h_m4_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/h_ms4_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/h_mn4_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/h_2n2_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/h_mf_tpxo9_atlas_30_v3.nc", + "TPXO9_atlas_v3/h_mm_tpxo9_atlas_30_v3.nc" + ], + "name": "TPXO9-atlas-v3", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo9-atlas", + "scale": 0.001, + "type": "z", + "variable": "tide_ocean", + "version": "v3" + }, + "TPXO9-atlas-v4": { + "format": "OTIS", + "grid_file": "TPXO9_atlas_v4/grid_tpxo9_atlas_30_v4", + "model_file": [ + "TPXO9_atlas_v4/h_q1_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/h_o1_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/h_p1_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/h_k1_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/h_n2_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/h_m2_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/h_s2_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/h_k2_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/h_m4_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/h_ms4_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/h_mn4_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/h_2n2_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/h_mf_tpxo9_atlas_30_v4", + "TPXO9_atlas_v4/h_mm_tpxo9_atlas_30_v4" + ], + "name": "TPXO9-atlas-v4", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo9-atlas", + "scale": 0.001, + "type": "z", + "variable": "tide_ocean", + "version": "v4" + }, + "TPXO9-atlas-v4-nc": { + "format": "ATLAS-netcdf", + "grid_file": "TPXO9_atlas_v4/grid_tpxo9_atlas_30_v4.nc", + "model_file": [ + "TPXO9_atlas_v4/h_q1_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/h_o1_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/h_p1_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/h_k1_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/h_n2_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/h_m2_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/h_s2_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/h_k2_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/h_m4_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/h_ms4_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/h_mn4_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/h_2n2_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/h_mf_tpxo9_atlas_30_v4.nc", + "TPXO9_atlas_v4/h_mm_tpxo9_atlas_30_v4.nc" + ], + "name": "TPXO9-atlas-v4", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo9-atlas", + "scale": 0.001, + "type": "z", + "variable": "tide_ocean", + "version": "v4" + }, + "TPXO9-atlas-v5": { + "format": "OTIS", + "grid_file": "TPXO9_atlas_v5/grid_tpxo9_atlas_30_v5", + "model_file": [ + "TPXO9_atlas_v5/h_q1_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/h_o1_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/h_p1_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/h_k1_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/h_n2_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/h_m2_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/h_s1_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/h_s2_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/h_k2_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/h_m4_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/h_ms4_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/h_mn4_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/h_2n2_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/h_mf_tpxo9_atlas_30_v5", + "TPXO9_atlas_v5/h_mm_tpxo9_atlas_30_v5" + ], + "name": "TPXO9-atlas-v5", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo9-atlas", + "scale": 0.001, + "type": "z", + "variable": "tide_ocean", + "version": "v5" + }, + "TPXO9-atlas-v5-nc": { + "format": "ATLAS-netcdf", + "grid_file": "TPXO9_atlas_v5/grid_tpxo9_atlas_30_v5.nc", + "model_file": [ + "TPXO9_atlas_v5/h_q1_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/h_o1_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/h_p1_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/h_k1_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/h_n2_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/h_m2_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/h_s1_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/h_s2_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/h_k2_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/h_m4_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/h_ms4_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/h_mn4_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/h_2n2_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/h_mf_tpxo9_atlas_30_v5.nc", + "TPXO9_atlas_v5/h_mm_tpxo9_atlas_30_v5.nc" + ], + "name": "TPXO9-atlas-v5", + "projection": "4326", + "reference": "https://www.tpxo.net/global/tpxo9-atlas", + "scale": 0.001, + "type": "z", + "variable": "tide_ocean", + "version": "v5" + }, + "TPXO9.1": { + "format": "OTIS", + "grid_file": "TPXO9.1/DATA/grid_tpxo9", + "model_file": "TPXO9.1/DATA/h_tpxo9.v1", + "name": "TPXO9.1", + "projection": "4326", + "reference": "http://volkov.oce.orst.edu/tides/global.html", + "type": "z", + "variable": "tide_ocean", + "version": "9.1" + } + } +} \ No newline at end of file diff --git a/test/model_to_json.py b/providers/_model_to_database.py similarity index 88% rename from test/model_to_json.py rename to providers/_model_to_database.py index 87268cf2..fb7940db 100644 --- a/test/model_to_json.py +++ b/providers/_model_to_database.py @@ -1,23 +1,18 @@ """ -model_to_json.py (08/2024) -Converts model definitions to a json file +_model_to_json.py (08/2024) +Converts model definitions to a JSON file """ -import re import copy import json -import inspect import pathlib import argparse import pyTMD.io - -# current file path -filename = inspect.getframeinfo(inspect.currentframe()).filename -filepath = pathlib.Path(filename).absolute().parents[1] +import pyTMD.utilities # PURPOSE: create argument parser def arguments(): parser = argparse.ArgumentParser( - description="""Converts model definitions to a json file" + description="""Converts model definitions to a JSON file" """, fromfile_prefix_chars="@" ) @@ -83,7 +78,7 @@ def main(): d['current'][f'{n}-nc'] = serialize(m.to_dict()) # writing model parameters to JSON database file - json_file = filepath.joinpath('pyTMD','data','database.json') + json_file = pyTMD.utilities.get_data_path(['data','database.json']) print(f'\t{json_file}') if args.verbose else None with open(json_file, 'w') as fid: indent = 4 if args.pretty else None diff --git a/providers/_providers_to_database.py b/providers/_providers_to_database.py new file mode 100644 index 00000000..d3306767 --- /dev/null +++ b/providers/_providers_to_database.py @@ -0,0 +1,57 @@ +""" +_providers_to_database.py (08/2024) +Compress providers to a single JSON database +""" +import re +import copy +import json +import inspect +import pathlib +import argparse +import pyTMD.utilities + +# current file path +filename = inspect.getframeinfo(inspect.currentframe()).filename +filepath = pathlib.Path(filename).absolute().parent + +# PURPOSE: create argument parser +def arguments(): + parser = argparse.ArgumentParser( + description="""Compress providers to a single JSON database" + """, + fromfile_prefix_chars="@" + ) + # command line parameters + parser.add_argument('--pretty', '-p', + action='store_true', + help='Pretty print the json file') + parser.add_argument('--verbose', '-v', + action='store_true', + help='Verbose output') + return parser + +def main(): + # Read the system arguments listed after the program + parser = arguments() + args,_ = parser.parse_known_args() + + # output dictionary + d = dict(elevation={}, current={}) + # find providers + providers = [f for f in filepath.iterdir() if (f.suffix == '.json')] + # for each provider + for provider in providers: + with provider.open('r', encoding='utf-8') as fid: + p = json.load(fid) + for key, val in p.items(): + d[key].update(val) + + # writing model parameters to JSON database file + json_file = pyTMD.utilities.get_data_path(['data','database.json']) + print(f'\t{json_file}') if args.verbose else None + with open(json_file, 'w') as fid: + indent = 4 if args.pretty else None + json.dump(d, fid, indent=indent, sort_keys=True) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/providers/providers.json b/providers/providers.json new file mode 100644 index 00000000..9c7eb02b --- /dev/null +++ b/providers/providers.json @@ -0,0 +1,200 @@ +{ + "current": { + "CATS2008-v2023": { + "format": "TMD3", + "grid_file": "CATS2008_v2023/CATS2008_v2023.nc", + "model_file": { + "u": "CATS2008_v2023/CATS2008_v2023.nc" + }, + "name": "CATS2008-v2023", + "projection": "CATS2008", + "type": [ + "u", + "v" + ] + }, + "HAMTIDE11": { + "constituents": [ + "2n2", + "k1", + "k2", + "m2", + "n2", + "o1", + "p1", + "q1", + "s2" + ], + "format": "FES-netcdf", + "model_file": { + "u": [ + "hamtide/HAMcurrent11a_2n.nc", + "hamtide/HAMcurrent11a_k1.nc", + "hamtide/HAMcurrent11a_k2.nc", + "hamtide/HAMcurrent11a_m2.nc", + "hamtide/HAMcurrent11a_n2.nc", + "hamtide/HAMcurrent11a_o1.nc", + "hamtide/HAMcurrent11a_p1.nc", + "hamtide/HAMcurrent11a_q1.nc", + "hamtide/HAMcurrent11a_s2.nc" + ], + "v": [ + "hamtide/HAMcurrent11a_2n.nc", + "hamtide/HAMcurrent11a_k1.nc", + "hamtide/HAMcurrent11a_k2.nc", + "hamtide/HAMcurrent11a_m2.nc", + "hamtide/HAMcurrent11a_n2.nc", + "hamtide/HAMcurrent11a_o1.nc", + "hamtide/HAMcurrent11a_p1.nc", + "hamtide/HAMcurrent11a_q1.nc", + "hamtide/HAMcurrent11a_s2.nc" + ] + }, + "name": "HAMTIDE11", + "reference": "https://doi.org/10.1002/2013JC009766", + "scale": 1.0, + "type": [ + "u", + "v" + ], + "version": "HAMTIDE11" + } + }, + "elevation": { + "CATS2008-v2023": { + "format": "TMD3", + "grid_file": "CATS2008_v2023/CATS2008_v2023.nc", + "model_file": "CATS2008_v2023/CATS2008_v2023.nc", + "name": "CATS2008-v2023", + "projection": "CATS2008", + "reference": "https://www.esr.org/research/polar-tide-models/list-of-polar-tide-models/cats2008/", + "type": "z", + "variable": "tide_ocean" + }, + "EOT20": { + "constituents": [ + "2n2", + "j1", + "k1", + "k2", + "m2", + "m4", + "mf", + "mm", + "n2", + "o1", + "p1", + "q1", + "s1", + "s2", + "sa", + "ssa", + "t2" + ], + "format": "FES-netcdf", + "model_file": [ + "EOT20/ocean_tides/2N2_ocean_eot20.nc", + "EOT20/ocean_tides/J1_ocean_eot20.nc", + "EOT20/ocean_tides/K1_ocean_eot20.nc", + "EOT20/ocean_tides/K2_ocean_eot20.nc", + "EOT20/ocean_tides/M2_ocean_eot20.nc", + "EOT20/ocean_tides/M4_ocean_eot20.nc", + "EOT20/ocean_tides/MF_ocean_eot20.nc", + "EOT20/ocean_tides/MM_ocean_eot20.nc", + "EOT20/ocean_tides/N2_ocean_eot20.nc", + "EOT20/ocean_tides/O1_ocean_eot20.nc", + "EOT20/ocean_tides/P1_ocean_eot20.nc", + "EOT20/ocean_tides/Q1_ocean_eot20.nc", + "EOT20/ocean_tides/S1_ocean_eot20.nc", + "EOT20/ocean_tides/S2_ocean_eot20.nc", + "EOT20/ocean_tides/SA_ocean_eot20.nc", + "EOT20/ocean_tides/SSA_ocean_eot20.nc", + "EOT20/ocean_tides/T2_ocean_eot20.nc" + ], + "name": "EOT20", + "reference": "https://doi.org/10.17882/79489", + "scale": 0.01, + "type": "z", + "variable": "tide_ocean", + "version": "EOT20" + }, + "EOT20_load": { + "constituents": [ + "2n2", + "j1", + "k1", + "k2", + "m2", + "m4", + "mf", + "mm", + "n2", + "o1", + "p1", + "q1", + "s1", + "s2", + "sa", + "ssa", + "t2" + ], + "format": "FES-netcdf", + "model_file": [ + "EOT20/load_tides/2N2_load_eot20.nc", + "EOT20/load_tides/J1_load_eot20.nc", + "EOT20/load_tides/K1_load_eot20.nc", + "EOT20/load_tides/K2_load_eot20.nc", + "EOT20/load_tides/M2_load_eot20.nc", + "EOT20/load_tides/M4_load_eot20.nc", + "EOT20/load_tides/MF_load_eot20.nc", + "EOT20/load_tides/MM_load_eot20.nc", + "EOT20/load_tides/N2_load_eot20.nc", + "EOT20/load_tides/O1_load_eot20.nc", + "EOT20/load_tides/P1_load_eot20.nc", + "EOT20/load_tides/Q1_load_eot20.nc", + "EOT20/load_tides/S1_load_eot20.nc", + "EOT20/load_tides/S2_load_eot20.nc", + "EOT20/load_tides/SA_load_eot20.nc", + "EOT20/load_tides/SSA_load_eot20.nc", + "EOT20/load_tides/T2_load_eot20.nc" + ], + "name": "EOT20_load", + "reference": "https://doi.org/10.17882/79489", + "scale": 0.01, + "type": "z", + "variable": "tide_load", + "version": "EOT20" + }, + "HAMTIDE11": { + "constituents": [ + "2n2", + "k1", + "k2", + "m2", + "n2", + "o1", + "p1", + "q1", + "s2" + ], + "format": "FES-netcdf", + "model_file": [ + "hamtide/2n.hamtide11a.nc", + "hamtide/k1.hamtide11a.nc", + "hamtide/k2.hamtide11a.nc", + "hamtide/m2.hamtide11a.nc", + "hamtide/n2.hamtide11a.nc", + "hamtide/o1.hamtide11a.nc", + "hamtide/p1.hamtide11a.nc", + "hamtide/q1.hamtide11a.nc", + "hamtide/s2.hamtide11a.nc" + ], + "name": "HAMTIDE11", + "reference": "https://doi.org/10.1002/2013JC009766", + "scale": 0.01, + "type": "z", + "variable": "tide_ocean", + "version": "HAMTIDE11" + } + } +} \ No newline at end of file diff --git a/pyTMD/check_points.py b/pyTMD/check_points.py index 8d3a3aa5..96bbcf39 100644 --- a/pyTMD/check_points.py +++ b/pyTMD/check_points.py @@ -52,6 +52,7 @@ UPDATE HISTORY: Updated 09/2024: use JSON database for known model parameters + drop support for the ascii definition file format Updated 07/2024: renamed format for ATLAS to ATLAS-compact renamed format for netcdf to ATLAS-netcdf renamed format for FES to FES-netcdf and added FES-ascii @@ -136,8 +137,7 @@ def check_points(x: np.ndarray, y: np.ndarray, # get parameters for tide model if DEFINITION_FILE is not None: - model = pyTMD.io.model(DIRECTORY).from_file( - pathlib.Path(DEFINITION_FILE).expanduser()) + model = pyTMD.io.model(DIRECTORY).from_file(DEFINITION_FILE) else: model = pyTMD.io.model(DIRECTORY, compressed=GZIP).elevation(MODEL) diff --git a/pyTMD/compute.py b/pyTMD/compute.py index f38d6260..8df1f2ba 100644 --- a/pyTMD/compute.py +++ b/pyTMD/compute.py @@ -61,6 +61,7 @@ UPDATE HISTORY: Updated 09/2024: use JSON database for known model parameters + drop support for the ascii definition file format Updated 08/2024: allow inferring only specific minor constituents use prediction functions for pole tides in cartesian coordinates use rotation matrix to convert from cartesian to spherical @@ -196,7 +197,6 @@ def tide_elevations( MODEL: str | None = None, GZIP: bool = False, DEFINITION_FILE: str | pathlib.Path | IOBase | None = None, - DEFINITION_FORMAT: str = 'auto', CROP: bool = False, BOUNDS: list | np.ndarray | None = None, EPSG: str | int = 3031, @@ -232,12 +232,6 @@ def tide_elevations( Tide model files are gzip compressed DEFINITION_FILE: str, pathlib.Path, io.IOBase or NoneType, default None Tide model definition file for use - DEFINITION_FORMAT: str, default 'auto' - Format for model definition file - - - ``'ascii'``: tab-delimited definition file - - ``'json'``: JSON formatted definition file - - ``'auto'``: auto-detect the definition file format CROP: bool, default False Crop tide model data to (buffered) bounds BOUNDS: list, np.ndarray or NoneType, default None @@ -303,8 +297,7 @@ def tide_elevations( # get parameters for tide model if DEFINITION_FILE is not None: - model = pyTMD.io.model(DIRECTORY).from_file(DEFINITION_FILE, - format=DEFINITION_FORMAT) + model = pyTMD.io.model(DIRECTORY).from_file(DEFINITION_FILE) else: model = pyTMD.io.model(DIRECTORY, compressed=GZIP).elevation(MODEL) @@ -440,7 +433,6 @@ def tide_currents( MODEL: str | None = None, GZIP: bool = False, DEFINITION_FILE: str | pathlib.Path | IOBase | None = None, - DEFINITION_FORMAT: str = 'ascii', CROP: bool = False, BOUNDS: list | np.ndarray | None = None, EPSG: str | int = 3031, @@ -475,12 +467,6 @@ def tide_currents( Tide model files are gzip compressed DEFINITION_FILE: str, pathlib.Path, io.IOBase or NoneType, default None Tide model definition file for use - DEFINITION_FORMAT: str, default 'auto' - Format for model definition file - - - ``'ascii'``: tab-delimited definition file - - ``'json'``: JSON formatted definition file - - ``'auto'``: auto-detect the definition file format CROP: bool, default False Crop tide model data to (buffered) bounds BOUNDS: list, np.ndarray or NoneType, default None @@ -542,8 +528,7 @@ def tide_currents( # get parameters for tide model if DEFINITION_FILE is not None: - model = pyTMD.io.model(DIRECTORY).from_file(DEFINITION_FILE, - format=DEFINITION_FORMAT) + model = pyTMD.io.model(DIRECTORY).from_file(DEFINITION_FILE) else: model = pyTMD.io.model(DIRECTORY, compressed=GZIP).current(MODEL) diff --git a/pyTMD/io/__init__.py b/pyTMD/io/__init__.py index 2f792cb1..ea64b9a0 100644 --- a/pyTMD/io/__init__.py +++ b/pyTMD/io/__init__.py @@ -7,4 +7,4 @@ from .OTIS import * from .IERS import * from .constituents import constituents -from .model import model +from .model import model, load_database diff --git a/pyTMD/io/model.py b/pyTMD/io/model.py index 7d626227..7c35647f 100644 --- a/pyTMD/io/model.py +++ b/pyTMD/io/model.py @@ -7,6 +7,7 @@ UPDATE HISTORY: Updated 09/2024: use JSON database for known model parameters + drop support for the ascii definition file format Updated 08/2024: added attribute for minor constituents to infer allow searching over iterable glob strings in definition files added option to try automatic detection of definition file format @@ -61,6 +62,33 @@ from pyTMD.utilities import get_data_path from collections.abc import Iterable +# PURPOSE: load the JSON database of model files +def load_database(extra_databases: list = []) -> dict: + """ + Load the JSON database of model files + + Parameters + ---------- + extra_databases: list, default [] + Additional databases to load + + Returns + ------- + parameters: dict + Database of model parameters + """ + # path to model database + database = get_data_path(['data','database.json']) + # extract JSON data + with database.open(mode='r', encoding='utf-8') as fid: + parameters = json.load(fid) + # load any additional databases + for db in extra_databases: + with open(db, 'r', encoding='utf-8') as fid: + parameters.update(json.load(fid)) + # return parameters + return parameters + class model: """Retrieves tide model parameters for named models or from a model definition file for use in the pyTMD tide @@ -155,7 +183,7 @@ def __init__(self, directory: str | pathlib.Path | None = None, **kwargs): self.verify = copy.copy(kwargs['verify']) self.version = None - def elevation(self, m: str): + def elevation(self, m: str, extra_databases: list = []): """ Create a model object from known tidal elevation models @@ -163,28 +191,29 @@ def elevation(self, m: str): ---------- m: str model name + extra_databases: list, default [] + Additional databases to load """ # set working data directory if unset if self.directory is None: self.directory = pathlib.Path().absolute() # select between known tide models - parameters = self.load_database() + parameters = load_database(extra_databases=extra_databases) # try to extract parameters for model try: self.from_dict(parameters['elevation'][m]) except (ValueError, KeyError) as exc: raise ValueError(f"Unlisted tide model {m}") - # validate paths - # grid file for OTIS, ATLAS models + # validate paths: grid file for OTIS, ATLAS models if hasattr(self, 'grid_file') and getattr(self, 'grid_file'): self.grid_file = self.pathfinder(self.grid_file) - # model constituent files + # validate paths: model constituent files self.model_file = self.pathfinder(self.model_file) # return the model parameters self.validate_format() return self - def current(self, m: str): + def current(self, m: str, extra_databases: list = []): """ Create a model object from known tidal current models @@ -192,22 +221,23 @@ def current(self, m: str): ---------- m: str model name + extra_databases: list, default [] + Additional databases to load """ # set working data directory if unset if self.directory is None: self.directory = pathlib.Path().absolute() # select between tide models - parameters = self.load_database() + parameters = load_database(extra_databases=extra_databases) # try to extract parameters for model try: self.from_dict(parameters['current'][m]) except (ValueError, KeyError) as exc: raise ValueError(f"Unlisted tide model {m}") - # validate paths - # grid file for OTIS, ATLAS models + # validate paths: grid file for OTIS, ATLAS models if hasattr(self, 'grid_file') and getattr(self, 'grid_file'): self.grid_file = self.pathfinder(self.grid_file) - # model constituent files + # validate paths: model constituent files for key, val in self.model_file.items(): self.model_file[key] = self.pathfinder(val) # return the model parameters @@ -334,173 +364,149 @@ def formats() -> list: """ Returns list of known model formats """ - return ['OTIS','ATLAS-compact','TMD3','ATLAS-netcdf', - 'GOT-ascii','GOT-netcdf','FES-ascii','FES-netcdf'] - - @staticmethod - def global_ocean() -> list: - """ - Returns list of global ocean tide elevation models - """ - return ['TPXO9-atlas','TPXO9-atlas-v2','TPXO9-atlas-v3', - 'TPXO9-atlas-v4','TPXO9-atlas-v5','TPXO10-atlas-v2', - 'TPXO9-atlas-nc','TPXO9-atlas-v2-nc','TPXO9-atlas-v3-nc', - 'TPXO9-atlas-v4-nc','TPXO9-atlas-v5-nc','TPXO10-atlas-v2-nc', - 'TPXO9.1','TPXO8-atlas','TPXO7.2', - 'GOT4.7','GOT4.8','GOT4.10','GOT5.5', - 'FES2014','FES2022','EOT20','HAMTIDE11'] - - @staticmethod - def global_load() -> list: - """ - Returns list of global load tide elevation models - """ - return ['TPXO7.2_load','GOT4.7_load','GOT4.8_load','GOT5.5_load', - 'GOT4.10_load','FES2014_load','FES2022_load','EOT20_load'] - - @staticmethod - def global_current() -> list: - """ - Returns list of global tidal current models - """ - return ['TPXO9-atlas','TPXO9-atlas-v2', 'TPXO9-atlas-v3', - 'TPXO9-atlas-v4','TPXO9-atlas-v5','TPXO10-atlas-v2', - 'TPXO9-atlas-nc','TPXO9-atlas-v2-nc', 'TPXO9-atlas-v3-nc', - 'TPXO9-atlas-v4-nc','TPXO9-atlas-v5-nc','TPXO10-atlas-v2-nc', - 'TPXO9.1','TPXO8-atlas','TPXO7.2', - 'FES2014','HAMTIDE11'] - - @staticmethod - def antarctic_ocean() -> list: - """ - Returns list of Antarctic ocean tide elevation models - """ - return ['CATS0201','CATS2008','CATS2008-v2023'] - - @staticmethod - def antarctic_load() -> list: - """ - Returns list of Antarctic load tide elevation models - """ - return ['CATS2008_load'] - - @staticmethod - def antarctic_current() -> list: - """ - Returns list of Antarctic tidal current models - """ - return ['CATS0201','CATS2008','CATS2008-v2023'] - - @staticmethod - def arctic_ocean() -> list: - """ - Returns list of Arctic ocean tide elevation models - """ - return ['AODTM-5','AOTIM-5','AOTIM-5-2018','Arc2kmTM', - 'Gr1kmTM','Gr1km-v2'] - - @staticmethod - def arctic_load() -> list: - """ - Returns list of Arctic load tide elevation models - """ - return [] - - @staticmethod - def arctic_current() -> list: - """ - Returns list of Arctic tidal current models - """ - return ['AODTM-5','AOTIM-5','AOTIM-5-2018','Arc2kmTM', - 'Gr1kmTM','Gr1km-v2'] + # load the database of model parameters + parameters = load_database() + # extract all known formats + format_list = [] + for type, models in parameters.items(): + for model, val in models.items(): + format_list.append(val['format']) + # return unique list of formats + return sorted(set(format_list)) @staticmethod def ocean_elevation() -> list: """ Returns list of ocean tide elevation models """ - return ['CATS0201','CATS2008','CATS2008-v2023', - 'TPXO9-atlas','TPXO9-atlas-v2', 'TPXO9-atlas-v3', - 'TPXO9-atlas-v4','TPXO9-atlas-v5','TPXO10-atlas-v2', - 'TPXO9-atlas-nc','TPXO9-atlas-v2-nc', 'TPXO9-atlas-v3-nc', - 'TPXO9-atlas-v4-nc','TPXO9-atlas-v5-nc','TPXO10-atlas-v2-nc', - 'TPXO9.1','TPXO8-atlas','TPXO7.2', - 'AODTM-5','AOTIM-5','AOTIM-5-2018', - 'Arc2kmTM','Gr1kmTM','Gr1km-v2', - 'GOT4.7','GOT4.8','GOT4.10','GOT5.5', - 'FES2014','FES2022','EOT20','HAMTIDE11'] + # load the database of model parameters + parameters = load_database() + # extract all known ocean tide elevation models + model_list = [] + for model, val in parameters['elevation'].items(): + if (val['type'] == 'z') and (val['variable'] == 'tide_ocean'): + model_list.append(model) + # return unique list of models + return sorted(set(model_list)) @staticmethod def load_elevation() -> list: """ Returns list of load tide elevation models """ - return ['CATS2008_load','TPXO7.2_load','GOT4.7_load', - 'GOT4.8_load','GOT4.10_load','GOT5.5_load', - 'FES2014_load','FES2022_load','EOT20_load'] + # load the database of model parameters + parameters = load_database() + # extract all known load tide elevation models + model_list = [] + for model, val in parameters['elevation'].items(): + if (val['type'] == 'z') and (val['variable'] == 'tide_load'): + model_list.append(model) + # return unique list of models + return sorted(set(model_list)) @staticmethod def ocean_current() -> list: """ Returns list of tidal current models """ - return ['CATS0201','CATS2008','CATS2008-v2023', - 'TPXO9-atlas','TPXO9-atlas-v2', 'TPXO9-atlas-v3', - 'TPXO9-atlas-v4','TPXO9-atlas-v5','TPXO10-atlas-v2', - 'TPXO9-atlas-nc','TPXO9-atlas-v2-nc', 'TPXO9-atlas-v3-nc', - 'TPXO9-atlas-v4-nc','TPXO9-atlas-v5-nc','TPXO10-atlas-v2-nc', - 'TPXO9.1','TPXO8-atlas','TPXO7.2', - 'AODTM-5','AOTIM-5','AOTIM-5-2018', - 'Arc2kmTM','Gr1kmTM','Gr1km-v2','FES2014','HAMTIDE11'] + # load the database of model parameters + parameters = load_database() + # extract all known ocean tide current models + model_list = [] + for model, val in parameters['current'].items(): + model_list.append(model) + # return unique list of models + return sorted(set(model_list)) @staticmethod def OTIS() -> list: """ Returns list of OTIS format models """ - return ['CATS0201','CATS2008','CATS2008_load','TPXO9.1', - 'TPXO7.2','TPXO7.2_load','AODTM-5','AOTIM-5', - 'AOTIM-5-2018','Arc2kmTM','Gr1kmTM','Gr1km-v2', - 'TPXO9-atlas','TPXO9-atlas-v2', 'TPXO9-atlas-v3', - 'TPXO9-atlas-v4','TPXO9-atlas-v5','TPXO10-atlas-v2'] + # load the database of model parameters + parameters = load_database() + # extract all known OTIS models + model_list = [] + for model, val in parameters['elevation'].items(): + if (val['format'] == 'OTIS'): + model_list.append(model) + # return unique list of models + return sorted(set(model_list)) @staticmethod def ATLAS_compact() -> list: """ Returns list of ATLAS compact format models """ - return ['TPXO8-atlas'] + # load the database of model parameters + parameters = load_database() + # extract all known ATLAS-compact models + model_list = [] + for model, val in parameters['elevation'].items(): + if (val['format'] == 'ATLAS-compact'): + model_list.append(model) + # return unique list of models + return sorted(set(model_list)) @staticmethod def TMD3() -> list: """ Returns list of TMD3 format models """ - return ['CATS2008-v2023'] + # load the database of model parameters + parameters = load_database() + # extract all known TMD3 models + model_list = [] + for model, val in parameters['elevation'].items(): + if (val['format'] == 'TMD3'): + model_list.append(model) + # return unique list of models + return sorted(set(model_list)) @staticmethod def ATLAS() -> list: """ - Returns list of ATLAS format models + Returns list of ATLAS-netcdf format models """ - return ['TPXO9-atlas-nc','TPXO9-atlas-v2-nc', 'TPXO9-atlas-v3-nc', - 'TPXO9-atlas-v4-nc','TPXO9-atlas-v5-nc','TPXO10-atlas-v2-nc'] + # load the database of model parameters + parameters = load_database() + # extract all known TMD3 models + model_list = [] + for model, val in parameters['elevation'].items(): + if (val['format'] == 'ATLAS-netcdf'): + model_list.append(model) + # return unique list of models + return sorted(set(model_list)) @staticmethod def GOT() -> list: """ Returns list of GOT format models """ - return ['GOT4.7','GOT4.7_load','GOT4.8','GOT4.8_load', - 'GOT4.10','GOT4.10_load','GOT5.5','GOT5.5_load'] + # load the database of model parameters + parameters = load_database() + # extract all known GOT-ascii or GOT-netcdf models + model_list = [] + for model, val in parameters['elevation'].items(): + if (val['format'] == 'GOT-ascii') or (val['format'] == 'GOT-netcdf'): + model_list.append(model) + # return unique list of models + return sorted(set(model_list)) @staticmethod def FES() -> list: """ Returns list of FES format models """ - return ['FES2014','FES2014_load','FES2022','FES2022_load', - 'EOT20','EOT20_load','HAMTIDE11'] + # load the database of model parameters + parameters = load_database() + # extract all known FES-ascii or FES-netcdf models + model_list = [] + for model, val in parameters['elevation'].items(): + if (val['format'] == 'FES-ascii') or (val['format'] == 'FES-netcdf'): + model_list.append(model) + # return unique list of models + return sorted(set(model_list)) def pathfinder(self, model_file: str | pathlib.Path | list): """ @@ -530,7 +536,7 @@ def pathfinder(self, model_file: str | pathlib.Path | list): def from_file(self, definition_file: str | pathlib.Path | io.IOBase, - format: str = 'auto' + **kwargs, ): """ Create a model object from an input definition file @@ -539,25 +545,19 @@ def from_file(self, ---------- definition_file: str, pathlib.Path or io.IOBase model definition file for creating model object - format: str, default 'json' - format of the input definition file - - - ``'ascii'``: tab-delimited definition file - - ``'json'``: JSON formatted definition file - - ``'auto'``: attempt to auto-detect format """ + # set default keyword arguments + kwargs.setdefault('format', 'json') # Opening definition file and assigning file ID number if isinstance(definition_file, io.IOBase): fid = copy.copy(definition_file) else: definition_file = pathlib.Path(definition_file).expanduser() - fid = definition_file.open(mode="r", encoding='utf8') + fid = definition_file.open(mode='r', encoding='utf8') # load and parse definition file type - if (format.lower() == 'ascii'): - self._parse_file(fid) - elif (format.lower() == 'json'): - self._parse_json(fid) - elif (format.lower() == 'auto'): + if (kwargs['format'].lower() == 'ascii'): + raise ValueError('ascii definition format no longer supported') + else: self._parse_file(fid) # close the definition file fid.close() @@ -580,181 +580,9 @@ def _parse_file(self, fid: io.IOBase): pass else: return self - # rewind the definition file - fid.seek(0) - # attempt to read and parse an ascii file - try: - self._parse_ascii(fid) - except Exception as exc: - pass - else: - return self # raise an exception raise IOError('Cannot load model definition file') - def _parse_ascii(self, fid: io.IOBase): - """ - Load and parse tab-delimited definition file - - Parameters - ---------- - fid: io.IOBase - open definition file object - """ - # variable with parameter definitions - parameters = {} - # for each line in the file will extract the parameter (name and value) - for fileline in fid: - # Splitting the input line between parameter name and value - part = fileline.rstrip().split(maxsplit=1) - # filling the parameter definition variable - parameters[part[0]] = part[1] - # convert from dictionary to model variable - temp = self.from_dict(parameters) - # verify model name, format and type - assert temp.name - temp.validate_format() - assert temp.type - assert temp.model_file - # split type into list if currents (u,v) - if re.search(r'[\s\,]+', temp.type): - temp.type = re.split(r'[\s\,]+', temp.type) - # split constituents into list if delimited-string - if isinstance(temp.constituents, str) and \ - re.search(r'[\s\,]+', temp.constituents): - temp.constituents = re.split(r'[\s\,]+', temp.constituents) - # split model file into list if an ATLAS, GOT or FES file - # model files can be comma, tab or space delimited - # extract full path to tide model files - # extract full path to tide grid file - if temp.format in ('OTIS','ATLAS-compact','TMD3'): - assert temp.grid_file - # check if grid file is relative - if (temp.directory is not None): - temp.grid_file = temp.directory.joinpath(temp.grid_file).resolve() - else: - temp.grid_file = pathlib.Path(temp.grid_file).expanduser() - # check if model files are as a delimited string - multi_file = re.search(r'[\s\,]+', temp.model_file) - # extract model files - if (temp.type == ['u','v']) and multi_file: - model_file = [pathlib.Path(f).expanduser() for f in - re.split(r'[\s\,]+', temp.model_file)] - # copy to model file and directory dictionaries - temp.model_file = dict(u=model_file, v=model_file) - elif (temp.type == 'z') and multi_file: - temp.model_file = [pathlib.Path(f).expanduser() for f in - re.split(r'[\s\,]+', temp.model_file)] - elif (temp.type == ['u','v']) and (temp.directory is not None): - # use glob strings to find files in directory - glob_string = copy.copy(temp.model_file) - model_file = [] - # search singular glob string or iterable glob strings - for p in re.split(r'[\s\,]+', temp.model_file): - model_file.extend(temp.directory.glob(p)) - # update model file - temp.model_file = dict(u=model_file, v=model_file) - elif (temp.type == 'z') and (temp.directory is not None): - # use glob strings to find files in directory - glob_string = copy.copy(temp.model_file) - temp.model_file = [] - # search singular glob string or iterable glob strings - for p in re.split(r'[\s\,]+', glob_string): - temp.model_file.extend(temp.directory.glob(p)) - else: - # fully defined single file case - temp.model_file = pathlib.Path(temp.model_file).expanduser() - elif temp.format in ('ATLAS-netcdf',): - assert temp.grid_file - # check if grid file is relative - if (temp.directory is not None): - temp.grid_file = temp.directory.joinpath(temp.grid_file).resolve() - else: - temp.grid_file = pathlib.Path(temp.grid_file).expanduser() - # extract model files - if (temp.type == ['u','v']) and (temp.directory is not None): - # split model file string at semicolon - glob_string = temp.model_file.split(';') - # use glob strings to find files in directory - temp.model_file = {} - temp.model_file['u'] = [] - temp.model_file['v'] = [] - # search singular glob string or iterable glob strings - for p in re.split(r'[\s\,]+', glob_string[0]): - temp.model_file['u'].extend(temp.directory.glob(p)) - for p in re.split(r'[\s\,]+', glob_string[1]): - temp.model_file['v'].extend(temp.directory.glob(p)) - elif (temp.type == 'z') and (temp.directory is not None): - # use glob strings to find files in directory - glob_string = copy.copy(temp.model_file) - temp.model_file = [] - # search singular glob string or iterable glob strings - for p in re.split(r'[\s\,]+', glob_string): - temp.model_file.extend(temp.directory.glob(p)) - elif (temp.type == ['u','v']): - # split model file string at semicolon - model_file = temp.model_file.split(';') - # split model into list of files for each direction - temp.model_file = {} - temp.model_file['u'] = [pathlib.Path(f).expanduser() for f in - re.split(r'[\s\,]+', model_file[0])] - temp.model_file['v'] = [pathlib.Path(f).expanduser() for f in - re.split(r'[\s\,]+', model_file[1])] - elif (temp.type == 'z'): - temp.model_file = [pathlib.Path(f).expanduser() for f in - re.split(r'[\s\,]+', temp.model_file)] - elif temp.format in ('FES-ascii','FES-netcdf','GOT-ascii','GOT-netcdf'): - # extract model files - if (temp.type == ['u','v']) and (temp.directory is not None): - # split model file string at semicolon - glob_string = temp.model_file.split(';') - # use glob strings to find files in directory - temp.model_file = {} - temp.model_file['u'] = [] - temp.model_file['v'] = [] - # search singular glob string or iterable glob strings - for p in re.split(r'[\s\,]+', glob_string[0]): - temp.model_file['u'].extend(temp.directory.glob(p)) - for p in re.split(r'[\s\,]+', glob_string[1]): - temp.model_file['v'].extend(temp.directory.glob(p)) - elif (temp.type == 'z') and (temp.directory is not None): - # use glob strings to find files in directory - glob_string = copy.copy(temp.model_file) - temp.model_file = [] - # search singular glob string or iterable glob strings - for p in re.split(r'[\s\,]+', glob_string): - temp.model_file.extend(temp.directory.glob(p)) - elif (temp.type == ['u','v']): - # split model file string at semicolon - model_file = temp.model_file.split(';') - # split model into list of files for each direction - temp.model_file = {} - temp.model_file['u'] = [pathlib.Path(f).expanduser() for f in - re.split(r'[\s\,]+', model_file[0])] - temp.model_file['v'] = [pathlib.Path(f).expanduser() for f in - re.split(r'[\s\,]+', model_file[1])] - elif (temp.type == 'z'): - temp.model_file = [pathlib.Path(f).expanduser() for f in - re.split(r'[\s\,]+', temp.model_file)] - # verify that projection attribute exists for projected models - if temp.format in ('OTIS','ATLAS-compact','TMD3'): - assert temp.projection - # convert scale from string to float - if temp.format in ('ATLAS-netcdf','GOT-ascii','GOT-netcdf','FES-ascii','FES-netcdf'): - assert temp.scale - temp.scale = float(temp.scale) - # assert that FES model has a version - # get model constituents from constituent files - if temp.format in ('FES-ascii','FES-netcdf',): - assert temp.version - if (temp.constituents is None): - temp.parse_constituents() - # convert boolean strings - if isinstance(temp.compressed, str): - temp.compressed = self.to_bool(temp.compressed) - # return the model parameters - return temp - def _parse_json(self, fid: io.IOBase): """ Load and parse JSON definition file @@ -940,18 +768,53 @@ def from_dict(self, d: dict): # return the model parameters return self - def to_dict(self): + def to_dict(self, **kwargs): """ Create a python dictionary from a model object + + Parameters + ---------- + fields: list, default all + List of model attributes to output + serialize: bool, default False + Serialize dictionary for JSON output """ - # output dictionary - d = {} - # model keys to return + # default fields keys = ['name', 'format', 'type', 'grid_file', 'model_file', 'projection', 'variable', 'scale', 'constituents', 'version', 'reference'] - for key in keys: + # set default keyword arguments + kwargs.setdefault('fields', keys) + kwargs.setdefault('serialize', False) + # output dictionary + d = {} + # for each field + for key in kwargs['fields']: if hasattr(self, key) and getattr(self, key) is not None: d[key] = getattr(self, key) + # serialize dictionary for JSON output + if kwargs['serialize']: + d = self.serialize(d) + # return the model dictionary + return d + + def serialize(self, d: dict): + """ + Encodes dictionary to be JSON serializable + + Parameters + ---------- + d: dict + Python dictionary to serialize + """ + # iterate over keys + for key, val in d.items(): + val = copy.copy(d[key]) + if isinstance(val, pathlib.Path): + d[key] = str(val) + elif isinstance(val, (list, tuple)) and isinstance(val[0], pathlib.Path): + d[key] = [str(v) for v in val] + elif isinstance(val, dict): + d[key] = self.serialize(val) # return the model dictionary return d @@ -976,33 +839,6 @@ def parse_constituents(self) -> list: # return the model parameters return self - @staticmethod - def load_database(extra_databases: list = []) -> dict: - """ - Load the JSON database of model files - - Parameters - ---------- - extra_databases: list, default [] - list of additional databases to load - - Returns - ------- - parameters: dict - Database of model parameters - """ - # path to model database - database = get_data_path(['data','database.json']) - # extract JSON data - with open(database, 'r') as fid: - parameters = json.load(fid) - # load any additional databases - for db in extra_databases: - with open(db, 'r') as fid: - parameters.update(json.load(fid)) - # return parameters - return parameters - @staticmethod def parse_file( model_file: str | pathlib.Path, diff --git a/pyproject.toml b/pyproject.toml index 3ccdd1be..f243bd30 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,7 +67,7 @@ all = ["cartopy", "gdal", "h5py", "ipyleaflet", "ipywidgets", "jplephem", "matpl dev = ["flake8", "pytest>=4.6", "pytest-cov", "oct2py", "boto3"] [tool.setuptools.packages.find] -exclude = ["test*", "run*"] +exclude = ["providers*", "run*", "test*"] [tool.pytest.ini_options] minversion = "6.0" @@ -95,8 +95,9 @@ omit = [ "pyTMD/convert_ll_xy.py", "pyTMD/load_constituent.py", "pyTMD/load_nodal_corrections.py", - "test/def_to_json.py", - "test/model_to_json.py", + "test/_def_to_json.py", + "providers/_model_to_json.py", + "providers/_providers_to_database.py", ] [tool.coverage.report] diff --git a/scripts/compute_tidal_currents.py b/scripts/compute_tidal_currents.py index 4a70b3e4..e74c07bc 100755 --- a/scripts/compute_tidal_currents.py +++ b/scripts/compute_tidal_currents.py @@ -100,6 +100,7 @@ UPDATE HISTORY: Updated 09/2024: use JSON database for known model parameters use model name in default output filename for definition file case + drop support for the ascii definition file format Updated 08/2024: allow inferring only specific minor constituents added option to try automatic detection of definition file format changed from 'geotiff' to 'GTiff' and 'cog' formats @@ -210,7 +211,6 @@ def compute_tidal_currents(tide_dir, input_file, output_file, TIDE_MODEL=None, GZIP=True, DEFINITION_FILE=None, - DEFINITION_FORMAT='auto', CROP=False, FORMAT='csv', VARIABLES=[], @@ -231,8 +231,7 @@ def compute_tidal_currents(tide_dir, input_file, output_file, # get parameters for tide model if DEFINITION_FILE is not None: - model = pyTMD.io.model(tide_dir).from_file(DEFINITION_FILE, - format=DEFINITION_FORMAT) + model = pyTMD.io.model(tide_dir).from_file(DEFINITION_FILE) else: model = pyTMD.io.model(tide_dir, compressed=GZIP).current(TIDE_MODEL) @@ -487,9 +486,6 @@ def arguments(): group.add_argument('--definition-file', type=pathlib.Path, help='Tide model definition file') - parser.add_argument('--definition-format', - type=str, default='auto', choices=('ascii','json','auto'), - help='Format for model definition file') # crop tide model to (buffered) bounds of data parser.add_argument('--crop', '-C', default=False, action='store_true', @@ -602,7 +598,6 @@ def main(): TIDE_MODEL=args.tide, GZIP=args.gzip, DEFINITION_FILE=args.definition_file, - DEFINITION_FORMAT=args.definition_format, CROP=args.crop, FORMAT=args.format, VARIABLES=args.variables, diff --git a/scripts/compute_tidal_elevations.py b/scripts/compute_tidal_elevations.py index 7e37f89d..abe59f1f 100755 --- a/scripts/compute_tidal_elevations.py +++ b/scripts/compute_tidal_elevations.py @@ -103,6 +103,7 @@ UPDATE HISTORY: Updated 09/2024: use JSON database for known model parameters use model name in default output filename for definition file case + drop support for the ascii definition file format Updated 08/2024: allow inferring only specific minor constituents added option to try automatic detection of definition file format changed from 'geotiff' to 'GTiff' and 'cog' formats @@ -214,7 +215,6 @@ def compute_tidal_elevations(tide_dir, input_file, output_file, TIDE_MODEL=None, GZIP=True, DEFINITION_FILE=None, - DEFINITION_FORMAT='ascii', CROP=False, FORMAT='csv', VARIABLES=[], @@ -236,8 +236,7 @@ def compute_tidal_elevations(tide_dir, input_file, output_file, # get parameters for tide model if DEFINITION_FILE is not None: - model = pyTMD.io.model(tide_dir).from_file(DEFINITION_FILE, - format=DEFINITION_FORMAT) + model = pyTMD.io.model(tide_dir).from_file(DEFINITION_FILE) else: model = pyTMD.io.model(tide_dir, compressed=GZIP).elevation(TIDE_MODEL) @@ -484,9 +483,6 @@ def arguments(): group.add_argument('--definition-file', type=pathlib.Path, help='Tide model definition file') - parser.add_argument('--definition-format', - type=str, default='auto', choices=('ascii','json','auto'), - help='Format for model definition file') # crop tide model to (buffered) bounds of data parser.add_argument('--crop', '-C', default=False, action='store_true', @@ -604,7 +600,6 @@ def main(): TIDE_MODEL=args.tide, GZIP=args.gzip, DEFINITION_FILE=args.definition_file, - DEFINITION_FORMAT=args.definition_format, CROP=args.crop, FORMAT=args.format, VARIABLES=args.variables, diff --git a/test/def_to_json.py b/test/_def_to_json.py similarity index 100% rename from test/def_to_json.py rename to test/_def_to_json.py diff --git a/test/model_CATS2008.def b/test/model_CATS2008.def deleted file mode 100644 index e2702e42..00000000 --- a/test/model_CATS2008.def +++ /dev/null @@ -1,8 +0,0 @@ -format OTIS -name CATS2008 -model_file CATS2008/hf.CATS2008.out -grid_file CATS2008/grid_CATS2008 -projection CATS2008 -type z -variable tide_ocean -reference https://doi.org/10.15784/601235 \ No newline at end of file diff --git a/test/model_FES2012.def b/test/model_FES2012.def deleted file mode 100644 index 2f21c8c5..00000000 --- a/test/model_FES2012.def +++ /dev/null @@ -1,9 +0,0 @@ -format FES -name FES2012 -model_file fes2012/*_FES2012_SLEV.nc.gz -type z -version FES2012 -variable tide_ocean -scale 0.01 -compressed True -reference https://www.aviso.altimetry.fr/en/data/products/auxiliary-products/global-tide-fes.html \ No newline at end of file diff --git a/test/model_FES2014.def b/test/model_FES2014.def deleted file mode 100644 index 4dd3a562..00000000 --- a/test/model_FES2014.def +++ /dev/null @@ -1,10 +0,0 @@ -format FES -name FES2014 -model_file fes2014/ocean_tide/2n2.nc.gz,fes2014/ocean_tide/eps2.nc.gz,fes2014/ocean_tide/j1.nc.gz,fes2014/ocean_tide/k1.nc.gz,fes2014/ocean_tide/k2.nc.gz,fes2014/ocean_tide/l2.nc.gz,fes2014/ocean_tide/la2.nc.gz,fes2014/ocean_tide/m2.nc.gz,fes2014/ocean_tide/m3.nc.gz,fes2014/ocean_tide/m4.nc.gz,fes2014/ocean_tide/m6.nc.gz,fes2014/ocean_tide/m8.nc.gz,fes2014/ocean_tide/mf.nc.gz,fes2014/ocean_tide/mks2.nc.gz,fes2014/ocean_tide/mm.nc.gz,fes2014/ocean_tide/mn4.nc.gz,fes2014/ocean_tide/ms4.nc.gz,fes2014/ocean_tide/msf.nc.gz,fes2014/ocean_tide/msqm.nc.gz,fes2014/ocean_tide/mtm.nc.gz,fes2014/ocean_tide/mu2.nc.gz,fes2014/ocean_tide/n2.nc.gz,fes2014/ocean_tide/n4.nc.gz,fes2014/ocean_tide/nu2.nc.gz,fes2014/ocean_tide/o1.nc.gz,fes2014/ocean_tide/p1.nc.gz,fes2014/ocean_tide/q1.nc.gz,fes2014/ocean_tide/r2.nc.gz,fes2014/ocean_tide/s1.nc.gz,fes2014/ocean_tide/s2.nc.gz,fes2014/ocean_tide/s4.nc.gz,fes2014/ocean_tide/sa.nc.gz,fes2014/ocean_tide/ssa.nc.gz,fes2014/ocean_tide/t2.nc.gz -constituents 2n2,eps2,j1,k1,k2,l2,lambda2,m2,m3,m4,m6,m8,mf,mks2,mm,mn4,ms4,msf,msqm,mtm,mu2,n2,n4,nu2,o1,p1,q1,r2,s1,s2,s4,sa,ssa,t2 -type z -version FES2014 -variable tide_ocean -scale 0.01 -compressed True -reference https://www.aviso.altimetry.fr/en/data/products/auxiliary-products/global-tide-fes.html \ No newline at end of file diff --git a/test/model_FES2014_currents.def b/test/model_FES2014_currents.def deleted file mode 100644 index b4b3ef5d..00000000 --- a/test/model_FES2014_currents.def +++ /dev/null @@ -1,9 +0,0 @@ -format FES -name FES2014 -model_file fes2014/eastward_velocity/2n2.nc.gz,fes2014/eastward_velocity/eps2.nc.gz,fes2014/eastward_velocity/j1.nc.gz,fes2014/eastward_velocity/k1.nc.gz,fes2014/eastward_velocity/k2.nc.gz,fes2014/eastward_velocity/l2.nc.gz,fes2014/eastward_velocity/la2.nc.gz,fes2014/eastward_velocity/m2.nc.gz,fes2014/eastward_velocity/m3.nc.gz,fes2014/eastward_velocity/m4.nc.gz,fes2014/eastward_velocity/m6.nc.gz,fes2014/eastward_velocity/m8.nc.gz,fes2014/eastward_velocity/mf.nc.gz,fes2014/eastward_velocity/mks2.nc.gz,fes2014/eastward_velocity/mm.nc.gz,fes2014/eastward_velocity/mn4.nc.gz,fes2014/eastward_velocity/ms4.nc.gz,fes2014/eastward_velocity/msf.nc.gz,fes2014/eastward_velocity/msqm.nc.gz,fes2014/eastward_velocity/mtm.nc.gz,fes2014/eastward_velocity/mu2.nc.gz,fes2014/eastward_velocity/n2.nc.gz,fes2014/eastward_velocity/n4.nc.gz,fes2014/eastward_velocity/nu2.nc.gz,fes2014/eastward_velocity/o1.nc.gz,fes2014/eastward_velocity/p1.nc.gz,fes2014/eastward_velocity/q1.nc.gz,fes2014/eastward_velocity/r2.nc.gz,fes2014/eastward_velocity/s1.nc.gz,fes2014/eastward_velocity/s2.nc.gz,fes2014/eastward_velocity/s4.nc.gz,fes2014/eastward_velocity/sa.nc.gz,fes2014/eastward_velocity/ssa.nc.gz,fes2014/eastward_velocity/t2.nc.gz;fes2014/northward_velocity/2n2.nc.gz,fes2014/northward_velocity/eps2.nc.gz,fes2014/northward_velocity/j1.nc.gz,fes2014/northward_velocity/k1.nc.gz,fes2014/northward_velocity/k2.nc.gz,fes2014/northward_velocity/l2.nc.gz,fes2014/northward_velocity/la2.nc.gz,fes2014/northward_velocity/m2.nc.gz,fes2014/northward_velocity/m3.nc.gz,fes2014/northward_velocity/m4.nc.gz,fes2014/northward_velocity/m6.nc.gz,fes2014/northward_velocity/m8.nc.gz,fes2014/northward_velocity/mf.nc.gz,fes2014/northward_velocity/mks2.nc.gz,fes2014/northward_velocity/mm.nc.gz,fes2014/northward_velocity/mn4.nc.gz,fes2014/northward_velocity/ms4.nc.gz,fes2014/northward_velocity/msf.nc.gz,fes2014/northward_velocity/msqm.nc.gz,fes2014/northward_velocity/mtm.nc.gz,fes2014/northward_velocity/mu2.nc.gz,fes2014/northward_velocity/n2.nc.gz,fes2014/northward_velocity/n4.nc.gz,fes2014/northward_velocity/nu2.nc.gz,fes2014/northward_velocity/o1.nc.gz,fes2014/northward_velocity/p1.nc.gz,fes2014/northward_velocity/q1.nc.gz,fes2014/northward_velocity/r2.nc.gz,fes2014/northward_velocity/s1.nc.gz,fes2014/northward_velocity/s2.nc.gz,fes2014/northward_velocity/s4.nc.gz,fes2014/northward_velocity/sa.nc.gz,fes2014/northward_velocity/ssa.nc.gz,fes2014/northward_velocity/t2.nc.gz -constituents 2n2,eps2,j1,k1,k2,l2,lambda2,m2,m3,m4,m6,m8,mf,mks2,mm,mn4,ms4,msf,msqm,mtm,mu2,n2,n4,nu2,o1,p1,q1,r2,s1,s2,s4,sa,ssa,t2 -type u,v -version FES2014 -scale 1.0 -compressed True -reference https://www.aviso.altimetry.fr/en/data/products/auxiliary-products/global-tide-fes.html \ No newline at end of file diff --git a/test/model_FES2022b_extrapolated.def b/test/model_FES2022b_extrapolated.def deleted file mode 100644 index 141563c9..00000000 --- a/test/model_FES2022b_extrapolated.def +++ /dev/null @@ -1,9 +0,0 @@ -format FES -name FES2022 -model_file fes2022b/ocean_tide_extrapolated/*fes2022.nc* -type z -version FES2022 -variable tide_ocean -scale 0.01 -compressed True -reference https://doi.org/10.24400/527896/A01-2024.004 \ No newline at end of file diff --git a/test/model_GOT4.10.def b/test/model_GOT4.10.def deleted file mode 100644 index 8940fb8d..00000000 --- a/test/model_GOT4.10.def +++ /dev/null @@ -1,9 +0,0 @@ -format GOT -name GOT4.10 -model_file GOT4.10c/grids_loadtide/k1load.d.gz,GOT4.10c/grids_loadtide/k2load.d.gz,GOT4.10c/grids_loadtide/m2load.d.gz,GOT4.10c/grids_loadtide/m4load.d.gz,GOT4.10c/grids_loadtide/n2load.d.gz,GOT4.10c/grids_loadtide/o1load.d.gz,GOT4.10c/grids_loadtide/p1load.d.gz,GOT4.10c/grids_loadtide/q1load.d.gz,GOT4.10c/grids_loadtide/s1load.d.gz,GOT4.10c/grids_loadtide/s2load.d.gz -type z -variable tide_load -version 4.10 -scale 0.001 -compressed True -reference https://ntrs.nasa.gov/citations/19990089548 \ No newline at end of file diff --git a/test/model_TPXO9-atlas-v5.def b/test/model_TPXO9-atlas-v5.def deleted file mode 100644 index f3e9f386..00000000 --- a/test/model_TPXO9-atlas-v5.def +++ /dev/null @@ -1,10 +0,0 @@ -format netcdf -name TPXO9-atlas-v5 -model_file TPXO9_atlas_v5/h_2n2_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/h_k1_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/h_k2_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/h_m2_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/h_m4_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/h_mf_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/h_mm_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/h_mn4_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/h_ms4_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/h_n2_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/h_o1_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/h_p1_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/h_q1_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/h_s1_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/h_s2_tpxo9_atlas_30_v5.nc -grid_file TPXO9_atlas_v5/grid_tpxo9_atlas_30_v5.nc -type z -variable tide_ocean -version v5 -scale 0.01 -compressed False -reference https://www.tpxo.net/global/tpxo9-atlas \ No newline at end of file diff --git a/test/model_TPXO9-atlas-v5_currents.def b/test/model_TPXO9-atlas-v5_currents.def deleted file mode 100644 index d18fdbde..00000000 --- a/test/model_TPXO9-atlas-v5_currents.def +++ /dev/null @@ -1,9 +0,0 @@ -format netcdf -name TPXO9-atlas-v5 -model_file TPXO9_atlas_v5/u_2n2_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_k1_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_k2_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_m2_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_m4_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_mf_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_mm_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_mn4_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_ms4_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_n2_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_o1_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_p1_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_q1_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_s1_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_s2_tpxo9_atlas_30_v5.nc;TPXO9_atlas_v5/u_2n2_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_k1_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_k2_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_m2_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_m4_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_mf_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_mm_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_mn4_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_ms4_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_n2_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_o1_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_p1_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_q1_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_s1_tpxo9_atlas_30_v5.nc,TPXO9_atlas_v5/u_s2_tpxo9_atlas_30_v5.nc -grid_file TPXO9_atlas_v5/grid_tpxo9_atlas_30_v5.nc -type u,v -version v5 -scale 0.01 -compressed False -reference https://www.tpxo.net/global/tpxo9-atlas \ No newline at end of file diff --git a/test/test_atlas_read.py b/test/test_atlas_read.py index a941e7b1..018b1c25 100644 --- a/test/test_atlas_read.py +++ b/test/test_atlas_read.py @@ -1,6 +1,6 @@ #!/usr/bin/env python u""" -test_atlas_read.py (07/2024) +test_atlas_read.py (09/2024) Tests that ATLAS compact and netCDF4 data can be downloaded from AWS S3 bucket Tests the read program to verify that constituents are being extracted @@ -16,6 +16,7 @@ https://boto3.amazonaws.com/v1/documentation/api/latest/index.html UPDATE HISTORY: + Updated 09/2024: drop support for the ascii definition file format Updated 07/2024: add parametrize over cropping the model fields Updated 04/2024: use timescale for temporal operations Updated 01/2024: test doodson and cartwright numbers of each constituent @@ -32,6 +33,7 @@ import re import io import gzip +import json import boto3 import shutil import pytest @@ -412,13 +414,8 @@ def test_definition_file(MODEL): # create model definition file fid = io.StringIO() attrs = ['name','format','grid_file','model_file','compressed','type','scale'] - for attr in attrs: - val = getattr(model,attr) - if isinstance(val,list): - var = ','.join(str(v) for v in val) - fid.write(f'{attr}\t{var}\n') - else: - fid.write(f'{attr}\t{val}\n') + d = model.to_dict(fields=attrs, serialize=True) + json.dump(d, fid) fid.seek(0) # use model definition file as input m = pyTMD.io.model().from_file(fid) diff --git a/test/test_download_and_read.py b/test/test_download_and_read.py index 61d39475..548a0f8d 100644 --- a/test/test_download_and_read.py +++ b/test/test_download_and_read.py @@ -1,6 +1,6 @@ #!/usr/bin/env python u""" -test_download_and_read.py (07/2024) +test_download_and_read.py (09/2024) Tests that CATS2008 data can be downloaded from the US Antarctic Program (USAP) Tests that AOTIM-5-2018 data can be downloaded from the NSF ArcticData server Tests the read program to verify that constituents are being extracted @@ -19,6 +19,7 @@ https://boto3.amazonaws.com/v1/documentation/api/latest/index.html UPDATE HISTORY: + Updated 09/2024: drop support for the ascii definition file format Updated 07/2024: add parametrize over cropping the model fields Updated 04/2024: use timescale for temporal operations Updated 01/2024: refactored compute functions into compute.py @@ -43,6 +44,7 @@ """ import re import io +import json import boto3 import shutil import pytest @@ -748,12 +750,8 @@ def test_definition_file(self, MODEL): # create model definition file fid = io.StringIO() attrs = ['name','format','grid_file','model_file','type','projection'] - for attr in attrs: - val = getattr(model,attr) - if isinstance(val,list): - fid.write('{0}\t{1}\n'.format(attr,','.join(val))) - else: - fid.write('{0}\t{1}\n'.format(attr,val)) + d = model.to_dict(fields=attrs, serialize=True) + json.dump(d, fid) fid.seek(0) # use model definition file as input m = pyTMD.io.model().from_file(fid) @@ -1025,13 +1023,8 @@ def test_definition_file(self, MODEL): # create model definition file fid = io.StringIO() attrs = ['name','format','grid_file','model_file','type','projection'] - for attr in attrs: - val = getattr(model,attr) - if isinstance(val,list): - var = ','.join(str(v) for v in val) - fid.write(f'{attr}\t{var}\n') - else: - fid.write(f'{attr}\t{val}\n') + d = model.to_dict(fields=attrs, serialize=True) + json.dump(d, fid) fid.seek(0) # use model definition file as input m = pyTMD.io.model().from_file(fid) diff --git a/test/test_fes_predict.py b/test/test_fes_predict.py index dae44bf1..e562f7f0 100644 --- a/test/test_fes_predict.py +++ b/test/test_fes_predict.py @@ -1,6 +1,6 @@ #!/usr/bin/env python u""" -test_fes_predict.py (07/2024) +test_fes_predict.py (09/2024) Tests that FES2014 data can be downloaded from AWS S3 bucket Tests the read program to verify that constituents are being extracted Tests that interpolated results are comparable to FES2014 program @@ -17,6 +17,7 @@ https://boto3.amazonaws.com/v1/documentation/api/latest/index.html UPDATE HISTORY: + Updated 09/2024: drop support for the ascii definition file format Updated 07/2024: add parametrize over cropping the model fields Updated 04/2024: use timescale for temporal operations Updated 01/2024: test doodson and cartwright numbers of each constituent @@ -29,6 +30,7 @@ Written 08/2020 """ import io +import json import boto3 import shutil import pytest @@ -254,13 +256,8 @@ def test_definition_file(MODEL): # create model definition file fid = io.StringIO() attrs = ['name','format','model_file','compressed','type','scale','version'] - for attr in attrs: - val = getattr(model,attr) - if isinstance(val,list): - var = ','.join(str(v) for v in val) - fid.write(f'{attr}\t{var}\n') - else: - fid.write(f'{attr}\t{val}\n') + d = model.to_dict(fields=attrs, serialize=True) + json.dump(d, fid) fid.seek(0) # use model definition file as input m = pyTMD.io.model().from_file(fid) diff --git a/test/test_model.py b/test/test_model.py index d2f0dd4f..15f33444 100644 --- a/test/test_model.py +++ b/test/test_model.py @@ -1,6 +1,12 @@ """ -test_model.py (04/2024) +test_model.py (09/2024) Tests the reading of model definition files + +UPDATE HISTORY: + Updated 09/2024: drop support for the ascii definition file format + Updated 08/2024: add automatic detection of definition file format + Updated 07/2024: add new JSON format definition file format + Written 04/2024 """ from __future__ import annotations @@ -16,18 +22,12 @@ filename = inspect.getframeinfo(inspect.currentframe()).filename filepath = pathlib.Path(filename).absolute().parent -@pytest.mark.parametrize("file_format", ['ascii','json','auto']) -def test_definition_CATS2008(file_format): +def test_definition_CATS2008(): """Tests the reading of the CATS2008 model definition file """ - # definition files of each format - definition_file = {} - definition_file['ascii'] = 'model_CATS2008.def' - definition_file['json'] = 'model_CATS2008.json' - definition_file['auto'] = 'model_CATS2008.json' - val = definition_file[file_format] - # read model definition file for format - m = pyTMD.io.model().from_file(filepath.joinpath(val), format=file_format) + # read definition file + definition_file = 'model_CATS2008.json' + m = pyTMD.io.model().from_file(filepath.joinpath(definition_file)) # test read variables assert m.format == 'OTIS' assert m.name == 'CATS2008' @@ -46,18 +46,12 @@ def test_definition_CATS2008(file_format): assert m.gla12 == 'd_ocElv' assert m.long_name == 'ocean_tide_elevation' -@pytest.mark.parametrize("file_format", ['ascii','json','auto']) -def test_definition_FES(file_format): +def test_definition_FES(): """Tests the reading of the FES2014 model definition file """ - # definition files of each format - definition_file = {} - definition_file['ascii'] = 'model_FES2014.def' - definition_file['json'] = 'model_FES2014.json' - definition_file['auto'] = 'model_FES2014.json' - val = definition_file[file_format] - # read model definition file for format - m = pyTMD.io.model().from_file(filepath.joinpath(val), format=file_format) + # read definition file + definition_file = 'model_FES2014.json' + m = pyTMD.io.model().from_file(filepath.joinpath(definition_file)) # model files and constituents model_files = ['fes2014/ocean_tide/2n2.nc.gz', 'fes2014/ocean_tide/eps2.nc.gz', 'fes2014/ocean_tide/j1.nc.gz', @@ -107,19 +101,13 @@ def test_definition_FES(file_format): assert m.long_name == 'ocean_tide_elevation' # PURPOSE: test glob file functionality -@pytest.mark.parametrize("file_format", ['ascii','json','auto']) -def test_definition_FES_glob(file_format): +def test_definition_FES_glob(): """Tests the reading of the FES2014 model definition file with glob file searching """ - # definition files of each format - definition_file = {} - definition_file['ascii'] = 'model_FES2014.def' - definition_file['json'] = 'model_FES2014.json' - definition_file['auto'] = 'model_FES2014.def' - val = definition_file[file_format] - # read model definition file for format - m = pyTMD.io.model().from_file(filepath.joinpath(val), format=file_format) + # read model definition file + definition_file = 'model_FES2014.json' + m = pyTMD.io.model().from_file(filepath.joinpath(definition_file)) # model files model_files = ['fes2014/ocean_tide/2n2.nc.gz', 'fes2014/ocean_tide/eps2.nc.gz', 'fes2014/ocean_tide/j1.nc.gz', @@ -148,25 +136,14 @@ def test_definition_FES_glob(file_format): fid = io.StringIO() glob_string = r'fes2014/ocean_tide/*.nc.gz' attrs = ['name','format','compressed','type','scale','version'] - if file_format in ('ascii', 'auto'): - # create tab-delimited definition file - for attr in attrs: - val = getattr(m,attr) - if isinstance(val,list): - fid.write('{0}\t{1}\n'.format(attr,','.join(val))) - else: - fid.write('{0}\t{1}\n'.format(attr,val)) - # append glob strings for model file - fid.write(f'model_file\t{glob_string}\n') - elif (file_format == 'json'): - # create JSON definition file - d = {attr:getattr(m,attr) for attr in attrs} - d['model_file'] = glob_string - json.dump(d, fid) + # create JSON definition file + d = {attr:getattr(m,attr) for attr in attrs} + d['model_file'] = glob_string + json.dump(d, fid) # rewind the glob definition file fid.seek(0) # use model definition file as input - model = pyTMD.io.model(directory=filepath).from_file(fid, format=file_format) + model = pyTMD.io.model(directory=filepath).from_file(fid) for attr in attrs: assert getattr(model,attr) == getattr(m,attr) # verify that the model files and constituents match @@ -180,18 +157,12 @@ def test_definition_FES_glob(file_format): # clean up model shutil.rmtree(filepath.joinpath('fes2014')) -@pytest.mark.parametrize("file_format", ['ascii','json','auto']) -def test_definition_FES_currents(file_format): +def test_definition_FES_currents(): """Tests the reading of the FES2014 model definition file for currents """ - # definition files of each format - definition_file = {} - definition_file['ascii'] = 'model_FES2014_currents.def' - definition_file['json'] = 'model_FES2014_currents.json' - definition_file['auto'] = 'model_FES2014_currents.json' - val = definition_file[file_format] - # read model definition file for format - m = pyTMD.io.model().from_file(filepath.joinpath(val), format=file_format) + # read model definition file + definition_file = 'model_FES2014_currents.json' + m = pyTMD.io.model().from_file(filepath.joinpath(definition_file)) # model files and constituents model_files = {} model_files['u'] = ['fes2014/eastward_velocity/2n2.nc.gz', @@ -254,19 +225,13 @@ def test_definition_FES_currents(file_format): assert m.long_name['v'] == 'meridional_tidal_current' # PURPOSE: test glob file functionality -@pytest.mark.parametrize("file_format", ['ascii','json','auto']) -def test_definition_FES_currents_glob(file_format): +def test_definition_FES_currents_glob(): """Tests the reading of the FES2014 model definition file with glob file searching for currents """ - # definition files of each format - definition_file = {} - definition_file['ascii'] = 'model_FES2014_currents.def' - definition_file['json'] = 'model_FES2014_currents.json' - definition_file['auto'] = 'model_FES2014_currents.def' - val = definition_file[file_format] - # read model definition file for format - m = pyTMD.io.model().from_file(filepath.joinpath(val), format=file_format) + # read model definition file + definition_file = 'model_FES2014_currents.json' + m = pyTMD.io.model().from_file(filepath.joinpath(definition_file)) # model files for each component model_files = {} model_files['u'] = ['fes2014/eastward_velocity/2n2.nc.gz', @@ -316,25 +281,14 @@ def test_definition_FES_currents_glob(file_format): attrs = ['name','format','compressed','type','scale','version'] glob_string_u = r'fes2014/eastward_velocity/*.nc.gz' glob_string_v = r'fes2014/northward_velocity/*.nc.gz' - if file_format in ('ascii','auto'): - # create tab-delimited definition file - for attr in attrs: - val = getattr(m,attr) - if isinstance(val,list): - fid.write('{0}\t{1}\n'.format(attr,','.join(val))) - else: - fid.write('{0}\t{1}\n'.format(attr,val)) - # append glob strings for model file - fid.write(f'model_file\t{glob_string_u};{glob_string_v}\n') - elif (file_format == 'json'): - # create JSON definition file - d = {attr:getattr(m,attr) for attr in attrs} - d['model_file'] = {'u':glob_string_u,'v':glob_string_v} - json.dump(d, fid) + # create JSON definition file + d = {attr:getattr(m,attr) for attr in attrs} + d['model_file'] = {'u':glob_string_u,'v':glob_string_v} + json.dump(d, fid) # rewind the glob definition file fid.seek(0) # use model definition file as input - model = pyTMD.io.model(directory=filepath).from_file(fid, format=file_format) + model = pyTMD.io.model(directory=filepath).from_file(fid) for attr in attrs: assert getattr(model,attr) == getattr(m,attr) # verify that the model files and constituents match @@ -349,18 +303,12 @@ def test_definition_FES_currents_glob(file_format): # clean up model shutil.rmtree(filepath.joinpath('fes2014')) -@pytest.mark.parametrize("file_format", ['ascii','json','auto']) -def test_definition_GOT(file_format): +def test_definition_GOT(): """Tests the reading of the GOT4.10 model definition file """ - # definition files of each format - definition_file = {} - definition_file['ascii'] = 'model_GOT4.10.def' - definition_file['json'] = 'model_GOT4.10.json' - definition_file['auto'] = 'model_GOT4.10.json' - val = definition_file[file_format] - # read model definition file for format - m = pyTMD.io.model().from_file(filepath.joinpath(val), format=file_format) + # read model definition file + definition_file = 'model_GOT4.10.json' + m = pyTMD.io.model().from_file(filepath.joinpath(definition_file)) # model files model_files = ['GOT4.10c/grids_loadtide/k1load.d.gz', 'GOT4.10c/grids_loadtide/k2load.d.gz', @@ -393,19 +341,13 @@ def test_definition_GOT(file_format): assert m.long_name == 'load_tide_elevation' # PURPOSE: test glob file functionality -@pytest.mark.parametrize("file_format", ['ascii','json','auto']) -def test_definition_GOT_glob(file_format): +def test_definition_GOT_glob(): """Tests the reading of the GOT4.10 model definition file with glob file searching """ - # definition files of each format - definition_file = {} - definition_file['ascii'] = 'model_GOT4.10.def' - definition_file['json'] = 'model_GOT4.10.json' - definition_file['auto'] = 'model_GOT4.10.def' - val = definition_file[file_format] - # read model definition file for format - m = pyTMD.io.model().from_file(filepath.joinpath(val), format=file_format) + # read model definition file + definition_file = 'model_GOT4.10.json' + m = pyTMD.io.model().from_file(filepath.joinpath(definition_file)) # model files model_files = ['GOT4.10c/grids_loadtide/k1load.d.gz', 'GOT4.10c/grids_loadtide/k2load.d.gz', @@ -426,25 +368,14 @@ def test_definition_GOT_glob(file_format): fid = io.StringIO() attrs = ['name','format','compressed','type','scale'] glob_string = r'GOT4.10c/grids_loadtide/*.d.gz' - if file_format in ('ascii','auto'): - # create tab-delimited definition file - for attr in attrs: - val = getattr(m,attr) - if isinstance(val,list): - fid.write('{0}\t{1}\n'.format(attr,','.join(val))) - else: - fid.write('{0}\t{1}\n'.format(attr,val)) - # append glob strings for model file - fid.write(f'model_file\t{glob_string}\n') - elif (file_format == 'json'): - # create JSON definition file - d = {attr:getattr(m,attr) for attr in attrs} - d['model_file'] = glob_string - json.dump(d, fid) + # create JSON definition file + d = {attr:getattr(m,attr) for attr in attrs} + d['model_file'] = glob_string + json.dump(d, fid) # rewind the glob definition file fid.seek(0) # use model definition file as input - model = pyTMD.io.model(directory=filepath).from_file(fid, format=file_format) + model = pyTMD.io.model(directory=filepath).from_file(fid) for attr in attrs: assert getattr(model,attr) == getattr(m,attr) # verify that the model files match @@ -456,18 +387,12 @@ def test_definition_GOT_glob(file_format): # clean up model shutil.rmtree(filepath.joinpath('GOT4.10c')) -@pytest.mark.parametrize("file_format", ['ascii','json','auto']) -def test_definition_TPXO9(file_format): +def test_definition_TPXO9(): """Tests the reading of the TPXO9-atlas-v5 model definition file """ - # definition files of each format - definition_file = {} - definition_file['ascii'] = 'model_TPXO9-atlas-v5.def' - definition_file['json'] = 'model_TPXO9-atlas-v5.json' - definition_file['auto'] = 'model_TPXO9-atlas-v5.json' - val = definition_file[file_format] - # read model definition file for format - m = pyTMD.io.model().from_file(filepath.joinpath(val), format=file_format) + # read model definition file + definition_file = 'model_TPXO9-atlas-v5.json' + m = pyTMD.io.model().from_file(filepath.joinpath(definition_file)) # model files model_files = ['TPXO9_atlas_v5/h_2n2_tpxo9_atlas_30_v5.nc', 'TPXO9_atlas_v5/h_k1_tpxo9_atlas_30_v5.nc', @@ -507,19 +432,13 @@ def test_definition_TPXO9(file_format): assert m.long_name == 'ocean_tide_elevation' # PURPOSE: test glob file functionality -@pytest.mark.parametrize("file_format", ['ascii','json','auto']) -def test_definition_TPXO9_glob(file_format): +def test_definition_TPXO9_glob(): """Tests the reading of the TPXO9-atlas-v5 model definition file with glob file searching """ - # definition files of each format - definition_file = {} - definition_file['ascii'] = 'model_TPXO9-atlas-v5.def' - definition_file['json'] = 'model_TPXO9-atlas-v5.json' - definition_file['auto'] = 'model_TPXO9-atlas-v5.def' - val = definition_file[file_format] - # read model definition file for format - m = pyTMD.io.model().from_file(filepath.joinpath(val), format=file_format) + # read model definition file + definition_file = 'model_TPXO9-atlas-v5.json' + m = pyTMD.io.model().from_file(filepath.joinpath(definition_file)) # model files model_files = ['TPXO9_atlas_v5/h_2n2_tpxo9_atlas_30_v5.nc', 'TPXO9_atlas_v5/h_k1_tpxo9_atlas_30_v5.nc', @@ -552,27 +471,15 @@ def test_definition_TPXO9_glob(file_format): fid = io.StringIO() attrs = ['name','format','compressed','type','scale'] glob_string = r'TPXO9_atlas_v5/h*.nc' - if file_format in ('ascii','auto'): - # create tab-delimited definition file - for attr in attrs: - val = getattr(m,attr) - if isinstance(val,list): - fid.write('{0}\t{1}\n'.format(attr,','.join(val))) - else: - fid.write('{0}\t{1}\n'.format(attr,val)) - # append glob strings for model file - fid.write(f'model_file\t{glob_string}\n') - fid.write(f'grid_file\t{grid_file}\n') - elif (file_format == 'json'): - # create JSON definition file - d = {attr:getattr(m,attr) for attr in attrs} - d['model_file'] = glob_string - d['grid_file'] = str(grid_file) - json.dump(d, fid) + # create JSON definition file + d = {attr:getattr(m,attr) for attr in attrs} + d['model_file'] = glob_string + d['grid_file'] = str(grid_file) + json.dump(d, fid) # rewind the glob definition file fid.seek(0) # use model definition file as input - model = pyTMD.io.model(directory=filepath).from_file(fid, format=file_format) + model = pyTMD.io.model(directory=filepath).from_file(fid) for attr in attrs: assert getattr(model,attr) == getattr(m,attr) # verify that the model files match @@ -584,18 +491,12 @@ def test_definition_TPXO9_glob(file_format): # clean up model shutil.rmtree(filepath.joinpath('TPXO9_atlas_v5')) -@pytest.mark.parametrize("file_format", ['ascii','json','auto']) -def test_definition_TPXO9_currents(file_format): +def test_definition_TPXO9_currents(): """Tests the reading of the TPXO9-atlas-v5 model definition file for currents """ - # definition files of each format - definition_file = {} - definition_file['ascii'] = 'model_TPXO9-atlas-v5_currents.def' - definition_file['json'] = 'model_TPXO9-atlas-v5_currents.json' - definition_file['auto'] = 'model_TPXO9-atlas-v5_currents.json' - val = definition_file[file_format] - # read model definition file for format - m = pyTMD.io.model().from_file(filepath.joinpath(val), format=file_format) + # read model definition file + definition_file = 'model_TPXO9-atlas-v5_currents.json' + m = pyTMD.io.model().from_file(filepath.joinpath(definition_file)) # model files for each component model_files = {} model_files['u'] = ['TPXO9_atlas_v5/u_2n2_tpxo9_atlas_30_v5.nc', @@ -642,20 +543,14 @@ def test_definition_TPXO9_currents(file_format): assert m.long_name['u'] == 'zonal_tidal_current' assert m.long_name['v'] == 'meridional_tidal_current' -# PURPOSE: test glob file functionalityfrom_dict -@pytest.mark.parametrize("file_format", ['ascii','json','auto']) -def test_definition_TPXO9_currents_glob(file_format): +# PURPOSE: test glob file functionality +def test_definition_TPXO9_currents_glob(): """Tests the reading of the TPXO9-atlas-v5 model definition file for currents with glob file searching """ - # definition files of each format - definition_file = {} - definition_file['ascii'] = 'model_TPXO9-atlas-v5_currents.def' - definition_file['json'] = 'model_TPXO9-atlas-v5_currents.json' - definition_file['auto'] = 'model_TPXO9-atlas-v5_currents.def' - val = definition_file[file_format] - # read model definition file for format - m = pyTMD.io.model().from_file(filepath.joinpath(val), format=file_format) + # read model definition file + definition_file = 'model_TPXO9-atlas-v5_currents.json' + m = pyTMD.io.model().from_file(filepath.joinpath(definition_file)) # model files for each component model_files = {} model_files['u'] = ['TPXO9_atlas_v5/u_2n2_tpxo9_atlas_30_v5.nc', @@ -703,27 +598,15 @@ def test_definition_TPXO9_currents_glob(file_format): attrs = ['name','format','compressed','type','scale'] glob_string_u = r'TPXO9_atlas_v5/u*.nc' glob_string_v = r'TPXO9_atlas_v5/u*.nc' - if file_format in ('ascii','auto'): - # create tab-delimited definition file - for attr in attrs: - val = getattr(m,attr) - if isinstance(val,list): - fid.write('{0}\t{1}\n'.format(attr,','.join(val))) - else: - fid.write('{0}\t{1}\n'.format(attr,val)) - # append glob strings for model file - fid.write(f'model_file\t{glob_string_u};{glob_string_v}\n') - fid.write(f'grid_file\t{grid_file}\n') - elif (file_format == 'json'): - # create JSON definition file - d = {attr:getattr(m,attr) for attr in attrs} - d['model_file'] = {'u':glob_string_u,'v':glob_string_v} - d['grid_file'] = str(grid_file) - json.dump(d, fid) + # create JSON definition file + d = {attr:getattr(m,attr) for attr in attrs} + d['model_file'] = {'u':glob_string_u,'v':glob_string_v} + d['grid_file'] = str(grid_file) + json.dump(d, fid) # rewind the glob definition file fid.seek(0) # use model definition file as input - model = pyTMD.io.model(directory=filepath).from_file(fid, format=file_format) + model = pyTMD.io.model(directory=filepath).from_file(fid) for attr in attrs: assert getattr(model,attr) == getattr(m,attr) # verify that the model files match diff --git a/test/test_perth3_read.py b/test/test_perth3_read.py index 996bf51d..ae3b971c 100644 --- a/test/test_perth3_read.py +++ b/test/test_perth3_read.py @@ -1,6 +1,6 @@ #!/usr/bin/env python u""" -test_perth3_read.py (08/2024) +test_perth3_read.py (09/2024) Tests that GOT4.7 data can be downloaded from AWS S3 bucket Tests the read program to verify that constituents are being extracted Tests that interpolated results are comparable to NASA PERTH3 program @@ -15,6 +15,7 @@ https://boto3.amazonaws.com/v1/documentation/api/latest/index.html UPDATE HISTORY: + Updated 09/2024: drop support for the ascii definition file format Updated 08/2024: increased tolerance for comparing with GOT4.7 tests as using nodal corrections from PERTH5 use a reduced list of minor constituents to match GOT4.7 tests @@ -33,6 +34,7 @@ """ import io import gzip +import json import boto3 import shutil import pytest @@ -272,13 +274,8 @@ def test_definition_file(MODEL): # create model definition file fid = io.StringIO() attrs = ['name','format','model_file','compressed','type','scale'] - for attr in attrs: - val = getattr(model,attr) - if isinstance(val,list): - var = ','.join(str(v) for v in val) - fid.write(f'{attr}\t{var}\n') - else: - fid.write(f'{attr}\t{val}\n') + d = model.to_dict(fields=attrs, serialize=True) + json.dump(d, fid) fid.seek(0) # use model definition file as input m = pyTMD.io.model().from_file(fid)