From b5e0e0e4a1b13e099062deab4394a108cdd374e9 Mon Sep 17 00:00:00 2001 From: tsutterley Date: Thu, 24 Sep 2020 13:36:26 -0700 Subject: [PATCH] switching primary branch to main update documentation and readme for branch changes generalize build opener function for different Earthdata instances update reduce OTIS files for python3 and projections add test tidal ellipse program adjust dimensions of input coordinates to be iterable --- README.md | 10 +- doc/source/conf.py | 2 +- doc/source/getting_started/Getting-Started.md | 12 +- doc/source/getting_started/Install.md | 4 +- doc/source/user_guide/arcticdata_tides.md | 2 +- doc/source/user_guide/aviso_fes_tides.md | 2 +- doc/source/user_guide/bilinear_interp.md | 2 +- .../user_guide/calc_astrol_longitudes.md | 2 +- doc/source/user_guide/calc_delta_time.md | 2 +- .../user_guide/compute_LPET_elevations.md | 2 +- .../user_guide/compute_LPET_icebridge_data.md | 2 +- .../user_guide/compute_LPT_displacements.md | 2 +- .../user_guide/compute_LPT_icebridge_data.md | 2 +- .../user_guide/compute_OPT_displacements.md | 2 +- .../user_guide/compute_OPT_icebridge_data.md | 2 +- .../user_guide/compute_equilibrium_tide.md | 2 +- .../user_guide/compute_tidal_currents.md | 2 +- .../user_guide/compute_tidal_elevations.md | 2 +- .../user_guide/compute_tide_corrections.md | 2 +- .../user_guide/compute_tides_ICESat2_ATL03.md | 2 +- .../user_guide/compute_tides_ICESat2_ATL06.md | 2 +- .../user_guide/compute_tides_ICESat2_ATL07.md | 2 +- .../user_guide/compute_tides_ICESat2_ATL12.md | 2 +- .../compute_tides_icebridge_data.md | 2 +- .../user_guide/convert_calendar_decimal.md | 2 +- doc/source/user_guide/convert_julian.md | 2 +- doc/source/user_guide/convert_ll_xy.md | 2 +- doc/source/user_guide/count_leap_seconds.md | 2 +- doc/source/user_guide/iers_mean_pole.md | 2 +- .../user_guide/infer_minor_corrections.md | 2 +- doc/source/user_guide/load_constituent.md | 2 +- .../user_guide/load_nodal_corrections.md | 2 +- doc/source/user_guide/output_otis_tides.md | 2 +- doc/source/user_guide/predict_tidal_ts.md | 2 +- doc/source/user_guide/predict_tide.md | 2 +- doc/source/user_guide/predict_tide_drift.md | 2 +- doc/source/user_guide/read_FES_model.md | 2 +- doc/source/user_guide/read_GOT_model.md | 2 +- doc/source/user_guide/read_iers_EOP.md | 2 +- doc/source/user_guide/read_netcdf_model.md | 2 +- doc/source/user_guide/read_ocean_pole_tide.md | 2 +- doc/source/user_guide/read_tide_model.md | 2 +- doc/source/user_guide/reduce_OTIS_files.md | 13 +- doc/source/user_guide/spatial.rst | 2 +- doc/source/user_guide/tidal_ellipse.md | 2 +- doc/source/user_guide/time.rst | 2 +- doc/source/user_guide/usap_cats_tides.md | 2 +- doc/source/user_guide/utilities.rst | 27 ++- pyTMD/__init__.py | 1 + pyTMD/output_otis_tides.py | 73 +++---- pyTMD/read_FES_model.py | 4 + pyTMD/read_GOT_model.py | 5 + pyTMD/read_netcdf_model.py | 4 + pyTMD/read_tide_model.py | 13 +- pyTMD/tidal_ellipse.py | 2 +- pyTMD/utilities.py | 45 ++-- scripts/reduce_OTIS_files.py | 195 ++++++++++-------- setup.py | 6 +- test/test_download_and_read.py | 101 +++++++++ 59 files changed, 390 insertions(+), 211 deletions(-) diff --git a/README.md b/README.md index 16ebc1c2..aa4234ef 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,12 @@ pyTMD ===== [![Language](https://img.shields.io/badge/python-v3.7-green.svg)](https://www.python.org/) -[![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/tsutterley/pyTMD/blob/master/LICENSE) +[![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/tsutterley/pyTMD/blob/main/LICENSE) [![PyPI Version](https://img.shields.io/pypi/v/pyTMD.svg)](https://pypi.python.org/pypi/pyTMD/) [![Documentation Status](https://readthedocs.org/projects/pytmd/badge/?version=latest)](https://pytmd.readthedocs.io/en/latest/?badge=latest) -[![codecov](https://codecov.io/gh/tsutterley/pyTMD/branch/master/graph/badge.svg)](https://codecov.io/gh/tsutterley/pyTMD) -[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tsutterley/pyTMD/master) -[![Binder](https://binder.pangeo.io/badge.svg)](https://binder.pangeo.io/v2/gh/tsutterley/pyTMD/master) +[![codecov](https://codecov.io/gh/tsutterley/pyTMD/branch/main/graph/badge.svg)](https://codecov.io/gh/tsutterley/pyTMD) +[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tsutterley/pyTMD/main) +[![Binder](https://binder.pangeo.io/badge.svg)](https://binder.pangeo.io/v2/gh/tsutterley/pyTMD/main) #### Python-based tidal prediction software that reads OTIS, GOT and FES formatted tidal solutions for calculating ocean and load tides @@ -57,7 +57,7 @@ L. Padman, M. R. Siegfried, H. A. Fricker, The program homepage is: https://github.com/tsutterley/pyTMD A zip archive of the latest version is available directly at: -https://github.com/tsutterley/pyTMD/archive/master.zip +https://github.com/tsutterley/pyTMD/archive/main.zip #### Software Matlab Tide Model Driver from Earth & Space Research is available at: diff --git a/doc/source/conf.py b/doc/source/conf.py index b96942b6..ed756d57 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -22,7 +22,7 @@ author = 'Tyler C. Sutterley' # The full version, including alpha/beta/rc tags -release = '1.0.2.12' +release = '1.0.2.13' # -- General configuration --------------------------------------------------- diff --git a/doc/source/getting_started/Getting-Started.md b/doc/source/getting_started/Getting-Started.md index 680d12ca..5f32b210 100644 --- a/doc/source/getting_started/Getting-Started.md +++ b/doc/source/getting_started/Getting-Started.md @@ -3,12 +3,12 @@ Getting Started #### Tide Model Formats OTIS and ATLAS formatted data use a single binary file to store all the constituents for either heights (`z`) or transports (`u`, `v`). -Arctic Ocean models can be downloaded from the NSF ArcticData server using the [`arcticdata_tides.py`](https://github.com/tsutterley/pyTMD/blob/master/scripts/arcticdata_tides.py) program. -CATS2008 can be downloaded from the US Antarctic Program (USAP) using the [`usap_cats_tides.py`](https://github.com/tsutterley/pyTMD/blob/master/scripts/usap_cats_tides.py) program. +Arctic Ocean models can be downloaded from the NSF ArcticData server using the [`arcticdata_tides.py`](https://github.com/tsutterley/pyTMD/blob/main/scripts/arcticdata_tides.py) program. +CATS2008 can be downloaded from the US Antarctic Program (USAP) using the [`usap_cats_tides.py`](https://github.com/tsutterley/pyTMD/blob/main/scripts/usap_cats_tides.py) program. ATLAS netCDF formatted data use netCDF4 files for each constituent and variable type (`z`, `u`, `v`). GOT formatted data use ascii files for each height constituent (`z`). FES formatted data use either ascii (1999, 2004) or netCDF4 (2012, 2014) files for each constituent and variable type (`z`, `u`, `v`). -The FES models can be downloaded using the [`aviso_fes_tides.py`](https://github.com/tsutterley/pyTMD/blob/master/scripts/aviso_fes_tides.py) program for users registered with AVISO. +The FES models can be downloaded using the [`aviso_fes_tides.py`](https://github.com/tsutterley/pyTMD/blob/main/scripts/aviso_fes_tides.py) program for users registered with AVISO. #### Directories pyTMD uses a tree structure for storing the tidal constituent data. @@ -45,14 +45,14 @@ This structure was chosen based on the different formats of each tide model. * [FES2014_load](https://www.aviso.altimetry.fr/data/products/auxiliary-products/global-tide-fes.html): `/fes2014/load_tide/` #### Programs -For users wanting to compute tide corrections for use with numpy arrays or pandas data structures, [`compute_tide_corrections.py`](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/compute_tide_corrections.py) is the place to start. It is a function that takes x, y, and time coordinates and computes the corresponding tidal elevation. +For users wanting to compute tide corrections for use with numpy arrays or pandas data structures, [`compute_tide_corrections.py`](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/compute_tide_corrections.py) is the place to start. It is a function that takes x, y, and time coordinates and computes the corresponding tidal elevation. ```python tide = compute_tide_corrections(x, y, delta_time, DIRECTORY=path_to_tide_models, MODEL='CATS2008', EPSG=3031, EPOCH=(2000,1,1,0,0,0), TYPE='drift', TIME='GPS', METHOD='spline', FILL_VALUE=np.nan) ``` -For users wanting to calculate tidal elevations or currents for a series of files, the [`compute_tidal_elevations.py`](https://github.com/tsutterley/pyTMD/blob/master/scripts/compute_tidal_elevations.py) and [`compute_tidal_currents.py`](https://github.com/tsutterley/pyTMD/blob/master/scripts/compute_tidal_currents.py) programs cover most use cases. They take an input file (in csv, netCDF4 or HDF5) and compute the tidal elevations or currents (zonal and meridonal) for each point. +For users wanting to calculate tidal elevations or currents for a series of files, the [`compute_tidal_elevations.py`](https://github.com/tsutterley/pyTMD/blob/main/scripts/compute_tidal_elevations.py) and [`compute_tidal_currents.py`](https://github.com/tsutterley/pyTMD/blob/main/scripts/compute_tidal_currents.py) programs cover most use cases. They take an input file (in csv, netCDF4 or HDF5) and compute the tidal elevations or currents (zonal and meridonal) for each point. ```bash python compute_tidal_elevations.py --directory= --tide=CATS2008 \ --format=HDF5 --variables=t_sec,lat,lon,h_cor --projection=4326 \ @@ -65,4 +65,4 @@ python compute_tidal_currents.py --directory= --tide=CATS20 input_file.H5 output_file.H5 ``` -There are specific programs for correcting NASA [Operation IceBridge](https://github.com/tsutterley/pyTMD/blob/master/scripts/compute_tides_icebridge_data.py), [ICESat-2 ATL03 geolocated photon](https://github.com/tsutterley/pyTMD/blob/master/scripts/compute_tides_ICESat2_ATL03.py), [ICESat-2 ATL06 land ice](https://github.com/tsutterley/pyTMD/blob/master/scripts/compute_tides_ICESat2_ATL06.py), [ICESat-2 ATL07 sea ice](https://github.com/tsutterley/pyTMD/blob/master/scripts/compute_tides_ICESat2_ATL07.py) and [ICESat-2 ATL12 ocean surface](https://github.com/tsutterley/pyTMD/blob/master/scripts/compute_tides_ICESat2_ATL12.py) data. +There are specific programs for correcting NASA [Operation IceBridge](https://github.com/tsutterley/pyTMD/blob/main/scripts/compute_tides_icebridge_data.py), [ICESat-2 ATL03 geolocated photon](https://github.com/tsutterley/pyTMD/blob/main/scripts/compute_tides_ICESat2_ATL03.py), [ICESat-2 ATL06 land ice](https://github.com/tsutterley/pyTMD/blob/main/scripts/compute_tides_ICESat2_ATL06.py), [ICESat-2 ATL07 sea ice](https://github.com/tsutterley/pyTMD/blob/main/scripts/compute_tides_ICESat2_ATL07.py) and [ICESat-2 ATL12 ocean surface](https://github.com/tsutterley/pyTMD/blob/main/scripts/compute_tides_ICESat2_ATL12.py) data. diff --git a/doc/source/getting_started/Install.md b/doc/source/getting_started/Install.md index 3d39645f..18052e81 100644 --- a/doc/source/getting_started/Install.md +++ b/doc/source/getting_started/Install.md @@ -2,7 +2,7 @@ Installation ============ Presently pyTMD is available for use as a [GitHub repository](https://github.com/tsutterley/pyTMD) and from the [Python Package Index (pypi)](https://pypi.org/project/pyTMD/). -The contents of the repository can be download from GitHub as a [zipped file](https://github.com/tsutterley/pyTMD/archive/master.zip) or cloned. +The contents of the repository can be download from GitHub as a [zipped file](https://github.com/tsutterley/pyTMD/archive/main.zip) or cloned. To use this repository from GitHub, please fork into your own account and then clone onto your system. ```bash git clone https://github.com/tsutterley/pyTMD.git @@ -21,4 +21,4 @@ To install from pypi using pip python3 -m pip install --user pyTMD ``` -Executable versions of this repository can also be tested using [Binder](https://mybinder.org/v2/gh/tsutterley/pyTMD/master) and [Pangeo](https://binder.pangeo.io/v2/gh/tsutterley/pyTMD/master). +Executable versions of this repository can also be tested using [Binder](https://mybinder.org/v2/gh/tsutterley/pyTMD/main) and [Pangeo](https://binder.pangeo.io/v2/gh/tsutterley/pyTMD/main). diff --git a/doc/source/user_guide/arcticdata_tides.md b/doc/source/user_guide/arcticdata_tides.md index cc29bcce..7ea96be9 100644 --- a/doc/source/user_guide/arcticdata_tides.md +++ b/doc/source/user_guide/arcticdata_tides.md @@ -10,7 +10,7 @@ arcticdata_tides.py ```bash python arcticdata_tides.py --directory= --tide=AOTIM-5-2018 ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/scripts/arcticdata_tides.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/scripts/arcticdata_tides.py) #### Command Line Options - `-D X`, `--directory=X`: Working Data Directory diff --git a/doc/source/user_guide/aviso_fes_tides.md b/doc/source/user_guide/aviso_fes_tides.md index d602713c..d4106499 100644 --- a/doc/source/user_guide/aviso_fes_tides.md +++ b/doc/source/user_guide/aviso_fes_tides.md @@ -9,7 +9,7 @@ aviso_fes_tides.py ```bash python aviso_fes_tides.py --directory= --tide=fes2014 ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/scripts/aviso_fes_tides.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/scripts/aviso_fes_tides.py) #### Command Line Options - `-D X`, `--directory=X`: Working Data Directory diff --git a/doc/source/user_guide/bilinear_interp.md b/doc/source/user_guide/bilinear_interp.md index 36f8484d..131cad73 100644 --- a/doc/source/user_guide/bilinear_interp.md +++ b/doc/source/user_guide/bilinear_interp.md @@ -8,7 +8,7 @@ bilinear_interp.py from pyTMD.bilinear_interp import bilinear_interp data = bilinear_interp(ilon,ilat,idata,lon,lat) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/bilinear_interp.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/bilinear_interp.py) #### Inputs 1. `ilon`: longitude of tidal model diff --git a/doc/source/user_guide/calc_astrol_longitudes.md b/doc/source/user_guide/calc_astrol_longitudes.md index 6a4bbf4a..3494bdec 100644 --- a/doc/source/user_guide/calc_astrol_longitudes.md +++ b/doc/source/user_guide/calc_astrol_longitudes.md @@ -10,7 +10,7 @@ calc_astrol_longitudes.py from pyTMD.calc_astrol_longitudes import calc_astrol_longitudes s,h,p,N,PP = calc_astrol_longitudes(MJD, ASTRO5=True) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/calc_astrol_longitudes.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/calc_astrol_longitudes.py) #### Inputs 1. `MJD`: Modified Julian Day of input date diff --git a/doc/source/user_guide/calc_delta_time.md b/doc/source/user_guide/calc_delta_time.md index 37b16966..cfeebe50 100644 --- a/doc/source/user_guide/calc_delta_time.md +++ b/doc/source/user_guide/calc_delta_time.md @@ -8,7 +8,7 @@ calc_delta_time.py from pyTMD.calc_delta_time import calc_delta_time deltat = calc_delta_time(delta_file, idays) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/calc_delta_time.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/calc_delta_time.py) #### Inputs 1. `delta_file`: diff --git a/doc/source/user_guide/compute_LPET_elevations.md b/doc/source/user_guide/compute_LPET_elevations.md index 6e4548d5..e1ab7cdf 100644 --- a/doc/source/user_guide/compute_LPET_elevations.md +++ b/doc/source/user_guide/compute_LPET_elevations.md @@ -8,7 +8,7 @@ compute_LPET_elevation.py ```bash python compute_LPET_elevation.py input_file output_file ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/compute_LPET_elevation.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/compute_LPET_elevation.py) #### Inputs 1. `input_file`: name of input file diff --git a/doc/source/user_guide/compute_LPET_icebridge_data.md b/doc/source/user_guide/compute_LPET_icebridge_data.md index 9a89f7ac..a2f61e38 100644 --- a/doc/source/user_guide/compute_LPET_icebridge_data.md +++ b/doc/source/user_guide/compute_LPET_icebridge_data.md @@ -8,7 +8,7 @@ compute_LPET_icebridge_data.py ```bash python compute_LPET_icebridge_data.py --verbose input_file ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/compute_LPET_icebridge_data.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/compute_LPET_icebridge_data.py) #### Inputs 1. `input_file`: input ATM1B, ATM icessn or LVIS file from NSIDC diff --git a/doc/source/user_guide/compute_LPT_displacements.md b/doc/source/user_guide/compute_LPT_displacements.md index a6c499ac..cc09e11b 100644 --- a/doc/source/user_guide/compute_LPT_displacements.md +++ b/doc/source/user_guide/compute_LPT_displacements.md @@ -9,7 +9,7 @@ compute_LPT_displacements.py ```bash python compute_LPT_displacements.py --directory= input_file output_file ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/scripts/compute_LPT_displacements.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/scripts/compute_LPT_displacements.py) #### Inputs 1. `input_file`: name of input file diff --git a/doc/source/user_guide/compute_LPT_icebridge_data.md b/doc/source/user_guide/compute_LPT_icebridge_data.md index b3bdee1a..a78c819f 100644 --- a/doc/source/user_guide/compute_LPT_icebridge_data.md +++ b/doc/source/user_guide/compute_LPT_icebridge_data.md @@ -9,7 +9,7 @@ compute_LPT_icebridge_data.py ```bash python compute_LPT_icebridge_data.py --directory= input_file ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/compute_LPT_icebridge_data.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/compute_LPT_icebridge_data.py) #### Inputs 1. `input_file`: input ATM1B, ATM icessn or LVIS file from NSIDC diff --git a/doc/source/user_guide/compute_OPT_displacements.md b/doc/source/user_guide/compute_OPT_displacements.md index 932963bf..15c89d66 100644 --- a/doc/source/user_guide/compute_OPT_displacements.md +++ b/doc/source/user_guide/compute_OPT_displacements.md @@ -9,7 +9,7 @@ compute_OPT_displacements.py ```bash python compute_OPT_displacements.py --directory= input_file output_file ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/scripts/compute_OPT_displacements.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/scripts/compute_OPT_displacements.py) #### Inputs 1. `input_file`: name of input file diff --git a/doc/source/user_guide/compute_OPT_icebridge_data.md b/doc/source/user_guide/compute_OPT_icebridge_data.md index 363df6e9..81f7b010 100644 --- a/doc/source/user_guide/compute_OPT_icebridge_data.md +++ b/doc/source/user_guide/compute_OPT_icebridge_data.md @@ -9,7 +9,7 @@ compute_OPT_icebridge_data.py ```bash python compute_OPT_icebridge_data.py --directory= input_file ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/scripts/compute_OPT_icebridge_data.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/scripts/compute_OPT_icebridge_data.py) #### Inputs 1. `input_file`: input ATM1B, ATM icessn or LVIS file from NSIDC diff --git a/doc/source/user_guide/compute_equilibrium_tide.md b/doc/source/user_guide/compute_equilibrium_tide.md index d9775c03..cfb9656e 100644 --- a/doc/source/user_guide/compute_equilibrium_tide.md +++ b/doc/source/user_guide/compute_equilibrium_tide.md @@ -9,7 +9,7 @@ compute_equilibrium_tide.py from pyTMD.compute_equilibrium_tide import compute_equilibrium_tide lpet = compute_equilibrium_tide(time,lat) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/compute_equilibrium_tide.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/compute_equilibrium_tide.py) #### Inputs 1. `t`: days relative to Jan 1, 1992 (48622mjd) diff --git a/doc/source/user_guide/compute_tidal_currents.md b/doc/source/user_guide/compute_tidal_currents.md index 60029513..cdef3d39 100644 --- a/doc/source/user_guide/compute_tidal_currents.md +++ b/doc/source/user_guide/compute_tidal_currents.md @@ -9,7 +9,7 @@ compute_tidal_currents.py ```bash python compute_tidal_currents.py --directory= --tide= input_file output_file ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/scripts/compute_tidal_currents.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/scripts/compute_tidal_currents.py) #### Inputs 1. `input_file`: name of input file diff --git a/doc/source/user_guide/compute_tidal_elevations.md b/doc/source/user_guide/compute_tidal_elevations.md index d34ffe86..dfe7fb9e 100644 --- a/doc/source/user_guide/compute_tidal_elevations.md +++ b/doc/source/user_guide/compute_tidal_elevations.md @@ -10,7 +10,7 @@ compute_tidal_elevations.py ```bash python compute_tidal_elevations.py --directory= --tide= input_file output_file ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/scripts/compute_tidal_elevations.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/scripts/compute_tidal_elevations.py) #### Inputs 1. `input_file`: name of input file diff --git a/doc/source/user_guide/compute_tide_corrections.md b/doc/source/user_guide/compute_tide_corrections.md index b505e696..101ec809 100644 --- a/doc/source/user_guide/compute_tide_corrections.md +++ b/doc/source/user_guide/compute_tide_corrections.md @@ -12,7 +12,7 @@ from pyTMD.compute_tide_corrections import compute_tide_corrections tide = compute_tide_corrections(x, y, delta_time, DIRECTORY=DIRECTORY, MODEL=MODEL, EPOCH=(2000,1,1,0,0,0), EPSG=3031, TYPE='drift') ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/compute_tide_corrections.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/compute_tide_corrections.py) #### Inputs 1. `x`: x-coordinates in projection EPSG diff --git a/doc/source/user_guide/compute_tides_ICESat2_ATL03.md b/doc/source/user_guide/compute_tides_ICESat2_ATL03.md index ac527224..d52f2b1a 100644 --- a/doc/source/user_guide/compute_tides_ICESat2_ATL03.md +++ b/doc/source/user_guide/compute_tides_ICESat2_ATL03.md @@ -10,7 +10,7 @@ compute_tides_ICESat2_ATL03.py ```bash python compute_tides_ICESat2_ATL03.py --directory= --tide= input_file ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/scripts/compute_tides_ICESat2_ATL03.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/scripts/compute_tides_ICESat2_ATL03.py) #### Inputs 1. `input_file`: input ICESat-2 ATL03 file diff --git a/doc/source/user_guide/compute_tides_ICESat2_ATL06.md b/doc/source/user_guide/compute_tides_ICESat2_ATL06.md index 7d979b39..b23f41f5 100644 --- a/doc/source/user_guide/compute_tides_ICESat2_ATL06.md +++ b/doc/source/user_guide/compute_tides_ICESat2_ATL06.md @@ -10,7 +10,7 @@ compute_tides_ICESat2_ATL06.py ```bash python compute_tides_ICESat2_ATL06.py --directory= --tide= input_file ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/scripts/compute_tides_ICESat2_ATL06.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/scripts/compute_tides_ICESat2_ATL06.py) #### Inputs 1. `input_file`: input ICESat-2 ATL06 file diff --git a/doc/source/user_guide/compute_tides_ICESat2_ATL07.md b/doc/source/user_guide/compute_tides_ICESat2_ATL07.md index cd3f857f..0107eb87 100644 --- a/doc/source/user_guide/compute_tides_ICESat2_ATL07.md +++ b/doc/source/user_guide/compute_tides_ICESat2_ATL07.md @@ -10,7 +10,7 @@ compute_tides_ICESat2_ATL07.py ```bash python compute_tides_ICESat2_ATL07.py --directory= --tide= input_file ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/scripts/compute_tides_ICESat2_ATL07.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/scripts/compute_tides_ICESat2_ATL07.py) #### Inputs 1. `input_file`: input ICESat-2 ATL07 file diff --git a/doc/source/user_guide/compute_tides_ICESat2_ATL12.md b/doc/source/user_guide/compute_tides_ICESat2_ATL12.md index dd342df8..68c15652 100644 --- a/doc/source/user_guide/compute_tides_ICESat2_ATL12.md +++ b/doc/source/user_guide/compute_tides_ICESat2_ATL12.md @@ -10,7 +10,7 @@ compute_tides_ICESat2_ATL12.py ```bash python compute_tides_ICESat2_ATL12.py --directory= --tide= input_file ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/scripts/compute_tides_ICESat2_ATL12.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/scripts/compute_tides_ICESat2_ATL12.py) #### Inputs 1. `input_file`: input ICESat-2 ATL12 file diff --git a/doc/source/user_guide/compute_tides_icebridge_data.md b/doc/source/user_guide/compute_tides_icebridge_data.md index 27eeb4ef..636c23ed 100644 --- a/doc/source/user_guide/compute_tides_icebridge_data.md +++ b/doc/source/user_guide/compute_tides_icebridge_data.md @@ -10,7 +10,7 @@ compute_tides_icebridge_data.py ```bash python compute_tides_icebridge_data.py --directory= --tide= input_file ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/scripts/compute_tides_icebridge_data.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/scripts/compute_tides_icebridge_data.py) #### Inputs 1. `input_file`: input ATM1B, ATM icessn or LVIS file from NSIDC diff --git a/doc/source/user_guide/convert_calendar_decimal.md b/doc/source/user_guide/convert_calendar_decimal.md index c70d6a75..550fd98a 100644 --- a/doc/source/user_guide/convert_calendar_decimal.md +++ b/doc/source/user_guide/convert_calendar_decimal.md @@ -9,7 +9,7 @@ from pyTMD.convert_calendar_decimal import convert_calendar_decimal t_date = convert_calendar_decimal(year, month, DAY=day, \ HOUR=hour, MINUTE=minute, SECOND=second) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/convert_calendar_decimal.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/convert_calendar_decimal.py) #### Inputs 1. `year`: calendar year diff --git a/doc/source/user_guide/convert_julian.md b/doc/source/user_guide/convert_julian.md index 43ae1f30..1360cdf0 100644 --- a/doc/source/user_guide/convert_julian.md +++ b/doc/source/user_guide/convert_julian.md @@ -8,7 +8,7 @@ convert_julian.py from pyTMD.convert_julian import convert_julian YEAR,MONTH,DAY,HOUR,MINUTE,SECOND = convert_julian(JD, FORMAT='tuple') ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/convert_julian.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/convert_julian.py) #### Inputs 1. `JD`: Julian Day of the specified calendar date (days since -4712-01-01T12:00:00) diff --git a/doc/source/user_guide/convert_ll_xy.md b/doc/source/user_guide/convert_ll_xy.md index 187327c2..f1352ff9 100644 --- a/doc/source/user_guide/convert_ll_xy.md +++ b/doc/source/user_guide/convert_ll_xy.md @@ -9,7 +9,7 @@ from pyTMD.convert_ll_xy import convert_ll_xy x,y = convert_ll_xy(lon,lat,PROJ,'F') lon,lat = convert_ll_xy(x,y,PROJ,'B') ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/convert_ll_xy.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/convert_ll_xy.py) #### Inputs 1. `i1`: longitude ('F') or projection easting x ('B') diff --git a/doc/source/user_guide/count_leap_seconds.md b/doc/source/user_guide/count_leap_seconds.md index e7baf41d..595a123d 100644 --- a/doc/source/user_guide/count_leap_seconds.md +++ b/doc/source/user_guide/count_leap_seconds.md @@ -9,7 +9,7 @@ count_leap_seconds.py from pyTMD.count_leap_seconds import count_leap_seconds n_leaps = count_leap_seconds(GPS_Time) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/count_leap_seconds.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/count_leap_seconds.py) #### Inputs 1. `GPS_Time`: seconds since January 6, 1980 at 00:00:00 diff --git a/doc/source/user_guide/iers_mean_pole.md b/doc/source/user_guide/iers_mean_pole.md index f6383044..0fa72990 100644 --- a/doc/source/user_guide/iers_mean_pole.md +++ b/doc/source/user_guide/iers_mean_pole.md @@ -10,7 +10,7 @@ iers_mean_pole.py from pyTMD.iers_mean_pole import iers_mean_pole x,y,flag = iers_mean_pole(input_file,input_epoch,version,FILL_VALUE=FILL_VALUE) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/iers_mean_pole.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/iers_mean_pole.py) #### Inputs 1. `input_file`: full path to mean-pole.tab file provided by IERS diff --git a/doc/source/user_guide/infer_minor_corrections.md b/doc/source/user_guide/infer_minor_corrections.md index 27909d3f..77750a93 100644 --- a/doc/source/user_guide/infer_minor_corrections.md +++ b/doc/source/user_guide/infer_minor_corrections.md @@ -10,7 +10,7 @@ from pyTMD.infer_minor_corrections import infer_minor_corrections dh = infer_minor_corrections(t, zmajor, constituents, DELTAT=DELTAT, CORRECTIONS=CORRECTIONS) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/infer_minor_corrections.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/infer_minor_corrections.py) #### Inputs 1. `t`: days relative to Jan 1, 1992 (48622mjd) diff --git a/doc/source/user_guide/load_constituent.md b/doc/source/user_guide/load_constituent.md index 087781ce..503c6bdb 100644 --- a/doc/source/user_guide/load_constituent.md +++ b/doc/source/user_guide/load_constituent.md @@ -8,7 +8,7 @@ load_constituent.py from pyTMD.load_constituent import load_constituent amplitude,phase,omega,alpha,species = load_constituent(c) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/load_constituent.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/load_constituent.py) #### Inputs 1. `c`: tidal constituent IDs diff --git a/doc/source/user_guide/load_nodal_corrections.md b/doc/source/user_guide/load_nodal_corrections.md index d51d24bd..d98df67d 100644 --- a/doc/source/user_guide/load_nodal_corrections.md +++ b/doc/source/user_guide/load_nodal_corrections.md @@ -9,7 +9,7 @@ load_nodal_corrections.py from pyTMD.load_nodal_corrections import load_nodal_corrections pu,pf,G = load_nodal_corrections(MJD,constituents) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/load_nodal_corrections.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/load_nodal_corrections.py) #### Inputs 1. `MJD`: Modified Julian Day of input date diff --git a/doc/source/user_guide/output_otis_tides.md b/doc/source/user_guide/output_otis_tides.md index d1e7c0d3..c7a73d80 100644 --- a/doc/source/user_guide/output_otis_tides.md +++ b/doc/source/user_guide/output_otis_tides.md @@ -11,7 +11,7 @@ output_otis_grid(grid_file,xlim,ylim,hz,mz,iob,dt) output_otis_elevation(elevation_file,h,xlim,ylim,constituents) output_otis_transport(transport_file,u,v,xlim,ylim,constituents) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/output_otis_tides.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/output_otis_tides.py) #### Inputs - `output_otis_grid()` diff --git a/doc/source/user_guide/predict_tidal_ts.md b/doc/source/user_guide/predict_tidal_ts.md index bd173b0a..e0efb108 100644 --- a/doc/source/user_guide/predict_tidal_ts.md +++ b/doc/source/user_guide/predict_tidal_ts.md @@ -9,7 +9,7 @@ predict_tidal_ts.py from pyTMD.predict_tidal_ts import predict_tidal_ts ht = predict_tidal_ts(time,hc,con) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/predict_tidal_ts.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/predict_tidal_ts.py) #### Inputs 1. `time`: days relative to Jan 1, 1992 (48622mjd) diff --git a/doc/source/user_guide/predict_tide.md b/doc/source/user_guide/predict_tide.md index efe0dc3b..ca490f98 100644 --- a/doc/source/user_guide/predict_tide.md +++ b/doc/source/user_guide/predict_tide.md @@ -9,7 +9,7 @@ predict_tide.py from pyTMD.predict_tide import predict_tide ht = predict_tide(time,hc,con) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/predict_tide.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/predict_tide.py) #### Inputs 1. `time`: days relative to Jan 1, 1992 (48622mjd) diff --git a/doc/source/user_guide/predict_tide_drift.md b/doc/source/user_guide/predict_tide_drift.md index 69af69ac..fed74e2e 100644 --- a/doc/source/user_guide/predict_tide_drift.md +++ b/doc/source/user_guide/predict_tide_drift.md @@ -9,7 +9,7 @@ predict_tide_drift.py from pyTMD.predict_tide_drift import predict_tide_drift ht = predict_tide_drift(time,hc,con) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/predict_tide_drift.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/predict_tide_drift.py) #### Inputs 1. `time`: days relative to Jan 1, 1992 (48622mjd) diff --git a/doc/source/user_guide/read_FES_model.md b/doc/source/user_guide/read_FES_model.md index ce9a14a3..186e248e 100644 --- a/doc/source/user_guide/read_FES_model.md +++ b/doc/source/user_guide/read_FES_model.md @@ -10,7 +10,7 @@ from pyTMD.read_FES_model import read_FES_model amp,ph = read_FES_model(ilon,ilat,directory,model_files,TYPE=type, VERSION=version,METHOD='spline',GZIP=True,SCALE=1.0/100.0) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/read_FES_model.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/read_FES_model.py) #### Inputs 1. `ilon`: longitude to interpolate diff --git a/doc/source/user_guide/read_GOT_model.md b/doc/source/user_guide/read_GOT_model.md index 8c48f1f9..5e536f17 100644 --- a/doc/source/user_guide/read_GOT_model.md +++ b/doc/source/user_guide/read_GOT_model.md @@ -9,7 +9,7 @@ read_GOT_model.py from pyTMD.read_GOT_model import read_GOT_model amp,ph = read_GOT_model(ilon,ilat,directory,model_files,METHOD='spline') ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/read_GOT_model.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/read_GOT_model.py) #### Inputs 1. `ilon`: longitude to interpolate diff --git a/doc/source/user_guide/read_iers_EOP.md b/doc/source/user_guide/read_iers_EOP.md index 0e721812..6383baa6 100644 --- a/doc/source/user_guide/read_iers_EOP.md +++ b/doc/source/user_guide/read_iers_EOP.md @@ -9,7 +9,7 @@ read_iers_EOP.py from pyTMD.read_iers_EOP import read_iers_EOP MJD,x,y,flag = read_iers_EOP(input_file) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/read_iers_EOP.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/read_iers_EOP.py) #### Inputs 1. `input_file`: full path to IERS EOP "finals" file diff --git a/doc/source/user_guide/read_netcdf_model.md b/doc/source/user_guide/read_netcdf_model.md index 5b49d580..43d1a46b 100644 --- a/doc/source/user_guide/read_netcdf_model.md +++ b/doc/source/user_guide/read_netcdf_model.md @@ -9,7 +9,7 @@ read_netcdf_model.py from pyTMD.read_netcdf_model import read_netcdf_model amp,ph,D,c = read_netcdf_model(ilon,ilat,directory,model_files,type,METHOD='spline') ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/read_netcdf_model.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/read_netcdf_model.py) #### Inputs 1. `ilon`: longitude to interpolate diff --git a/doc/source/user_guide/read_ocean_pole_tide.md b/doc/source/user_guide/read_ocean_pole_tide.md index 9dac829b..7fdb973a 100644 --- a/doc/source/user_guide/read_ocean_pole_tide.md +++ b/doc/source/user_guide/read_ocean_pole_tide.md @@ -12,7 +12,7 @@ from pyTMD.read_ocean_pole_tide import read_ocean_pole_tide ocean_pole_tide_file = get_data_path(['data','opoleloadcoefcmcor.txt.gz']) ur,un,ue,glon,glat = read_ocean_pole_tide(ocean_pole_tide_file) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/read_ocean_pole_tide.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/read_ocean_pole_tide.py) #### Inputs 1. `input_file`: [IERS 0.5x0.5 map of ocean pole tide coefficients](ftp://maia.usno.navy.mil/conventions/2010/2010_update/chapter7/additional_info/opoleloadcoefcmcor.txt.gz) diff --git a/doc/source/user_guide/read_tide_model.md b/doc/source/user_guide/read_tide_model.md index 6fd4539d..a473f1e3 100644 --- a/doc/source/user_guide/read_tide_model.md +++ b/doc/source/user_guide/read_tide_model.md @@ -10,7 +10,7 @@ from pyTMD.read_tide_model import read_tide_model amp,ph,D,c = read_tide_model(ilon, ilat, grid_file, model_file, EPSG, TYPE=type, METHOD='spline', GRID='OTIS') ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/read_tide_model.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/read_tide_model.py) #### Inputs 1. `ilon`: longitude to interpolate diff --git a/doc/source/user_guide/reduce_OTIS_files.md b/doc/source/user_guide/reduce_OTIS_files.md index 2e58fa31..07ca11b5 100644 --- a/doc/source/user_guide/reduce_OTIS_files.md +++ b/doc/source/user_guide/reduce_OTIS_files.md @@ -6,14 +6,13 @@ reduce_OTIS_files.py #### Calling Sequence ```bash python reduce_OTIS_files.py --directory= --tide= \ - --bounds= + --bounds= --projection=3031 ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/scripts/reduce_OTIS_files.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/scripts/reduce_OTIS_files.py) #### Command Line Options - `-D X`, `--directory=X`: Working data directory - `-B X`, `--bounds=X`: Grid Bounds (xmin,xmax,ymin,ymax) - - `--date=X`: date to forecast in ISO format (YYYY-MM-DD) - `-T X`, `--tide=X`: Tide model to use * CATS0201 * CATS2008 @@ -26,10 +25,6 @@ python reduce_OTIS_files.py --directory= --tide= \ * AODTM-5 * AOTIM-5 * AOTIM-5-2018 - * GOT4.7 - * GOT4.7_load - * GOT4.8 - * GOT4.8_load - * GOT4.10 - * GOT4.10_load + - `--projection=X`: spatial projection of bounds as EPSG code or PROJ4 string + * 4326: latitude and longitude coordinates on WGS84 reference ellipsoid - `-M X`, `--mode=X`: Permission mode of output file diff --git a/doc/source/user_guide/spatial.rst b/doc/source/user_guide/spatial.rst index b1eca646..0f2a03b6 100644 --- a/doc/source/user_guide/spatial.rst +++ b/doc/source/user_guide/spatial.rst @@ -26,7 +26,7 @@ Reading a HDF5 file `Source code`__ -.. __: https://github.com/tsutterley/pyTMD/blob/master/pyTMD/spatial.py +.. __: https://github.com/tsutterley/pyTMD/blob/main/pyTMD/spatial.py General Methods =============== diff --git a/doc/source/user_guide/tidal_ellipse.md b/doc/source/user_guide/tidal_ellipse.md index 7f402626..8c176964 100644 --- a/doc/source/user_guide/tidal_ellipse.md +++ b/doc/source/user_guide/tidal_ellipse.md @@ -8,7 +8,7 @@ tidal_ellipse.py from pyTMD.tidal_ellipse import tidal_ellipse umajor,uminor,uincl,uphase = tidal_ellipse(u,v) ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/pyTMD/tidal_ellipse.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/pyTMD/tidal_ellipse.py) #### Inputs 1. `u`: zonal current (EW) diff --git a/doc/source/user_guide/time.rst b/doc/source/user_guide/time.rst index 76e886f3..d8b1f524 100644 --- a/doc/source/user_guide/time.rst +++ b/doc/source/user_guide/time.rst @@ -38,7 +38,7 @@ Convert a calendar date into Modified Julian Days `Source code`__ -.. __: https://github.com/tsutterley/pyTMD/blob/master/pyTMD/time.py +.. __: https://github.com/tsutterley/pyTMD/blob/main/pyTMD/time.py General Methods diff --git a/doc/source/user_guide/usap_cats_tides.md b/doc/source/user_guide/usap_cats_tides.md index 7934217f..42be6573 100644 --- a/doc/source/user_guide/usap_cats_tides.md +++ b/doc/source/user_guide/usap_cats_tides.md @@ -8,7 +8,7 @@ usap_cats_tides.py ```bash python usap_cats_tides.py --directory= --tide=CATS2008 ``` -[Source code](https://github.com/tsutterley/pyTMD/blob/master/scripts/usap_cats_tides.py) +[Source code](https://github.com/tsutterley/pyTMD/blob/main/scripts/usap_cats_tides.py) #### Command Line Options - `-D X`, `--directory=X`: Working Data Directory diff --git a/doc/source/user_guide/utilities.rst b/doc/source/user_guide/utilities.rst index 58cffb29..ef29f468 100644 --- a/doc/source/user_guide/utilities.rst +++ b/doc/source/user_guide/utilities.rst @@ -10,7 +10,7 @@ Download and management utilities for syncing time and auxiliary files `Source code`__ -.. __: https://github.com/tsutterley/pyTMD/blob/master/pyTMD/utilities.py +.. __: https://github.com/tsutterley/pyTMD/blob/main/pyTMD/utilities.py General Methods @@ -154,6 +154,31 @@ General Methods `mode`: permissions mode of output local file +.. method:: pyTMD.utilities.build_opener(username, password, context=ssl.SSLContext(ssl.PROTOCOL_TLS), password_manager=True, get_ca_certs=True, redirect=True, authorization_header=False, urs='https://urs.earthdata.nasa.gov') + + build urllib opener for NASA Earthdata with supplied credentials + + Arguments: + + `username`: NASA Earthdata username + + `password`: NASA Earthdata password + + Keyword arguments: + + `context`: SSL context for opener object + + `password_manager`: create password manager context using default realm + + `get_ca_certs`: get list of loaded “certification authority” certificates + + `redirect`: create redirect handler object + + `authorization_header`: add base64 encoded authorization header to opener + + `urs`: Earthdata login URS 3 host + + .. method:: pyTMD.utilities.check_credentials() Check that entered NASA Earthdata credentials are valid diff --git a/pyTMD/__init__.py b/pyTMD/__init__.py index 478305fd..4a8a00b2 100644 --- a/pyTMD/__init__.py +++ b/pyTMD/__init__.py @@ -30,6 +30,7 @@ from pyTMD.predict_tidal_ts import predict_tidal_ts from pyTMD.predict_tide_drift import predict_tide_drift from pyTMD.predict_tide import predict_tide +from pyTMD.tidal_ellipse import tidal_ellipse from pyTMD.calc_iers_mean_pole import calc_iers_mean_pole from pyTMD.iers_mean_pole import iers_mean_pole from pyTMD.read_iers_EOP import read_iers_EOP diff --git a/pyTMD/output_otis_tides.py b/pyTMD/output_otis_tides.py index 1b55974d..a909c624 100644 --- a/pyTMD/output_otis_tides.py +++ b/pyTMD/output_otis_tides.py @@ -1,7 +1,7 @@ #!/usr/bin/env python u""" output_otis_tides.py -Written by Tyler Sutterley (07/2020) +Written by Tyler Sutterley (09/2020) Writes OTIS-format tide files for use in the tide program http://volkov.oce.orst.edu/tides/region.html https://www.esr.org/research/polar-tide-models/list-of-polar-tide-models/ @@ -13,6 +13,7 @@ https://numpy.org/doc/stable/user/numpy-for-matlab-users.html UPDATE HISTORY: + Updated 09/2020: python3 compatibility updates for struct and utf-8 encoding Updated 07/2020: added function docstrings Written 08/2018 """ @@ -40,34 +41,34 @@ def output_otis_grid(grid_file,xlim,ylim,hz,mz,iob,dt): nob = len(iob) ny,nx = np.shape(hz) reclen = 32 - fid.write(struct.pack('>i4',reclen)) - fid.write(struct.pack('>i4',nx)) - fid.write(struct.pack('>i4',ny)) + fid.write(struct.pack('>i',reclen)) + fid.write(struct.pack('>i',nx)) + fid.write(struct.pack('>i',ny)) ylim.tofile(fid,format='>f4') xlim.tofile(fid,format='>f4') - fid.write(struct.pack('>f4',dt)) - fid.write(struct.pack('>i4',nob)) - fid.write(struct.pack('>i4',reclen)) + fid.write(struct.pack('>f',dt)) + fid.write(struct.pack('>i',nob)) + fid.write(struct.pack('>i',reclen)) if (nob == 0): - fid.write(struct.pack('>i4',4)) - fid.write(struct.pack('>i4',0)) - fid.write(struct.pack('>i4',4)) + fid.write(struct.pack('>i',4)) + fid.write(struct.pack('>i',0)) + fid.write(struct.pack('>i',4)) else: reclen = 8*nob - fid.write(struct.pack('>i4',reclen)) + fid.write(struct.pack('>i',reclen)) iob.tofile(fid,format='>i4') - fid.write(struct.pack('>i4',reclen)) + fid.write(struct.pack('>i',reclen)) reclen = 4*nx*ny #-- write depth and mask data to file - fid.write(struct.pack('>i4',reclen)) + fid.write(struct.pack('>i',reclen)) hz.tofile(fid,format='>f4') for m in range(ny): hz[m,:].tofile(fid,format='>f4') - fid.write(struct.pack('>i4',reclen)) - fid.write(struct.pack('>i4',reclen)) + fid.write(struct.pack('>i',reclen)) + fid.write(struct.pack('>i',reclen)) for m in range(ny): mz[m,:].tofile(fid,format='>i4') - fid.write(struct.pack('>i4',reclen)) + fid.write(struct.pack('>i',reclen)) #-- close the output OTIS file fid.close() @@ -86,27 +87,27 @@ def output_otis_elevation(elevation_file,h,xlim,ylim,constituents): """ fid = open(elevation_file,'wb') ny,nx,nc = np.shape(h) - #-- length of header: allow for 4 character >i4 c_id strings + #-- length of header: allow for 4 character >i c_id strings header_length = 4*(7 + nc) - fid.write(struct.pack('>i4',header_length)) - fid.write(struct.pack('>i4',nx)) - fid.write(struct.pack('>i4',ny)) - fid.write(struct.pack('>i4',nc)) + fid.write(struct.pack('>i',header_length)) + fid.write(struct.pack('>i',nx)) + fid.write(struct.pack('>i',ny)) + fid.write(struct.pack('>i',nc)) ylim.tofile(fid,format='>f4') xlim.tofile(fid,format='>f4') for c in constituents: - fid.write('{0:4}'.format(c)) - fid.write(struct.pack('>i4',header_length)) + fid.write(c.ljust(4).encode('utf-8')) + fid.write(struct.pack('>i',header_length)) #-- write each constituent to file constituent_header = 8*nx*ny for ic in range(nc): - fid.write(struct.pack('>i4',constituent_header)) + fid.write(struct.pack('>i',constituent_header)) for m in range(ny): - temp = np.zeros((2*nx),dtype='>f4') + temp = np.zeros((2*nx),dtype='>f') temp[0:2*nx-1:2] = h.real[m,:,ic] temp[1:2*nx:2] = h.imag[m,:,ic] temp.tofile(fid,format='>f4') - fid.write(struct.pack('>i4',constituent_header)) + fid.write(struct.pack('>i',constituent_header)) #-- close the output OTIS file fid.close() @@ -126,28 +127,28 @@ def output_otis_transport(transport_file,u,v,xlim,ylim,constituents): """ fid = open(transport_file,'wb') ny,nx,nc = np.shape(u) - #-- length of header: allow for 4 character >i4 c_id strings + #-- length of header: allow for 4 character >i c_id strings header_length = 4*(7 + nc) - fid.write(struct.pack('>i4',header_length)) - fid.write(struct.pack('>i4',nx)) - fid.write(struct.pack('>i4',ny)) - fid.write(struct.pack('>i4',nc)) + fid.write(struct.pack('>i',header_length)) + fid.write(struct.pack('>i',nx)) + fid.write(struct.pack('>i',ny)) + fid.write(struct.pack('>i',nc)) ylim.tofile(fid,format='>f4') xlim.tofile(fid,format='>f4') for c in constituents: - fid.write('{0:4}'.format(c)) - fid.write(struct.pack('>i4',header_length)) + fid.write(c.ljust(4).encode('utf-8')) + fid.write(struct.pack('>i',header_length)) #-- write each constituent to file constituent_header = 2*8*nx*ny for ic in range(nc): - fid.write(struct.pack('>i4',constituent_header)) + fid.write(struct.pack('>i',constituent_header)) for m in range(ny): - temp = np.zeros((4*nx),dtype='>f4') + temp = np.zeros((4*nx),dtype='>f') temp[0:4*nx-3:4] = u.real[m,:,ic] temp[1:4*nx-2:4] = u.imag[m,:,ic] temp[2:4*nx-1:4] = v.real[m,:,ic] temp[3:4*nx:4] = v.imag[m,:,ic] temp.tofile(fid,format='>f4') - fid.write(struct.pack('>i4',constituent_header)) + fid.write(struct.pack('>i',constituent_header)) #-- close the output OTIS file fid.close() diff --git a/pyTMD/read_FES_model.py b/pyTMD/read_FES_model.py index 02ee5271..2829b211 100644 --- a/pyTMD/read_FES_model.py +++ b/pyTMD/read_FES_model.py @@ -51,6 +51,7 @@ UPDATE HISTORY: Updated 09/2020: set bounds error to false for regular grid interpolations + adjust dimensions of input coordinates to be iterable Updated 08/2020: replaced griddata with scipy regular grid interpolators Written 07/2020 """ @@ -101,6 +102,9 @@ def extract_FES_constants(ilon, ilat, directory, model_files, phase: phases of tidal constituents """ + #-- adjust dimensions of input coordinates to be iterable + ilon = np.atleast_1d(ilon) + ilat = np.atleast_1d(ilat) #-- adjust longitudinal convention of input latitude and longitude #-- to fit tide model convention if (np.min(ilon) < 0.0): diff --git a/pyTMD/read_GOT_model.py b/pyTMD/read_GOT_model.py index c606c28c..ad05183c 100644 --- a/pyTMD/read_GOT_model.py +++ b/pyTMD/read_GOT_model.py @@ -36,6 +36,7 @@ UPDATE HISTORY: Updated 09/2020: set bounds error to false for regular grid interpolations + adjust dimensions of input coordinates to be iterable Updated 08/2020: replaced griddata with scipy regular grid interpolators Updated 07/2020: added function docstrings. separate bilinear interpolation update griddata interpolation. add option GZIP for compression @@ -85,6 +86,10 @@ def extract_GOT_constants(ilon, ilat, directory, model_files, amplitude: amplitudes of tidal constituents phase: phases of tidal constituents """ + + #-- adjust dimensions of input coordinates to be iterable + ilon = np.atleast_1d(ilon) + ilat = np.atleast_1d(ilat) #-- adjust longitudinal convention of input latitude and longitude #-- to fit tide model convention if (np.min(ilon) < 0.0): diff --git a/pyTMD/read_netcdf_model.py b/pyTMD/read_netcdf_model.py index 5a62d3a5..e0740c21 100644 --- a/pyTMD/read_netcdf_model.py +++ b/pyTMD/read_netcdf_model.py @@ -52,6 +52,7 @@ UPDATE HISTORY: Updated 09/2020: set bounds error to false for regular grid interpolations + adjust dimensions of input coordinates to be iterable Updated 08/2020: replaced griddata with scipy regular grid interpolators Updated 07/2020: added function docstrings. separate bilinear interpolation changed TYPE variable to keyword argument. update griddata interpolation @@ -152,6 +153,9 @@ def extract_netcdf_constants(ilon, ilat, directory, grid_file, model_files, #-- create meshes from latitude and longitude gridlon,gridlat = np.meshgrid(lon,lat) + #-- adjust dimensions of input coordinates to be iterable + ilon = np.atleast_1d(ilon) + ilat = np.atleast_1d(ilat) #-- adjust longitudinal convention of input latitude and longitude #-- to fit tide model convention lt0, = np.nonzero(ilon < 0) diff --git a/pyTMD/read_tide_model.py b/pyTMD/read_tide_model.py index 3f288399..f3b4eda8 100644 --- a/pyTMD/read_tide_model.py +++ b/pyTMD/read_tide_model.py @@ -51,6 +51,7 @@ UPDATE HISTORY: Updated 09/2020: set bounds error to false for regular grid interpolations + adjust dimensions of input coordinates to be iterable Updated 08/2020: check that interpolated points are within range of model replaced griddata interpolation with scipy regular grid interpolators Updated 07/2020: added function docstrings. separate bilinear interpolation @@ -109,7 +110,6 @@ def extract_tidal_constants(ilon, ilat, grid_file, model_file, EPSG, TYPE='z', D: bathymetry of tide model constituents: list of model constituents """ - #-- read the OTIS-format tide grid file if (GRID == 'ATLAS'): #-- if reading a global solution with localized solutions @@ -117,10 +117,11 @@ def extract_tidal_constants(ilon, ilat, grid_file, model_file, EPSG, TYPE='z', xi,yi,hz = combine_atlas_model(x0,y0,hz0,pmask,local,VARIABLE='depth') mz = create_atlas_mask(x0,y0,mz0,local,VARIABLE='depth') else: - #-- if reading a pure global solution + #-- if reading a single OTIS solution xi,yi,hz,mz,iob,dt = read_tide_grid(grid_file) + #-- adjust dimensions of input coordinates to be iterable #-- run wrapper function to convert coordinate systems of input lat/lon - x,y = convert_ll_xy(ilon,ilat,EPSG,'F') + x,y = convert_ll_xy(np.atleast_1d(ilon),np.atleast_1d(ilat),EPSG,'F') invalid = (x < xi.min()) | (x > xi.max()) | (y < yi.min()) | (y > yi.max()) #-- grid step size of tide model dx = xi[1] - xi[0] @@ -142,12 +143,10 @@ def extract_tidal_constants(ilon, ilat, grid_file, model_file, EPSG, TYPE='z', #-- adjust longitudinal convention of input latitude and longitude #-- to fit tide model convention - xmin = np.min(x) - xmax = np.max(y) - if (xmin < xi[0]) & (EPSG == '4326'): + if (np.min(x) < np.min(xi)) & (EPSG == '4326'): lt0, = np.nonzero(x < 0) x[lt0] += 360.0 - if (xmax > xi[-1]) & (EPSG == '4326'): + if (np.max(x) > np.max(xi)) & (EPSG == '4326'): gt180, = np.nonzero(x > 180) x[gt180] -= 360.0 diff --git a/pyTMD/tidal_ellipse.py b/pyTMD/tidal_ellipse.py index f5c54b56..1ce76481 100644 --- a/pyTMD/tidal_ellipse.py +++ b/pyTMD/tidal_ellipse.py @@ -68,6 +68,6 @@ def tidal_ellipse(u,v): uincl[uincl > 180.0] -= 180.0 uphase = -0.5*(ep - em) uphase[uphase < 0.0] += 360.0 - uphase[uphase >- 360.0] -= 360.0 + uphase[uphase >= 360.0] -= 360.0 #-- return values return (umajor,uminor,uincl,uphase) diff --git a/pyTMD/utilities.py b/pyTMD/utilities.py index 75346c0d..812a8cf9 100644 --- a/pyTMD/utilities.py +++ b/pyTMD/utilities.py @@ -9,6 +9,7 @@ UPDATE HISTORY: Updated 09/2020: copy from http and https to bytesIO object in chunks use netrc credentials if not entered from CDDIS functions + generalize build opener function for different Earthdata instances Updated 08/2020: add GSFC CDDIS opener, login and download functions Written 08/2020 """ @@ -328,7 +329,9 @@ def from_http(HOST,timeout=None,local=None,hash='',chunk=16384, return remote_buffer #-- PURPOSE: "login" to NASA Earthdata with supplied credentials -def build_opener(username, password, urs='https://urs.earthdata.nasa.gov'): +def build_opener(username, password, context=ssl.SSLContext(ssl.PROTOCOL_TLS), + password_manager=True, get_ca_certs=True, redirect=True, + authorization_header=False, urs='https://urs.earthdata.nasa.gov'): """ build urllib opener for NASA Earthdata with supplied credentials @@ -339,26 +342,40 @@ def build_opener(username, password, urs='https://urs.earthdata.nasa.gov'): Keyword arguments ----------------- + context: SSL context for opener object + password_manager: create password manager context using default realm + get_ca_certs: get list of loaded “certification authority” certificates + redirect: create redirect handler object + authorization_header: add base64 encoded authorization header to opener urs: Earthdata login URS 3 host """ #-- https://docs.python.org/3/howto/urllib2.html#id5 + handler = [] #-- create a password manager - password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() - #-- Add the username and password for NASA Earthdata Login system - password_mgr.add_password(None,urs,username,password) + if password_manager: + password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() + #-- Add the username and password for NASA Earthdata Login system + password_mgr.add_password(None,urs,username,password) + handler.append(urllib2.HTTPBasicAuthHandler(password_mgr)) #-- Create cookie jar for storing cookies. This is used to store and return #-- the session cookie given to use by the data server (otherwise will just #-- keep sending us back to Earthdata Login to authenticate). cookie_jar = CookieJar() - #-- SSL context with TLS support - context = ssl.SSLContext(ssl.PROTOCOL_TLS) - context.get_ca_certs() + handler.append(urllib2.HTTPCookieProcessor(cookie_jar)) + #-- SSL context handler + if get_ca_certs: + context.get_ca_certs() + handler.append(urllib2.HTTPSHandler(context=context)) + #-- redirect handler + if redirect: + handler.append(urllib2.HTTPRedirectHandler()) #-- create "opener" (OpenerDirector instance) - opener = urllib2.build_opener( - urllib2.HTTPBasicAuthHandler(password_mgr), - urllib2.HTTPSHandler(context=context), - urllib2.HTTPRedirectHandler(), - urllib2.HTTPCookieProcessor(cookie_jar)) + opener = urllib2.build_opener(*handler) + #-- Encode username/password for request authorization headers + #-- add Authorization header to opener + if authorization_header: + b64 = base64.b64encode('{0}:{1}'.format(username,password).encode()) + opener.addheaders = [("Authorization","Basic {0}".format(b64.decode()))] #-- Now all calls to urllib2.urlopen use our opener. urllib2.install_opener(opener) #-- All calls to urllib2.urlopen will now use handler @@ -407,7 +424,7 @@ def cddis_list(HOST,username=None,password=None,build=True,timeout=None, collastmod: list of last modification times for items in the directory """ #-- use netrc credentials - if not (username or password): + if build and not (username or password): urs = 'urs.earthdata.nasa.gov' username,login,password = netrc.netrc().authenticators(urs) #-- build urllib2 opener and check credentials @@ -479,7 +496,7 @@ def from_cddis(HOST,username=None,password=None,build=True,timeout=None, remote_buffer: BytesIO representation of file """ #-- use netrc credentials - if not (username or password): + if build and not (username or password): urs = 'urs.earthdata.nasa.gov' username,login,password = netrc.netrc().authenticators(urs) #-- build urllib2 opener and check credentials diff --git a/scripts/reduce_OTIS_files.py b/scripts/reduce_OTIS_files.py index c7913d14..9d4871fc 100644 --- a/scripts/reduce_OTIS_files.py +++ b/scripts/reduce_OTIS_files.py @@ -1,13 +1,15 @@ #!/usr/bin/env python u""" reduce_OTIS_files.py -Written by Tyler Sutterley (07/2020) +Written by Tyler Sutterley (09/2020) Read OTIS-format tidal files and reduce to a regional subset COMMAND LINE OPTIONS: -D X, --directory=X: working data directory -T X, --tide=X: Tide model to use -B X, --bounds=X: Grid Bounds (xmin,xmax,ymin,ymax) + --projection=X: spatial projection of bounds as EPSG code or PROJ4 string + 4326: latitude and longitude coordinates on WGS84 reference ellipsoid -M X, --mode=X: permissions mode of the output files PYTHON DEPENDENCIES: @@ -22,9 +24,11 @@ PROGRAM DEPENDENCIES: read_tide_model.py: extract tidal harmonic constants out of a tidal model convert_ll_xy.py: converts lat/lon points to and from projected coordinates - output_otis_tides.py: writes OTIS-format tide files + output_otis_tides.py: writes OTIS-format tide files UPDATE HISTORY: + Updated 09/2020: can use projected coordinates for output model bounds + compatibility updates for python3 Updated 07/2020: renamed coordinate conversion program Updated 02/2020: changed CATS2008 grid to match version on U.S. Antarctic Program Data Center http://www.usap-dc.org/view/dataset/601235 @@ -36,93 +40,80 @@ import sys import os import getopt +import pyproj import numpy as np from pyTMD.convert_ll_xy import convert_ll_xy from pyTMD.read_tide_model import * from pyTMD.output_otis_tides import * #-- PURPOSE: read 1 degree land sea mask and create masks for specific regions -def make_regional_OTIS_files(tide_dir, TIDE_MODEL, BOUNDS, MODE=0o775): +def make_regional_OTIS_files(tide_dir, TIDE_MODEL, TYPE=['z','uv'], + BOUNDS=4*[None], PROJECTION='4326', MODE=0o775): + model_file = {} + new_model_file = {} #-- select between tide models if (TIDE_MODEL == 'CATS0201'): grid_file = os.path.join(tide_dir,'cats0201_tmd','grid_CATS') - z_file = os.path.join(tide_dir,'cats0201_tmd','h0_CATS02_01') - uv_file = os.path.join(tide_dir,'cats0201_tmd','UV0_CATS02_01') - reference = 'https://mail.esr.org/polar_tide_models/Model_CATS0201.html' + model_file['z'] = os.path.join(tide_dir,'cats0201_tmd','h0_CATS02_01') + model_file['uv'] = os.path.join(tide_dir,'cats0201_tmd','UV0_CATS02_01') model_format = 'OTIS' EPSG = '4326' elif (TIDE_MODEL == 'CATS2008'): grid_file = os.path.join(tide_dir,'CATS2008','grid_CATS2008') - z_file = os.path.join(tide_dir,'CATS2008','hf.CATS2008.out') - uv_file = os.path.join(tide_dir,'CATS2008','uv.CATS2008.out') - reference = ('https://www.esr.org/research/polar-tide-models/' - 'list-of-polar-tide-models/cats2008/') + model_file['z'] = os.path.join(tide_dir,'CATS2008','hf.CATS2008.out') + model_file['uv'] = os.path.join(tide_dir,'CATS2008','uv.CATS2008.out') model_format = 'OTIS' EPSG = 'CATS2008' elif (TIDE_MODEL == 'CATS2008_load'): grid_file = os.path.join(tide_dir,'CATS2008a_SPOTL_Load','grid_CATS2008a_opt') - z_file = os.path.join(tide_dir,'CATS2008a_SPOTL_Load','h_CATS2008a_SPOTL_load') - reference = ('https://www.esr.org/research/polar-tide-models/' - 'list-of-polar-tide-models/cats2008/') + model_file['z'] = os.path.join(tide_dir,'CATS2008a_SPOTL_Load','h_CATS2008a_SPOTL_load') model_format = 'OTIS' EPSG = 'CATS2008' elif (MODEL == 'TPXO9_atlas'): grid_file = os.path.join(tide_dir,'tpxo9_atlas','grid_tpxo9atlas_30_v1') - z_file = os.path.join(tide_dir,'tpxo9_atlas','hf.tpxo9_atlas_30_v1') - uv_file = os.path.join(tide_dir,'tpxo9_atlas','uv.tpxo9_atlas_30_v1') - reference = 'http://volkov.oce.orst.edu/tides/tpxo9_atlas.html' + model_file['z'] = os.path.join(tide_dir,'tpxo9_atlas','hf.tpxo9_atlas_30_v1') + model_file['uv'] = os.path.join(tide_dir,'tpxo9_atlas','uv.tpxo9_atlas_30_v1') model_format = 'ATLAS' EPSG = '4326' elif (TIDE_MODEL == 'TPXO9.1'): grid_file = os.path.join(tide_dir,'TPX09.1','DATA','grid_tpxo9') - z_file = os.path.join(tide_dir,'TPX09.1','DATA','h_tpxo9.v1') - uv = os.path.join(tide_dir,'TPX09.1','DATA','h_tpxo9.v1') - reference = 'http://volkov.oce.orst.edu/tides/global.html' + model_file['z'] = os.path.join(tide_dir,'TPX09.1','DATA','h_tpxo9.v1') + model_file['uv'] = os.path.join(tide_dir,'TPX09.1','DATA','u_tpxo9.v1') model_format = 'OTIS' EPSG = '4326' elif (TIDE_MODEL == 'TPXO8-atlas'): grid_file = os.path.join(tide_dir,'tpxo8_atlas','grid_tpxo8atlas_30_v1') - z_file = os.path.join(tide_dir,'tpxo8_atlas','hf.tpxo8_atlas_30_v1') - uv_file = os.path.join(tide_dir,'tpxo8_atlas','uv.tpxo8_atlas_30_v1') - reference = 'http://volkov.oce.orst.edu/tides/tpxo8_atlas.html' + model_file['z'] = os.path.join(tide_dir,'tpxo8_atlas','hf.tpxo8_atlas_30_v1') + model_file['uv'] = os.path.join(tide_dir,'tpxo8_atlas','uv.tpxo8_atlas_30_v1') model_format = 'ATLAS' EPSG = '4326' elif (TIDE_MODEL == 'TPXO7.2'): grid_file = os.path.join(tide_dir,'TPXO7.2_tmd','grid_tpxo7.2') - z_file = os.path.join(tide_dir,'TPXO7.2_tmd','h_tpxo7.2') - uv_file = os.path.join(tide_dir,'TPXO7.2_tmd','u_tpxo7.2') - reference = 'http://volkov.oce.orst.edu/tides/global.html' + model_file['z'] = os.path.join(tide_dir,'TPXO7.2_tmd','h_tpxo7.2') + model_file['uv'] = os.path.join(tide_dir,'TPXO7.2_tmd','u_tpxo7.2') model_format = 'OTIS' EPSG = '4326' elif (TIDE_MODEL == 'TPXO7.2_load'): grid_file = os.path.join(tide_dir,'TPXO7.2_load','grid_tpxo6.2') - z_file = os.path.join(tide_dir,'TPXO7.2_load','h_tpxo7.2_load') - uv_file = os.path.join(tide_dir,'TPXO7.2_load','u_tpxo7.2_load') - reference = 'http://volkov.oce.orst.edu/tides/global.html' + model_file['z'] = os.path.join(tide_dir,'TPXO7.2_load','h_tpxo7.2_load') model_format = 'OTIS' EPSG = '4326' elif (TIDE_MODEL == 'AODTM-5'): grid_file = os.path.join(tide_dir,'aodtm5_tmd','grid_Arc5km') - z_file = os.path.join(tide_dir,'aodtm5_tmd','h0_Arc5km.oce') - uv_file = os.path.join(tide_dir,'aodtm5_tmd','u0_Arc5km.oce') - reference = ('https://www.esr.org/research/polar-tide-models/' - 'list-of-polar-tide-models/aodtm-5/') + model_file['z'] = os.path.join(tide_dir,'aodtm5_tmd','h0_Arc5km.oce') + model_file['uv'] = os.path.join(tide_dir,'aodtm5_tmd','u0_Arc5km.oce') model_format = 'OTIS' EPSG = 'PSNorth' elif (TIDE_MODEL == 'AOTIM-5'): grid_file = os.path.join(tide_dir,'aotim5_tmd','grid_Arc5km') - z_file = os.path.join(tide_dir,'aotim5_tmd','h_Arc5km.oce') - uv_file = os.path.join(tide_dir,'aotim5_tmd','u_Arc5km.oce') - reference = ('https://www.esr.org/research/polar-tide-models/' - 'list-of-polar-tide-models/aotim-5/') + model_file['z'] = os.path.join(tide_dir,'aotim5_tmd','h_Arc5km.oce') + model_file['uv'] = os.path.join(tide_dir,'aotim5_tmd','u_Arc5km.oce') model_format = 'OTIS' EPSG = 'PSNorth' elif (TIDE_MODEL == 'AOTIM-5-2018'): grid_file = os.path.join(tide_dir,'aotim5_tmd','grid_Arc5km') - z_file = os.path.join(tide_dir,'aotim5_tmd','h_Arc5km2018') - uv_file = os.path.join(tide_dir,'aotim5_tmd','u_Arc5km2018_T') - reference = ('https://www.esr.org/research/polar-tide-models/' - 'list-of-polar-tide-models/aotim-5/') + model_file['z'] = os.path.join(tide_dir,'aotim5_tmd','h_Arc5km2018') + model_file['uv'] = os.path.join(tide_dir,'aotim5_tmd','u_Arc5km2018_T') model_format = 'OTIS' EPSG = 'PSNorth' @@ -136,14 +127,26 @@ def make_regional_OTIS_files(tide_dir, TIDE_MODEL, BOUNDS, MODE=0o775): #-- if reading a pure global solution xi,yi,hz,mz,iob,dt = read_tide_grid(grid_file) + #-- converting bounds x,y from projection to latitude/longitude + try: + crs1 = pyproj.CRS.from_string("epsg:{0:d}".format(int(PROJECTION))) + except (ValueError,pyproj.exceptions.CRSError): + crs1 = pyproj.CRS.from_string(PROJECTION) + crs2 = pyproj.CRS.from_string("epsg:{0:d}".format(4326)) + transformer = pyproj.Transformer.from_crs(crs1, crs2, always_xy=True) + xbox = np.array([BOUNDS[0],BOUNDS[1],BOUNDS[1],BOUNDS[0],BOUNDS[0]]) + ybox = np.array([BOUNDS[2],BOUNDS[2],BOUNDS[3],BOUNDS[3],BOUNDS[2]]) + lon,lat = transformer.transform(xbox,ybox) + #-- convert bounds from latitude/longitude to model coordinates - x,y = convert_ll_xy(BOUNDS[:2],BOUNDS[2:],EPSG,'F') + x,y = convert_ll_xy(lon,lat,EPSG,'F') + #-- find indices to reduce to xmin,xmax,ymin,ymax gridx,gridy = np.meshgrid(xi,yi) - indy,indx = np.nonzero((gridx >= x[0]) & (gridx <= x[1]) & - (gridy >= y[0]) & (gridy <= y[1])) - nx = np.count_nonzero((xi >= x[0]) & (xi <= x[1])) - ny = np.count_nonzero((yi >= y[0]) & (yi <= y[1])) + indy,indx = np.nonzero((gridx >= x.min()) & (gridx <= x.max()) & + (gridy >= y.min()) & (gridy <= y.max())) + nx = np.count_nonzero((xi >= x.min()) & (xi <= x.max())) + ny = np.count_nonzero((yi >= y.min()) & (yi <= y.max())) #-- calculate new grid limits and convert back to grid-cell edges dx = np.abs(xi[1]-xi[0]) dy = np.abs(yi[1]-yi[0]) @@ -155,48 +158,63 @@ def make_regional_OTIS_files(tide_dir, TIDE_MODEL, BOUNDS, MODE=0o775): hz1[:,:] = hz[indy,indx].reshape(ny,nx) mz1[:,:] = mz[indy,indx].reshape(ny,nx) #-- output reduced grid to file - new_grid_file = create_unique_filename('{0}.reduced'.format(grid_file)) + new_grid_file = create_unique_filename(grid_file) output_otis_grid(new_grid_file,xlim,ylim,hz1,mz1,iob,dt) #-- change the permissions level to MODE os.chmod(new_grid_file, MODE) - #-- read each constituent - constituents,nc = read_constituents(z_file) - z1 = np.zeros((ny,nx,nc),dtype=np.complex64) - u1 = np.zeros((ny,nx,nc),dtype=np.complex64) - v1 = np.zeros((ny,nx,nc),dtype=np.complex64) - for i,c in enumerate(constituents): - #-- read constituent from elevation file - if (model_format == 'ATLAS'): - z0,zlocal = read_atlas_elevation(z_file,i,c) - xi,yi,z = combine_atlas_model(x0,y0,z0,pmask,zlocal,VARIABLE='z') - else: - z = read_elevation_file(z_file,i) - #-- reduce elevation to new bounds - z1[:,:,i] = z[indy,indx].reshape(ny,nx) - #-- read constituent from transport file - if (model_format == 'ATLAS'): - u0,v0,uvlocal = read_atlas_transport(uv_file,i,c) - xi,yi,u = combine_atlas_model(x0,y0,u0,pmask,uvlocal,VARIABLE='u') - xi,yi,v = combine_atlas_model(x0,y0,v0,pmask,uvlocal,VARIABLE='v') - else: - u,v = read_transport_file(uv_file,i) - #-- reduce transport components to new bounds - u1[:,:,i] = u[indy,indx].reshape(ny,nx) - v1[:,:,i] = v[indy,indx].reshape(ny,nx) - #-- output reduced elevation and transport components - new_z_file = create_unique_filename('{0}.reduced'.format(z_file)) - new_uv_file = create_unique_filename('{0}.reduced'.format(uv_file)) - output_otis_elevation(new_z_file,z1,xlim,ylim,constituents) - output_otis_transport(new_uv_file,u1,v1,xlim,ylim,constituents) - #-- change the permissions level to MODE - os.chmod(new_z_file, MODE) - os.chmod(new_uv_file, MODE) + #-- combine ATLAS sub-grids into single output grid + #-- reduce elevation files to bounds + if 'z' in model_file.keys(): + #-- read each constituent + constituents,nc = read_constituents(model_file['z']) + z1 = np.zeros((ny,nx,nc),dtype=np.complex64) + for i,c in enumerate(constituents): + #-- read constituent from elevation file + if (model_format == 'ATLAS'): + z0,zlocal=read_atlas_elevation(model_file['z'],i,c) + xi,yi,z=combine_atlas_model(x0,y0,z0,pmask,zlocal,VARIABLE='z') + else: + z=read_elevation_file(model_file['z'],i) + #-- reduce elevation to new bounds + z1[:,:,i] = z[indy,indx].reshape(ny,nx) + #-- output reduced elevation components + new_model_file['z'] = create_unique_filename(model_file['z']) + output_otis_elevation(new_model_file['z'],z1,xlim,ylim,constituents) + #-- change the permissions level to MODE + os.chmod(new_model_file['z'], MODE) + + #-- combine ATLAS sub-grids into single output grid + #-- reduce transport files to bounds + if 'uv' in model_file.keys(): + #-- read each constituent + constituents,nc = read_constituents(model_file['uv']) + u1 = np.zeros((ny,nx,nc),dtype=np.complex64) + v1 = np.zeros((ny,nx,nc),dtype=np.complex64) + for i,c in enumerate(constituents): + #-- read constituent from transport file + if (model_format == 'ATLAS'): + u0,v0,uvlocal=read_atlas_transport(model_file['uv'],i,c) + xi,yi,u=combine_atlas_model(x0,y0,u0,pmask,uvlocal,VARIABLE='u') + xi,yi,v=combine_atlas_model(x0,y0,v0,pmask,uvlocal,VARIABLE='v') + else: + u,v=read_transport_file(model_file['uv'],i) + #-- reduce transport components to new bounds + u1[:,:,i] = u[indy,indx].reshape(ny,nx) + v1[:,:,i] = v[indy,indx].reshape(ny,nx) + #-- output reduced transport components + new_model_file['uv'] = create_unique_filename(model_file['uv']) + output_otis_transport(new_model_file['uv'],u1,v1,xlim,ylim,constituents) + #-- change the permissions level to MODE + os.chmod(new_model_file['uv'], MODE) #-- PURPOSE: create a unique filename adding a numerical instance if existing def create_unique_filename(filename): #-- split filename into fileBasename and fileExtension fileBasename, fileExtension = os.path.splitext(filename) + fileExtension = '' if (fileExtension in ('.out','.oce')) else fileExtension + #-- replace extension with reduced flag + filename = '{0}{1}{2}'.format(fileBasename, fileExtension, '.reduced') #-- create counter to add to the end of the filename if existing counter = 1 while counter: @@ -209,8 +227,9 @@ def create_unique_filename(filename): #-- close the file descriptor and return the filename os.close(fd) return filename - #-- new filename adds counter the between fileBasename and fileExtension - filename = '{0}{1}_{2:d}'.format(fileBasename, fileExtension, counter) + #-- new filename adds counter + args = (fileBasename, fileExtension, '.reduced', counter) + filename = '{0}{1}{2}_{3:d}'.format(*args) counter += 1 #-- PURPOSE: help module to describe the optional input parameters @@ -219,19 +238,24 @@ def usage(): print(' -D X, --directory=X\tWorking data directory') print(' -T X, --tide=X\t\tTide model to use in correction') print(' -B X, --bounds=X\tGrid Bounds (xmin,xmax,ymin,ymax)') - print(' -M X, --mode=X\t\tPermission mode of directories and files\n') + print(' --projection=X\t\tSpatial projection as EPSG code or PROJ4 string') + print(' -M X, --mode=X\t\tPermission mode of the output files\n') #-- This is the main part of the program that calls the individual modules def main(): #-- Read the system arguments listed after the program - long_options = ['help','directory=','tide=','bounds=','mode='] + long_options = ['help','directory=','tide=','bounds=','projection=','mode='] optlist,arglist = getopt.getopt(sys.argv[1:],'hD:T:B:M:',long_options) #-- command line parameters tide_dir = os.getcwd() - BOUNDS = None #-- tide model to use TIDE_MODEL = 'TPXO9.1' + #-- bounds for reducing model (xmin,xmax,ymin,ymax) + BOUNDS = 4*[None] + #-- spatial projection of input bounds (EPSG code or PROJ4 string) + PROJECTION = '4326' + #-- permissions mode of output reduced files MODE = 0o775 for opt, arg in optlist: if opt in ('-h','--help'): @@ -243,6 +267,8 @@ def main(): TIDE_MODEL = arg elif opt in ("-B","--bounds"): BOUNDS = np.array(arg.split(','),dtype=np.float) + elif opt in ("--projection",): + PROJECTION = arg elif opt in ("-M","--mode"): MODE = int(arg,8) @@ -253,7 +279,8 @@ def main(): assert TIDE_MODEL in model_list, 'Unlisted tide model' #-- run program - make_regional_OTIS_files(tide_dir, TIDE_MODEL, BOUNDS, MODE=MODE) + make_regional_OTIS_files(tide_dir, TIDE_MODEL, BOUNDS=BOUNDS, + PROJECTION=PROJECTION, MODE=MODE) #-- run main program if __name__ == '__main__': diff --git a/setup.py b/setup.py index 8fa39a9d..d4048d45 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ setup( name='pyTMD', - version='1.0.2.12', + version='1.0.2.13', description='Tide Model Driver to read OTIS, GOT and FES formatted tidal solutions and make tidal predictions', long_description=long_description, long_description_content_type="text/markdown", @@ -36,8 +36,8 @@ keywords='Ocean Tides, Load Tides, Pole Tides, Tidal Prediction, OTIS, GOT, FES', packages=find_packages(), install_requires=install_requires, - dependency_links=['https://github.com/tsutterley/read-ICESat-2/tarball/master', - 'https://github.com/tsutterley/read-ATM1b-QFIT-binary/tarball/master'], + dependency_links=['https://github.com/tsutterley/read-ICESat-2/tarball/main', + 'https://github.com/tsutterley/read-ATM1b-QFIT-binary/tarball/main'], scripts=scripts, include_package_data=True, ) diff --git a/test/test_download_and_read.py b/test/test_download_and_read.py index 7e89d3b9..adb7e8b1 100644 --- a/test/test_download_and_read.py +++ b/test/test_download_and_read.py @@ -36,6 +36,7 @@ import pyTMD.read_tide_model import pyTMD.predict_tidal_ts import pyTMD.infer_minor_corrections +import pyTMD.tidal_ellipse from oct2py import octave #-- current file path @@ -331,6 +332,7 @@ def test_verify_CATS2008(parameters): difference = np.ma.zeros((ndays)) difference.data[:] = tide.data - validation.T difference.mask = (tide.mask | np.isnan(validation)) + difference.data[difference.mask] = 0.0 if not np.all(difference.mask): assert np.all(np.abs(difference) < eps) @@ -420,5 +422,104 @@ def test_verify_AOTIM5_2018(parameters): difference = np.ma.zeros((ndays)) difference.data[:] = tide.data - validation.T difference.mask = (tide.mask | np.isnan(validation)) + difference.data[difference.mask] = 0.0 + if not np.all(difference.mask): + assert np.all(np.abs(difference) < eps) + +#-- PURPOSE: Tests that tidal ellipse results are comparable to Matlab program +def test_tidal_ellipse(): + #-- model parameters for CATS2008 + grid_file = os.path.join(filepath,'grid_CATS2008') + model_file = os.path.join(filepath,'uv.CATS2008.out') + TYPES = ['u','v'] + GRID = 'OTIS' + EPSG = 'CATS2008' + + #-- open Antarctic Tide Gauge (AntTG) database + with open(os.path.join(filepath,'AntTG_ocean_height_v1.txt'),'r') as f: + file_contents = f.read().splitlines() + #-- counts the number of lines in the header + count = 0 + HEADER = True + #-- Reading over header text + while HEADER: + #-- check if file line at count starts with matlab comment string + HEADER = file_contents[count].startswith('%') + #-- add 1 to counter + count += 1 + #-- rewind 1 line + count -= 1 + #-- iterate over number of stations + antarctic_stations = (len(file_contents) - count)//10 + stations = [None]*antarctic_stations + shortname = [None]*antarctic_stations + station_type = [None]*antarctic_stations + station_lon = np.zeros((antarctic_stations)) + station_lat = np.zeros((antarctic_stations)) + for s in range(antarctic_stations): + i = count + s*10 + stations[s] = file_contents[i + 1].strip() + shortname[s] = file_contents[i + 3].strip() + lat,lon,aux1,aux2 = file_contents[i + 4].split() + station_type[s] = file_contents[i + 6].strip() + station_lon[s] = np.float(lon) + station_lat[s] = np.float(lat) + + #-- compare daily outputs at each station point + invalid_list = ['Ablation Lake','Amery','Bahia Esperanza','Beaver Lake', + 'Cape Roberts','Casey','Doake Ice Rumples','EE4A','EE4B', + 'Eklund Islands','Gerlache C','Groussac','Gurrachaga','Half Moon Is.', + 'Heard Island','Hobbs Pool','Mawson','McMurdo','Mikkelsen','Palmer', + 'Primavera','Rutford GL','Rutford GPS','Rothera','Scott Base', + 'Seymour Is','Terra Nova Bay'] + #-- remove coastal stations from the list + i = [i for i,s in enumerate(shortname) if s not in invalid_list] + valid_stations = len(i) + #-- will verify differences between model outputs are within tolerance + eps = np.finfo(np.float16).eps + + #-- save complex amplitude for each current + hc1,hc2 = ({},{}) + #-- iterate over zonal and meridional currents + for TYPE in TYPES: + #-- extract amplitude and phase from tide model + amp,ph,D,c=pyTMD.read_tide_model.extract_tidal_constants(station_lon[i], + station_lat[i], grid_file, model_file, EPSG, TYPE=TYPE, + METHOD='spline', GRID=GRID) + #-- calculate complex phase in radians for Euler's + cph = -1j*ph*np.pi/180.0 + #-- calculate constituent oscillation for station + hc1[TYPE] = amp*np.exp(cph) + + #-- compute validation data from Matlab TMD program using octave + #-- https://github.com/EarthAndSpaceResearch/TMD_Matlab_Toolbox_v2.5 + TMDpath = os.path.join(filepath,'..','TMD_Matlab_Toolbox','TMD') + octave.addpath(octave.genpath(os.path.normpath(TMDpath))) + octave.addpath(filepath) + octave.warning('off', 'all') + CFname = os.path.join(filepath,'Model_CATS2008') + #-- extract tidal harmonic constants out of a tidal model + amp,ph,D,cons = octave.tmd_extract_HC(CFname,station_lat[i], + station_lon[i],TYPE,nout=4) + #-- calculate complex phase in radians for Euler's + cph = -1j*ph*np.pi/180.0 + #-- calculate constituent oscillation for station + hc2[TYPE] = amp*np.exp(cph) + + #-- compute tidal ellipse parameters for python program + test = {} + test['umajor'],test['uminor'],test['uincl'],test['uphase'] = \ + pyTMD.tidal_ellipse(hc1['u'],hc1['v']) + #-- compute tidal ellipse parameters for TMD matlab program + valid = {} + valid['umajor'],valid['uminor'],valid['uincl'],valid['uphase'] = \ + octave.TideEl(hc2['u'],hc2['v'],nout=4) + + #-- calculate differences between matlab and python version + for key in ['umajor','uminor','uincl','uphase']: + difference = np.ma.zeros((valid_stations,len(c))) + difference.data[:] = test[key].data - valid[key].T + difference.mask = (test[key].mask | np.isnan(valid[key].T)) + difference.data[difference.mask] = 0.0 if not np.all(difference.mask): assert np.all(np.abs(difference) < eps)