Source code for pyTMD.datasets.fetch_gsfc_got

#!/usr/bin/env python
"""
fetch_gsfc_got.py
Written by Tyler Sutterley (12/2025)
Download Goddard Ocean Tide (GOT) models

CALLING SEQUENCE:
    python fetch_gsfc_got.py --tide=GOT5.6

COMMAND LINE OPTIONS:
    --help: list the command line options
    -D X, --directory X: working data directory
    -T X, --tide X: GOT tide model to download
        GOT4.8
        GOT4.10
        GOT5.5
        GOT5.5D
        GOT5.6
        RE14
    --format: GOT tide model format to download
        ascii
        netCDF
    -G, --gzip: compress output ASCII and netCDF4 tide files
    -t X, --timeout X: timeout in seconds for blocking operations
    -M X, --mode X: Local permissions mode of the files downloaded

PYTHON DEPENDENCIES:
    future: Compatibility layer between Python 2 and Python 3
        https://python-future.org/

PROGRAM DEPENDENCIES:
    utilities.py: download and management utilities for syncing files

UPDATE HISTORY:
    Updated 12/2025: use URL class to build and operate on URLs
        simplify function call signatures
    Updated 10/2025: change default directory for tide models to cache
    Updated 09/2025: renamed module and function to fetch_gsfc_got
        made a callable function and added function docstrings
    Updated 07/2025: add a default directory for tide models
    Updated 01/2025: scrubbed use of pathlib.os to just use os directly
    Updated 09/2024: added Ray and Erofeeva (2014) long-period tide model
    Updated 08/2024: keep prime nomenclature for 3rd degree tides
    Written 07/2024
"""

from __future__ import print_function, annotations

import os
import re
import gzip
import shutil
import logging
import pathlib
import tarfile
import argparse
import posixpath
import pyTMD.utilities

# default data directory for tide models
_default_directory = pyTMD.utilities.get_cache_path()


# PURPOSE: Download Goddard Ocean Tide (GOT) models
[docs] def fetch_gsfc_got( model: str, directory: str | pathlib.Path | None = _default_directory, format: str = "netcdf", compressed: bool = False, timeout: int | None = None, mode: oct = 0o775, ): """ Download Goddard Ocean Tide (GOT) models from NASA Goddard Space Flight Center (GSFC) Parameters ---------- model: str GOT tide model to download directory: str or pathlib.Path Working data directory format: str, default 'netcdf' GOT tide model format to download compressed: bool, default False Compress output ASCII and netCDF4 tide files timeout: int, default None Timeout in seconds for blocking operations mode: oct, default 0o775 Local permissions mode of the files downloaded """ # create logger for verbosity level logger = pyTMD.utilities.build_logger(__name__, level=logging.INFO) # if compressing the output files opener = gzip.open if compressed else open # url for each tide model tarfile PATH = {} PATH["GOT4.8"] = ["2022-07", "got4.8.tar.gz"] PATH["GOT4.10"] = ["2023-12", "got4.10c.tar.gz"] PATH["GOT5.5"] = ["2024-07", "GOT5.5.tar%201.gz"] PATH["GOT5.5D"] = ["2024-07", "GOT5.5D.tar%201.gz"] PATH["GOT5.6"] = ["2024-07", "GOT5.6.tar%201.gz"] PATH["RE14"] = ["2022-07", "re14_longperiodtides_rel.tar"] # tarfile mode for each tide model TAR = {} TAR["GOT4.8"] = "r:gz" TAR["GOT4.10"] = "r:gz" TAR["GOT5.5"] = "r:gz" TAR["GOT5.5D"] = "r:gz" TAR["GOT5.6"] = "r:gz" TAR["RE14"] = "r" # recursively create directories if non-existent directory = pyTMD.utilities.Path(directory).resolve() directory.mkdir(mode=mode, parents=True, exist_ok=True) # build host url for model url = [ "https://earth.gsfc.nasa.gov", "sites", "default", "files", *PATH[model], ] URL = pyTMD.utilities.URL.from_parts(url) logger.info(f"{URL} -->\n") # open the tar file tar = tarfile.open( name=URL.parts[-1], fileobj=URL.get(timeout=timeout), mode=TAR[model] ) # read tar file and extract all files member_files = [m for m in tar.getmembers() if tarfile.TarInfo.isfile(m)] for m in member_files: # extract file contents to new file base, sfx = posixpath.splitext(m.name) # skip files that are not in the desired format if (sfx == ".nc") and (format == "ascii"): continue elif (sfx == ".d") and (format == "netcdf"): continue elif re.match(r"^.DS", posixpath.basename(m.name)): continue elif re.match(r"^._", posixpath.basename(m.name)): continue # output file name if sfx in (".d", ".nc") and compressed: output = f"{m.name}.gz" else: output = m.name # create local file path local_file = directory.joinpath(*posixpath.split(output)) # check if the local file exists if local_file.exists() and _newer(m.mtime, local_file.stat().st_mtime): # check the modification time of the local file # if remote file is newer: overwrite the local file continue # print the file being transferred logger.info(f"\t{str(local_file)}") # recursively create output directory if non-existent local_file.parent.mkdir(mode=mode, parents=True, exist_ok=True) # extract file to local directory with tar.extractfile(m) as f_in, opener(local_file, "wb") as f_out: shutil.copyfileobj(f_in, f_out) # get last modified date of remote file within tar file # keep remote modification time of file and local access time os.utime(local_file, (local_file.stat().st_atime, m.mtime)) local_file.chmod(mode=mode)
# PURPOSE: compare the modification time of two files def _newer(t1: int, t2: int) -> bool: """ Compare the modification time of two files Parameters ---------- t1: int Modification time of first file t2: int Modification time of second file """ return pyTMD.utilities.even(t1) <= pyTMD.utilities.even(t2) # PURPOSE: create argument parser def arguments(): parser = argparse.ArgumentParser( description="""Download Goddard Tide models from NASA Goddard Space Flight Center (GSFC) """, fromfile_prefix_chars="@", ) parser.convert_arg_line_to_args = pyTMD.utilities.convert_arg_line_to_args # command line parameters # working data directory for location of tide models parser.add_argument( "--directory", "-D", type=pathlib.Path, default=_default_directory, help="Working data directory", ) # Goddard Ocean Tide model to download parser.add_argument( "--tide", "-T", metavar="TIDE", type=str, nargs="+", default=["GOT5.5"], choices=("GOT4.8", "GOT4.10", "GOT5.5", "GOT5.5D", "GOT5.6", "RE14"), help="Goddard Ocean Tide model to download", ) # Goddard Ocean Tide model format to download parser.add_argument( "--format", type=str, default="netcdf", choices=("ascii", "netcdf"), help="Goddard Ocean Tide model format to download", ) # compress output ascii and netCDF4 tide files with gzip parser.add_argument( "--gzip", "-G", default=False, action="store_true", help="Compress output ASCII and netCDF tide files", ) # connection timeout parser.add_argument( "--timeout", "-t", type=int, default=360, help="Timeout in seconds for blocking operations", ) # permissions mode of the local directories and files (number in octal) parser.add_argument( "--mode", "-M", type=lambda x: int(x, base=8), default=0o775, help="Permissions mode of the files downloaded", ) # return the parser return parser # This is the main part of the program that calls the individual functions def main(): # Read the system arguments listed after the program parser = arguments() args, _ = parser.parse_known_args() # check internet connection before attempting to run program if pyTMD.utilities.check_connection("https://earth.gsfc.nasa.gov"): for m in args.tide: fetch_gsfc_got( m, directory=args.directory, format=args.format, compressed=args.gzip, timeout=args.timeout, mode=args.mode, ) # run main program if __name__ == "__main__": main()