Example #1
0
def test_ATL06_download_and_read(username, password):
    HOST = [
        'https://n5eil01u.ecs.nsidc.org', 'ATLAS', 'ATL06.003', '2018.10.14',
        'ATL06_20181014001049_02350102_003_01.h5'
    ]
    buffer, error = icesat2_toolkit.utilities.from_nsidc(HOST,
                                                         username=username,
                                                         password=password,
                                                         local=HOST[-1],
                                                         verbose=True)
    #-- raise exception if download error
    if not buffer:
        raise Exception(error)
    #-- read ATL06 data from downloaded HDF5 file
    IS2_atl06_mds, IS2_atl06_attrs, IS2_atl06_beams = read_HDF5_ATL06(
        HOST[-1],
        ATTRIBUTES=False,
        HISTOGRAM=False,
        QUALITY=False,
        VERBOSE=True)
    assert all(gtx in IS2_atl06_mds.keys() for gtx in IS2_atl06_beams)
def compute_LPET_ICESat2(INPUT_FILE, VERBOSE=False, MODE=0o775):

    #-- read data from input file
    print('{0} -->'.format(os.path.basename(INPUT_FILE))) if VERBOSE else None
    IS2_atl06_mds, IS2_atl06_attrs, IS2_atl06_beams = read_HDF5_ATL06(
        INPUT_FILE, ATTRIBUTES=True)
    DIRECTORY = os.path.dirname(INPUT_FILE)
    #-- extract parameters from ICESat-2 ATLAS HDF5 file name
    rx = re.compile(
        r'(processed_)?(ATL\d{2})_(\d{4})(\d{2})(\d{2})(\d{2})'
        r'(\d{2})(\d{2})_(\d{4})(\d{2})(\d{2})_(\d{3})_(\d{2})(.*?).h5$')
    try:
        SUB, PRD, YY, MM, DD, HH, MN, SS, TRK, CYCL, GRAN, RL, VERS, AUX = rx.findall(
            INPUT_FILE).pop()
    except:
        #-- output long-period equilibrium tide HDF5 file (generic)
        fileBasename, fileExtension = os.path.splitext(INPUT_FILE)
        OUTPUT_FILE = '{0}_{1}{2}'.format(fileBasename, 'LPET', fileExtension)
    else:
        #-- output long-period equilibrium tide HDF5 file for ASAS/NSIDC granules
        args = (PRD, YY, MM, DD, HH, MN, SS, TRK, CYCL, GRAN, RL, VERS, AUX)
        file_format = '{0}_LPET_{1}{2}{3}{4}{5}{6}_{7}{8}{9}_{10}_{11}{12}.h5'
        OUTPUT_FILE = file_format.format(*args)

    #-- number of GPS seconds between the GPS epoch
    #-- and ATLAS Standard Data Product (SDP) epoch
    atlas_sdp_gps_epoch = IS2_atl06_mds['ancillary_data'][
        'atlas_sdp_gps_epoch']

    #-- copy variables for outputting to HDF5 file
    IS2_atl06_tide = {}
    IS2_atl06_fill = {}
    IS2_atl06_dims = {}
    IS2_atl06_tide_attrs = {}
    #-- number of GPS seconds between the GPS epoch (1980-01-06T00:00:00Z UTC)
    #-- and ATLAS Standard Data Product (SDP) epoch (2018-01-01T00:00:00Z UTC)
    #-- Add this value to delta time parameters to compute full gps_seconds
    IS2_atl06_tide['ancillary_data'] = {}
    IS2_atl06_tide_attrs['ancillary_data'] = {}
    for key in ['atlas_sdp_gps_epoch']:
        #-- get each HDF5 variable
        IS2_atl06_tide['ancillary_data'][key] = IS2_atl06_mds[
            'ancillary_data'][key]
        #-- Getting attributes of group and included variables
        IS2_atl06_tide_attrs['ancillary_data'][key] = {}
        for att_name, att_val in IS2_atl06_attrs['ancillary_data'][key].items(
        ):
            IS2_atl06_tide_attrs['ancillary_data'][key][att_name] = att_val

    #-- for each input beam within the file
    for gtx in sorted(IS2_atl06_beams):
        #-- output data dictionaries for beam
        IS2_atl06_tide[gtx] = dict(land_ice_segments={})
        IS2_atl06_fill[gtx] = dict(land_ice_segments={})
        IS2_atl06_dims[gtx] = dict(land_ice_segments={})
        IS2_atl06_tide_attrs[gtx] = dict(land_ice_segments={})

        #-- number of segments
        val = IS2_atl06_mds[gtx]['land_ice_segments']
        n_seg = len(val['segment_id'])
        #-- find valid segments for beam
        fv = IS2_atl06_attrs[gtx]['land_ice_segments']['h_li']['_FillValue']

        #-- convert time from ATLAS SDP to days relative to Jan 1, 1992
        gps_seconds = atlas_sdp_gps_epoch + val['delta_time']
        leap_seconds = pyTMD.time.count_leap_seconds(gps_seconds)
        tide_time = pyTMD.time.convert_delta_time(gps_seconds - leap_seconds,
                                                  epoch1=(1980, 1, 6, 0, 0, 0),
                                                  epoch2=(1992, 1, 1, 0, 0, 0),
                                                  scale=1.0 / 86400.0)
        #-- interpolate delta times from calendar dates to tide time
        delta_file = get_data_path(['data', 'merged_deltat.data'])
        deltat = calc_delta_time(delta_file, tide_time)

        #-- predict long-period equilibrium tides at latitudes and time
        tide_lpe = np.ma.zeros((n_seg), fill_value=fv)
        tide_lpe.data[:] = compute_equilibrium_tide(tide_time + deltat,
                                                    val['latitude'])
        tide_lpe.mask = (val['latitude'] == fv) | (val['delta_time'] == fv)

        #-- group attributes for beam
        IS2_atl06_tide_attrs[gtx]['Description'] = IS2_atl06_attrs[gtx][
            'Description']
        IS2_atl06_tide_attrs[gtx]['atlas_pce'] = IS2_atl06_attrs[gtx][
            'atlas_pce']
        IS2_atl06_tide_attrs[gtx]['atlas_beam_type'] = IS2_atl06_attrs[gtx][
            'atlas_beam_type']
        IS2_atl06_tide_attrs[gtx]['groundtrack_id'] = IS2_atl06_attrs[gtx][
            'groundtrack_id']
        IS2_atl06_tide_attrs[gtx]['atmosphere_profile'] = IS2_atl06_attrs[gtx][
            'atmosphere_profile']
        IS2_atl06_tide_attrs[gtx]['atlas_spot_number'] = IS2_atl06_attrs[gtx][
            'atlas_spot_number']
        IS2_atl06_tide_attrs[gtx]['sc_orientation'] = IS2_atl06_attrs[gtx][
            'sc_orientation']
        #-- group attributes for land_ice_segments
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['Description'] = (
            "The land_ice_segments group "
            "contains the primary set of derived products. This includes geolocation, height, and "
            "standard error and quality measures for each segment. This group is sparse, meaning "
            "that parameters are provided only for pairs of segments for which at least one beam "
            "has a valid surface-height measurement.")
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['data_rate'] = (
            "Data within this group are "
            "sparse.  Data values are provided only for those ICESat-2 20m segments where at "
            "least one beam has a valid land ice height measurement.")

        #-- geolocation, time and segment ID
        #-- delta time
        delta_time = np.ma.array(val['delta_time'],
                                 fill_value=fv,
                                 mask=(val['delta_time'] == fv))
        IS2_atl06_tide[gtx]['land_ice_segments']['delta_time'] = delta_time
        IS2_atl06_fill[gtx]['land_ice_segments'][
            'delta_time'] = delta_time.fill_value
        IS2_atl06_dims[gtx]['land_ice_segments']['delta_time'] = None
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['delta_time'] = {}
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['delta_time'][
            'units'] = "seconds since 2018-01-01"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['delta_time'][
            'long_name'] = "Elapsed GPS seconds"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['delta_time'][
            'standard_name'] = "time"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['delta_time'][
            'calendar'] = "standard"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['delta_time'][
            'description'] = (
                "Number of GPS "
                "seconds since the ATLAS SDP epoch. The ATLAS Standard Data Products (SDP) epoch offset "
                "is defined within /ancillary_data/atlas_sdp_gps_epoch as the number of GPS seconds "
                "between the GPS epoch (1980-01-06T00:00:00.000000Z UTC) and the ATLAS SDP epoch. By "
                "adding the offset contained within atlas_sdp_gps_epoch to delta time parameters, the "
                "time in gps_seconds relative to the GPS epoch can be computed."
            )
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['delta_time']['coordinates'] = \
            "segment_id latitude longitude"
        #-- latitude
        latitude = np.ma.array(val['latitude'],
                               fill_value=fv,
                               mask=(val['latitude'] == fv))
        IS2_atl06_tide[gtx]['land_ice_segments']['latitude'] = latitude
        IS2_atl06_fill[gtx]['land_ice_segments'][
            'latitude'] = latitude.fill_value
        IS2_atl06_dims[gtx]['land_ice_segments']['latitude'] = ['delta_time']
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude'] = {}
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude'][
            'units'] = "degrees_north"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude'][
            'contentType'] = "physicalMeasurement"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude'][
            'long_name'] = "Latitude"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude'][
            'standard_name'] = "latitude"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude'][
            'description'] = ("Latitude of "
                              "segment center")
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude'][
            'valid_min'] = -90.0
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude'][
            'valid_max'] = 90.0
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude']['coordinates'] = \
            "segment_id delta_time longitude"
        #-- longitude
        longitude = np.ma.array(val['longitude'],
                                fill_value=fv,
                                mask=(val['longitude'] == fv))
        IS2_atl06_tide[gtx]['land_ice_segments']['longitude'] = longitude
        IS2_atl06_fill[gtx]['land_ice_segments'][
            'longitude'] = longitude.fill_value
        IS2_atl06_dims[gtx]['land_ice_segments']['longitude'] = ['delta_time']
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude'] = {}
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude'][
            'units'] = "degrees_east"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude'][
            'contentType'] = "physicalMeasurement"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude'][
            'long_name'] = "Longitude"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude'][
            'standard_name'] = "longitude"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude'][
            'description'] = ("Longitude of "
                              "segment center")
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude'][
            'valid_min'] = -180.0
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude'][
            'valid_max'] = 180.0
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude']['coordinates'] = \
            "segment_id delta_time latitude"
        #-- segment ID
        IS2_atl06_tide[gtx]['land_ice_segments']['segment_id'] = val[
            'segment_id']
        IS2_atl06_fill[gtx]['land_ice_segments']['segment_id'] = None
        IS2_atl06_dims[gtx]['land_ice_segments']['segment_id'] = ['delta_time']
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['segment_id'] = {}
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['segment_id'][
            'units'] = "1"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['segment_id'][
            'contentType'] = "referenceInformation"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['segment_id'][
            'long_name'] = "Along-track segment ID number"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['segment_id'][
            'description'] = (
                "A 7 digit number "
                "identifying the along-track geolocation segment number.  These are sequential, starting with "
                "1 for the first segment after an ascending equatorial crossing node. Equal to the segment_id for "
                "the second of the two 20m ATL03 segments included in the 40m ATL06 segment"
            )
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['segment_id']['coordinates'] = \
            "delta_time latitude longitude"

        #-- geophysical variables
        IS2_atl06_tide[gtx]['land_ice_segments']['geophysical'] = {}
        IS2_atl06_fill[gtx]['land_ice_segments']['geophysical'] = {}
        IS2_atl06_dims[gtx]['land_ice_segments']['geophysical'] = {}
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical'] = {}
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical'][
            'Description'] = (
                "The geophysical group "
                "contains parameters used to correct segment heights for geophysical effects, parameters "
                "related to solar background and parameters indicative of the presence or absence of clouds."
            )
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical'][
            'data_rate'] = (
                "Data within this group "
                "are stored at the land_ice_segments segment rate.")
        #-- computed long-period equilibrium tide
        IS2_atl06_tide[gtx]['land_ice_segments']['geophysical'][
            'tide_equilibrium'] = tide_lpe
        IS2_atl06_fill[gtx]['land_ice_segments']['geophysical'][
            'tide_equilibrium'] = tide_lpe.fill_value
        IS2_atl06_dims[gtx]['land_ice_segments']['geophysical'][
            'tide_equilibrium'] = ['delta_time']
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical'][
            'tide_equilibrium'] = {}
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical'][
            'tide_equilibrium']['units'] = "meters"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical'][
            'tide_equilibrium']['contentType'] = "referenceInformation"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical']['tide_equilibrium']['long_name'] = \
            "Long Period Equilibrium Tide"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical'][
            'tide_equilibrium']['description'] = (
                "Long-period "
                "equilibrium tidal elevation from the summation of fifteen tidal spectral lines"
            )
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical']['tide_equilibrium']['reference'] = \
            "https://doi.org/10.1111/j.1365-246X.1973.tb03420.x"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical']['tide_equilibrium']['coordinates'] = \
            "../segment_id ../delta_time ../latitude ../longitude"

    #-- print file information
    print('\t{0}'.format(OUTPUT_FILE)) if VERBOSE else None
    HDF5_ATL06_tide_write(IS2_atl06_tide,
                          IS2_atl06_tide_attrs,
                          CLOBBER=True,
                          INPUT=os.path.basename(INPUT_FILE),
                          FILL_VALUE=IS2_atl06_fill,
                          DIMENSIONS=IS2_atl06_dims,
                          FILENAME=os.path.join(DIRECTORY, OUTPUT_FILE))
    #-- change the permissions mode
    os.chmod(os.path.join(DIRECTORY, OUTPUT_FILE), MODE)
Example #3
0
def interp_IB_response_ICESat2(base_dir,
                               FILE,
                               MODEL,
                               RANGE=None,
                               DENSITY=None,
                               VERBOSE=False,
                               MODE=0o775):

    #-- create logger
    loglevel = logging.INFO if VERBOSE else logging.CRITICAL
    logging.basicConfig(level=loglevel)

    #-- directory setup for reanalysis model
    ddir = os.path.join(base_dir, MODEL)
    #-- set model specific parameters
    if (MODEL == 'ERA-Interim'):
        #-- mean sea level pressure file
        input_mean_file = 'ERA-Interim-Mean-MSL-{0:4d}-{1:4d}.nc'
        #-- input land-sea mask for ocean redistribution
        input_mask_file = 'ERA-Interim-Invariant-Parameters.nc'
        VARNAME = 'msl'
        LONNAME = 'longitude'
        LATNAME = 'latitude'
        TIMENAME = 'time'
        #-- land-sea mask variable name and value of oceanic points
        MASKNAME = 'lsm'
        OCEAN = 0
        #-- projection string
        proj4_params = ('+proj=longlat +ellps=WGS84 +datum=WGS84 '
                        '+no_defs lon_wrap=180')
    elif (MODEL == 'ERA5'):
        #-- mean sea level pressure file
        input_mean_file = 'ERA5-Mean-MSL-{0:4d}-{1:4d}.nc'
        #-- input land-sea mask for ocean redistribution
        input_mask_file = 'ERA5-Invariant-Parameters.nc'
        VARNAME = 'msl'
        LONNAME = 'longitude'
        LATNAME = 'latitude'
        TIMENAME = 'time'
        #-- land-sea mask variable name and value of oceanic points
        MASKNAME = 'lsm'
        OCEAN = 0
        #-- projection string
        proj4_params = ('+proj=longlat +ellps=WGS84 +datum=WGS84 '
                        '+no_defs lon_wrap=180')
    elif (MODEL == 'MERRA-2'):
        #-- mean sea level pressure file
        input_mean_file = 'MERRA2.Mean_SLP.{0:4d}-{1:4d}.nc'
        #-- input land-sea mask for ocean redistribution
        input_mask_file = 'MERRA2_101.const_2d_asm_Nx.00000000.nc4'
        VARNAME = 'SLP'
        LONNAME = 'lon'
        LATNAME = 'lat'
        TIMENAME = 'time'
        #-- land-sea mask variable name and value of oceanic points
        MASKNAME = 'FROCEAN'
        OCEAN = 1
        #-- projection string
        proj4_params = 'epsg:4326'

    #-- read data from input_file
    logging.info('{0} -->'.format(os.path.basename(FILE)))
    IS2_atl06_mds, IS2_atl06_attrs, IS2_atl06_beams = read_HDF5_ATL06(
        FILE, ATTRIBUTES=True)
    DIRECTORY = os.path.dirname(FILE)
    #-- extract parameters from ICESat-2 ATLAS HDF5 file name
    rx = re.compile(
        r'(processed_)?(ATL\d{2})_(\d{4})(\d{2})(\d{2})(\d{2})'
        r'(\d{2})(\d{2})_(\d{4})(\d{2})(\d{2})_(\d{3})_(\d{2})(.*?).h5$')
    SUB, PRD, YY, MM, DD, HH, MN, SS, TRK, CYCL, GRAN, RL, VERS, AUX = rx.findall(
        FILE).pop()

    #-- number of GPS seconds between the GPS epoch
    #-- and ATLAS Standard Data Product (SDP) epoch
    atlas_sdp_gps_epoch = IS2_atl06_mds['ancillary_data'][
        'atlas_sdp_gps_epoch']

    #-- read mean pressure field
    mean_file = os.path.join(ddir, input_mean_file.format(RANGE[0], RANGE[1]))
    mean_pressure, lon, lat = ncdf_mean_pressure(mean_file, VARNAME, LONNAME,
                                                 LATNAME)

    #-- pyproj transformer for converting from input coordinates (EPSG)
    #-- to model coordinates
    crs1 = pyproj.CRS.from_string('epsg:{0:d}'.format(4326))
    crs2 = pyproj.CRS.from_string(proj4_params)
    transformer = pyproj.Transformer.from_crs(crs1, crs2, always_xy=True)

    #-- grid step size in radians
    dphi = np.pi * np.abs(lon[1] - lon[0]) / 180.0
    dth = np.pi * np.abs(lat[1] - lat[0]) / 180.0
    #-- calculate meshgrid from latitude and longitude
    gridlon, gridlat = np.meshgrid(lon, lat)
    gridphi = gridlon * np.pi / 180.0
    #-- calculate colatitude
    gridtheta = (90.0 - gridlat) * np.pi / 180.0

    #-- ellipsoidal parameters of WGS84 ellipsoid
    #-- semimajor axis of the ellipsoid [m]
    a_axis = 6378137.0
    #-- flattening of the ellipsoid
    flat = 1.0 / 298.257223563
    #-- semiminor axis of the ellipsoid [m]
    b_axis = (1.0 - flat) * a_axis
    #-- calculate grid areas globally
    AREA = dphi * dth * np.sin(gridtheta) * np.sqrt((a_axis**2) * (b_axis**2) *
                                                    ((np.sin(gridtheta)**2) *
                                                     (np.cos(gridphi)**2) +
                                                     (np.sin(gridtheta)**2) *
                                                     (np.sin(gridphi)**2)) +
                                                    (a_axis**4) *
                                                    (np.cos(gridtheta)**2))
    #-- read land-sea mask to find ocean values
    #-- ocean pressure points will be based on reanalysis mask
    MASK = ncdf_landmask(os.path.join(ddir, input_mask_file), MASKNAME, OCEAN)

    #-- find and read each reanalysis pressure field
    MJD = icesat2_toolkit.time.convert_calendar_dates(int(YY),
                                                      int(MM),
                                                      int(DD),
                                                      epoch=(1858, 11, 17, 0,
                                                             0, 0),
                                                      scale=1.0)
    FILENAMES = find_pressure_files(ddir, MODEL, MJD)
    #-- read sea level pressure and calculate anomalies
    islp, itpx, ilat, imjd = ncdf_pressure(FILENAMES, VARNAME, TIMENAME,
                                           LATNAME, mean_pressure, MASK, AREA)
    #-- create an interpolator for sea level pressure anomalies
    R1 = scipy.interpolate.RegularGridInterpolator((imjd, ilat, lon),
                                                   islp,
                                                   bounds_error=False)
    R2 = scipy.interpolate.RegularGridInterpolator((imjd, ilat, lon),
                                                   itpx,
                                                   bounds_error=False)

    #-- copy variables for outputting to HDF5 file
    IS2_atl06_corr = {}
    IS2_atl06_fill = {}
    IS2_atl06_dims = {}
    IS2_atl06_corr_attrs = {}
    #-- number of GPS seconds between the GPS epoch (1980-01-06T00:00:00Z UTC)
    #-- and ATLAS Standard Data Product (SDP) epoch (2018-01-01T00:00:00Z UTC)
    #-- Add this value to delta time parameters to compute full gps_seconds
    IS2_atl06_corr['ancillary_data'] = {}
    IS2_atl06_corr_attrs['ancillary_data'] = {}
    for key in ['atlas_sdp_gps_epoch']:
        #-- get each HDF5 variable
        IS2_atl06_corr['ancillary_data'][key] = IS2_atl06_mds[
            'ancillary_data'][key]
        #-- Getting attributes of group and included variables
        IS2_atl06_corr_attrs['ancillary_data'][key] = {}
        for att_name, att_val in IS2_atl06_attrs['ancillary_data'][key].items(
        ):
            IS2_atl06_corr_attrs['ancillary_data'][key][att_name] = att_val
    #-- for each input beam within the file
    for gtx in sorted(IS2_atl06_beams):
        #-- output data dictionaries for beam
        IS2_atl06_corr[gtx] = dict(land_ice_segments={})
        IS2_atl06_fill[gtx] = dict(land_ice_segments={})
        IS2_atl06_dims[gtx] = dict(land_ice_segments={})
        IS2_atl06_corr_attrs[gtx] = dict(land_ice_segments={})

        #-- number of segments
        val = IS2_atl06_mds[gtx]['land_ice_segments']
        n_seg = len(val['segment_id'])
        #-- find valid segments for beam
        fv = IS2_atl06_attrs[gtx]['land_ice_segments']['h_li']['_FillValue']
        valid, = np.nonzero(val['h_li'] != fv)

        #-- convert time from ATLAS SDP to Modified Julian Days
        gps_seconds = atlas_sdp_gps_epoch + val['delta_time']
        leap_seconds = icesat2_toolkit.time.count_leap_seconds(gps_seconds)
        MJD = icesat2_toolkit.time.convert_delta_time(
            gps_seconds - leap_seconds,
            epoch1=(1980, 1, 6, 0, 0, 0),
            epoch2=(1858, 11, 17, 0, 0, 0),
            scale=1.0 / 86400.0)

        #-- calculate projected coordinates of input coordinates
        ix, iy = transformer.transform(val['longitude'], val['latitude'])

        #-- colatitudes of the ATL06 measurements
        th = (90.0 - val['latitude']) * np.pi / 180.0
        #-- gravitational acceleration at mean sea level at the equator
        ge = 9.780356
        #-- gravitational acceleration at mean sea level over colatitudes
        #-- from Heiskanen and Moritz, Physical Geodesy, (1967)
        gs = ge * (1.0 + 5.2885e-3 * np.cos(th)**2 -
                   5.9e-6 * np.cos(2.0 * th)**2)

        #-- interpolate sea level pressure anomalies to points
        SLP = R1.__call__(np.c_[MJD[valid], iy[valid], ix[valid]])
        #-- calculate inverse barometer response
        IB = np.ma.zeros((n_seg), fill_value=fv)
        IB.data[valid] = -SLP * (DENSITY * gs[valid])**-1
        #-- interpolate conventional inverse barometer response to points
        TPX = np.ma.zeros((n_seg), fill_value=fv)
        TPX.data[valid] = R2.__call__(np.c_[MJD[valid], iy[valid], ix[valid]])
        #-- replace any nan values with fill value
        IB.mask = (val['h_li'] == fv) | np.isnan(IB.data)
        TPX.mask = (val['h_li'] == fv) | np.isnan(TPX.data)
        IB.data[IB.mask] = IB.fill_value
        TPX.data[TPX.mask] = TPX.fill_value

        #-- group attributes for beam
        IS2_atl06_corr_attrs[gtx]['Description'] = IS2_atl06_attrs[gtx][
            'Description']
        IS2_atl06_corr_attrs[gtx]['atlas_pce'] = IS2_atl06_attrs[gtx][
            'atlas_pce']
        IS2_atl06_corr_attrs[gtx]['atlas_beam_type'] = IS2_atl06_attrs[gtx][
            'atlas_beam_type']
        IS2_atl06_corr_attrs[gtx]['groundtrack_id'] = IS2_atl06_attrs[gtx][
            'groundtrack_id']
        IS2_atl06_corr_attrs[gtx]['atmosphere_profile'] = IS2_atl06_attrs[gtx][
            'atmosphere_profile']
        IS2_atl06_corr_attrs[gtx]['atlas_spot_number'] = IS2_atl06_attrs[gtx][
            'atlas_spot_number']
        IS2_atl06_corr_attrs[gtx]['sc_orientation'] = IS2_atl06_attrs[gtx][
            'sc_orientation']
        #-- group attributes for land_ice_segments
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['Description'] = (
            "The land_ice_segments group "
            "contains the primary set of derived products. This includes geolocation, height, and "
            "standard error and quality measures for each segment. This group is sparse, meaning "
            "that parameters are provided only for pairs of segments for which at least one beam "
            "has a valid surface-height measurement.")
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['data_rate'] = (
            "Data within this group are "
            "sparse.  Data values are provided only for those ICESat-2 20m segments where at "
            "least one beam has a valid land ice height measurement.")

        #-- geolocation, time and segment ID
        #-- delta time
        IS2_atl06_corr[gtx]['land_ice_segments']['delta_time'] = val[
            'delta_time'].copy()
        IS2_atl06_fill[gtx]['land_ice_segments']['delta_time'] = None
        IS2_atl06_dims[gtx]['land_ice_segments']['delta_time'] = None
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['delta_time'] = {}
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['delta_time'][
            'units'] = "seconds since 2018-01-01"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['delta_time'][
            'long_name'] = "Elapsed GPS seconds"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['delta_time'][
            'standard_name'] = "time"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['delta_time'][
            'calendar'] = "standard"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['delta_time'][
            'description'] = (
                "Number of GPS "
                "seconds since the ATLAS SDP epoch. The ATLAS Standard Data Products (SDP) epoch offset "
                "is defined within /ancillary_data/atlas_sdp_gps_epoch as the number of GPS seconds "
                "between the GPS epoch (1980-01-06T00:00:00.000000Z UTC) and the ATLAS SDP epoch. By "
                "adding the offset contained within atlas_sdp_gps_epoch to delta time parameters, the "
                "time in gps_seconds relative to the GPS epoch can be computed."
            )
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['delta_time']['coordinates'] = \
            "segment_id latitude longitude"
        #-- latitude
        IS2_atl06_corr[gtx]['land_ice_segments']['latitude'] = val[
            'latitude'].copy()
        IS2_atl06_fill[gtx]['land_ice_segments']['latitude'] = None
        IS2_atl06_dims[gtx]['land_ice_segments']['latitude'] = ['delta_time']
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude'] = {}
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude'][
            'units'] = "degrees_north"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude'][
            'contentType'] = "physicalMeasurement"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude'][
            'long_name'] = "Latitude"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude'][
            'standard_name'] = "latitude"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude'][
            'description'] = ("Latitude of "
                              "segment center")
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude'][
            'valid_min'] = -90.0
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude'][
            'valid_max'] = 90.0
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude']['coordinates'] = \
            "segment_id delta_time longitude"
        #-- longitude
        IS2_atl06_corr[gtx]['land_ice_segments']['longitude'] = val[
            'longitude'].copy()
        IS2_atl06_fill[gtx]['land_ice_segments']['longitude'] = None
        IS2_atl06_dims[gtx]['land_ice_segments']['longitude'] = ['delta_time']
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude'] = {}
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude'][
            'units'] = "degrees_east"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude'][
            'contentType'] = "physicalMeasurement"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude'][
            'long_name'] = "Longitude"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude'][
            'standard_name'] = "longitude"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude'][
            'description'] = ("Longitude of "
                              "segment center")
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude'][
            'valid_min'] = -180.0
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude'][
            'valid_max'] = 180.0
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude']['coordinates'] = \
            "segment_id delta_time latitude"
        #-- segment ID
        IS2_atl06_corr[gtx]['land_ice_segments']['segment_id'] = val[
            'segment_id']
        IS2_atl06_fill[gtx]['land_ice_segments']['segment_id'] = None
        IS2_atl06_dims[gtx]['land_ice_segments']['segment_id'] = ['delta_time']
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['segment_id'] = {}
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['segment_id'][
            'units'] = "1"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['segment_id'][
            'contentType'] = "referenceInformation"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['segment_id'][
            'long_name'] = "Along-track segment ID number"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['segment_id'][
            'description'] = (
                "A 7 digit number "
                "identifying the along-track geolocation segment number.  These are sequential, starting with "
                "1 for the first segment after an ascending equatorial crossing node. Equal to the segment_id for "
                "the second of the two 20m ATL03 segments included in the 40m ATL06 segment"
            )
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['segment_id']['coordinates'] = \
            "delta_time latitude longitude"

        #-- geophysical variables
        IS2_atl06_corr[gtx]['land_ice_segments']['geophysical'] = {}
        IS2_atl06_fill[gtx]['land_ice_segments']['geophysical'] = {}
        IS2_atl06_dims[gtx]['land_ice_segments']['geophysical'] = {}
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical'] = {}
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical'][
            'Description'] = (
                "The geophysical group "
                "contains parameters used to correct segment heights for geophysical effects, parameters "
                "related to solar background and parameters indicative of the presence or absence of clouds."
            )
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical'][
            'data_rate'] = (
                "Data within this group "
                "are stored at the land_ice_segments segment rate.")

        #-- inverse barometer response
        IS2_atl06_corr[gtx]['land_ice_segments']['geophysical'][
            'ib'] = IB.copy()
        IS2_atl06_fill[gtx]['land_ice_segments']['geophysical'][
            'ib'] = IB.fill_value
        IS2_atl06_dims[gtx]['land_ice_segments']['geophysical']['ib'] = [
            'delta_time'
        ]
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical'][
            'ib'] = {}
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical']['ib'][
            'units'] = "meters"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical']['ib'][
            'contentType'] = "referenceInformation"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical']['ib'][
            'long_name'] = "inverse barometer"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical']['ib'][
            'description'] = ("Instantaneous inverse "
                              "barometer effect due to atmospheric loading")
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical']['ib'][
            'source'] = MODEL
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical']['ib']['reference'] = \
            'https://doi.org/10.1029/96RG03037'
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical']['ib']['coordinates'] = \
            "../segment_id ../delta_time ../latitude ../longitude"
        #-- conventional (TOPEX/POSEIDON) inverse barometer response
        IS2_atl06_corr[gtx]['land_ice_segments']['geophysical'][
            'tpx'] = TPX.copy()
        IS2_atl06_fill[gtx]['land_ice_segments']['geophysical'][
            'tpx'] = TPX.fill_value
        IS2_atl06_dims[gtx]['land_ice_segments']['geophysical']['tpx'] = [
            'delta_time'
        ]
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical'][
            'tpx'] = {}
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical']['tpx'][
            'units'] = "meters"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical']['tpx'][
            'contentType'] = "referenceInformation"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical']['tpx'][
            'long_name'] = "inverse barometer"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical']['tpx'][
            'description'] = (
                "Conventional "
                "(TOPEX/POSEIDON) instantaneous inverse barometer effect due to atmospheric loading"
            )
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical']['tpx'][
            'source'] = MODEL
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical']['tpx']['reference'] = \
            'TOPEX/POSEIDON Project GDR Users Handbook'
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical']['tpx']['coordinates'] = \
            "../segment_id ../delta_time ../latitude ../longitude"

    #-- output HDF5 files with interpolated inverse barometer data
    fargs = (PRD, MODEL, YY, MM, DD, HH, MN, SS, TRK, CYCL, GRAN, RL, VERS,
             AUX)
    file_format = '{0}_{1}_IB_{2}{3}{4}{5}{6}{7}_{8}{9}{10}_{11}_{12}{13}.h5'
    output_file = os.path.join(DIRECTORY, file_format.format(*fargs))
    #-- print file information
    logging.info('\t{0}'.format(output_file))
    HDF5_ATL06_corr_write(IS2_atl06_corr,
                          IS2_atl06_corr_attrs,
                          CLOBBER=True,
                          INPUT=os.path.basename(FILE),
                          FILL_VALUE=IS2_atl06_fill,
                          DIMENSIONS=IS2_atl06_dims,
                          FILENAME=output_file)
    #-- change the permissions mode
    os.chmod(output_file, MODE)
Example #4
0
def compute_tides_ICESat2(tide_dir, FILE, TIDE_MODEL=None, METHOD='spline',
    EXTRAPOLATE=False, VERBOSE=False, MODE=0o775):
    #-- select between tide models
    if (TIDE_MODEL == 'CATS0201'):
        grid_file = os.path.join(tide_dir,'cats0201_tmd','grid_CATS')
        model_file = os.path.join(tide_dir,'cats0201_tmd','h0_CATS02_01')
        reference = 'https://mail.esr.org/polar_tide_models/Model_CATS0201.html'
        variable = 'tide_ocean'
        long_name = "Ocean Tide"
        description = ("Ocean Tides including diurnal and semi-diurnal "
            "(harmonic analysis), and longer period tides (dynamic and "
            "self-consistent equilibrium).")
        model_format = 'OTIS'
        EPSG = '4326'
        TYPE = 'z'
    elif (TIDE_MODEL == 'CATS2008'):
        grid_file = os.path.join(tide_dir,'CATS2008','grid_CATS2008')
        model_file = os.path.join(tide_dir,'CATS2008','hf.CATS2008.out')
        reference = ('https://www.esr.org/research/polar-tide-models/'
            'list-of-polar-tide-models/cats2008/')
        variable = 'tide_ocean'
        long_name = "Ocean Tide"
        description = ("Ocean Tides including diurnal and semi-diurnal "
            "(harmonic analysis), and longer period tides (dynamic and "
            "self-consistent equilibrium).")
        model_format = 'OTIS'
        EPSG = 'CATS2008'
        TYPE = 'z'
    elif (TIDE_MODEL == 'CATS2008_load'):
        grid_file = os.path.join(tide_dir,'CATS2008a_SPOTL_Load','grid_CATS2008a_opt')
        model_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/')
        variable = 'tide_load'
        long_name = "Load Tide"
        description = "Local displacement due to Ocean Loading (-6 to 0 cm)"
        model_format = 'OTIS'
        EPSG = 'CATS2008'
        TYPE = 'z'
    elif (TIDE_MODEL == 'TPXO9-atlas'):
        model_directory = os.path.join(tide_dir,'TPXO9_atlas')
        grid_file = os.path.join(model_directory,'grid_tpxo9_atlas.nc.gz')
        model_files = ['h_q1_tpxo9_atlas_30.nc.gz','h_o1_tpxo9_atlas_30.nc.gz',
            'h_p1_tpxo9_atlas_30.nc.gz','h_k1_tpxo9_atlas_30.nc.gz',
            'h_n2_tpxo9_atlas_30.nc.gz','h_m2_tpxo9_atlas_30.nc.gz',
            'h_s2_tpxo9_atlas_30.nc.gz','h_k2_tpxo9_atlas_30.nc.gz',
            'h_m4_tpxo9_atlas_30.nc.gz','h_ms4_tpxo9_atlas_30.nc.gz',
            'h_mn4_tpxo9_atlas_30.nc.gz','h_2n2_tpxo9_atlas_30.nc.gz']
        model_file = [os.path.join(model_directory,m) for m in model_files]
        reference = 'http://volkov.oce.orst.edu/tides/tpxo9_atlas.html'
        variable = 'tide_ocean'
        long_name = "Ocean Tide"
        description = ("Ocean Tides including diurnal and semi-diurnal "
            "(harmonic analysis), and longer period tides (dynamic and "
            "self-consistent equilibrium).")
        model_format = 'netcdf'
        TYPE = 'z'
        SCALE = 1.0/1000.0
        GZIP = True
    elif (TIDE_MODEL == 'TPXO9-atlas-v2'):
        model_directory = os.path.join(tide_dir,'TPXO9_atlas_v2')
        grid_file = os.path.join(model_directory,'grid_tpxo9_atlas_30_v2.nc.gz')
        model_files = ['h_q1_tpxo9_atlas_30_v2.nc.gz','h_o1_tpxo9_atlas_30_v2.nc.gz',
            'h_p1_tpxo9_atlas_30_v2.nc.gz','h_k1_tpxo9_atlas_30_v2.nc.gz',
            'h_n2_tpxo9_atlas_30_v2.nc.gz','h_m2_tpxo9_atlas_30_v2.nc.gz',
            'h_s2_tpxo9_atlas_30_v2.nc.gz','h_k2_tpxo9_atlas_30_v2.nc.gz',
            'h_m4_tpxo9_atlas_30_v2.nc.gz','h_ms4_tpxo9_atlas_30_v2.nc.gz',
            'h_mn4_tpxo9_atlas_30_v2.nc.gz','h_2n2_tpxo9_atlas_30_v2.nc.gz']
        model_file = [os.path.join(model_directory,m) for m in model_files]
        reference = 'https://www.tpxo.net/global/tpxo9-atlas'
        variable = 'tide_ocean'
        long_name = "Ocean Tide"
        description = ("Ocean Tides including diurnal and semi-diurnal "
            "(harmonic analysis), and longer period tides (dynamic and "
            "self-consistent equilibrium).")
        model_format = 'netcdf'
        TYPE = 'z'
        SCALE = 1.0/1000.0
        GZIP = True
    elif (TIDE_MODEL == 'TPXO9-atlas-v3'):
        model_directory = os.path.join(tide_dir,'TPXO9_atlas_v3')
        grid_file = os.path.join(model_directory,'grid_tpxo9_atlas_30_v3.nc.gz')
        model_files = ['h_q1_tpxo9_atlas_30_v3.nc.gz','h_o1_tpxo9_atlas_30_v3.nc.gz',
            'h_p1_tpxo9_atlas_30_v3.nc.gz','h_k1_tpxo9_atlas_30_v3.nc.gz',
            'h_n2_tpxo9_atlas_30_v3.nc.gz','h_m2_tpxo9_atlas_30_v3.nc.gz',
            'h_s2_tpxo9_atlas_30_v3.nc.gz','h_k2_tpxo9_atlas_30_v3.nc.gz',
            'h_m4_tpxo9_atlas_30_v3.nc.gz','h_ms4_tpxo9_atlas_30_v3.nc.gz',
            'h_mn4_tpxo9_atlas_30_v3.nc.gz','h_2n2_tpxo9_atlas_30_v3.nc.gz',
            'h_mf_tpxo9_atlas_30_v3.nc.gz','h_mm_tpxo9_atlas_30_v3.nc.gz']
        model_file = [os.path.join(model_directory,m) for m in model_files]
        reference = 'https://www.tpxo.net/global/tpxo9-atlas'
        variable = 'tide_ocean'
        long_name = "Ocean Tide"
        description = ("Ocean Tides including diurnal and semi-diurnal "
            "(harmonic analysis), and longer period tides (dynamic and "
            "self-consistent equilibrium).")
        model_format = 'netcdf'
        TYPE = 'z'
        SCALE = 1.0/1000.0
        GZIP = True
    elif (TIDE_MODEL == 'TPXO9-atlas-v4'):
        model_directory = os.path.join(tide_dir,'TPXO9_atlas_v4')
        grid_file = os.path.join(model_directory,'grid_tpxo9_atlas_30_v4')
        model_files = ['h_q1_tpxo9_atlas_30_v4','h_o1_tpxo9_atlas_30_v4',
            'h_p1_tpxo9_atlas_30_v4','h_k1_tpxo9_atlas_30_v4',
            'h_n2_tpxo9_atlas_30_v4','h_m2_tpxo9_atlas_30_v4',
            'h_s2_tpxo9_atlas_30_v4','h_k2_tpxo9_atlas_30_v4',
            'h_m4_tpxo9_atlas_30_v4','h_ms4_tpxo9_atlas_30_v4',
            'h_mn4_tpxo9_atlas_30_v4','h_2n2_tpxo9_atlas_30_v4',
            'h_mf_tpxo9_atlas_30_v4','h_mm_tpxo9_atlas_30_v4']
        model_file = [os.path.join(model_directory,m) for m in model_files]
        reference = 'https://www.tpxo.net/global/tpxo9-atlas'
        variable = 'tide_ocean'
        long_name = "Ocean Tide"
        description = ("Ocean Tides including diurnal and semi-diurnal "
            "(harmonic analysis), and longer period tides (dynamic and "
            "self-consistent equilibrium).")
        model_format = 'OTIS'
        EPSG = '4326'
        TYPE = 'z'
    elif (TIDE_MODEL == 'TPXO9.1'):
        grid_file = os.path.join(tide_dir,'TPXO9.1','DATA','grid_tpxo9')
        model_file = os.path.join(tide_dir,'TPXO9.1','DATA','h_tpxo9.v1')
        reference = 'http://volkov.oce.orst.edu/tides/global.html'
        variable = 'tide_ocean'
        long_name = "Ocean Tide"
        description = ("Ocean Tides including diurnal and semi-diurnal "
            "(harmonic analysis), and longer period tides (dynamic and "
            "self-consistent equilibrium).")
        model_format = 'OTIS'
        EPSG = '4326'
        TYPE = 'z'
    elif (TIDE_MODEL == 'TPXO8-atlas'):
        grid_file = os.path.join(tide_dir,'tpxo8_atlas','grid_tpxo8atlas_30_v1')
        model_file = os.path.join(tide_dir,'tpxo8_atlas','hf.tpxo8_atlas_30_v1')
        reference = 'http://volkov.oce.orst.edu/tides/tpxo8_atlas.html'
        variable = 'tide_ocean'
        long_name = "Ocean Tide"
        description = ("Ocean Tides including diurnal and semi-diurnal "
            "(harmonic analysis), and longer period tides (dynamic and "
            "self-consistent equilibrium).")
        model_format = 'ATLAS'
        EPSG = '4326'
        TYPE = 'z'
    elif (TIDE_MODEL == 'TPXO7.2'):
        grid_file = os.path.join(tide_dir,'TPXO7.2_tmd','grid_tpxo7.2')
        model_file = os.path.join(tide_dir,'TPXO7.2_tmd','h_tpxo7.2')
        reference = 'http://volkov.oce.orst.edu/tides/global.html'
        variable = 'tide_ocean'
        long_name = "Ocean Tide"
        description = ("Ocean Tides including diurnal and semi-diurnal "
            "(harmonic analysis), and longer period tides (dynamic and "
            "self-consistent equilibrium).")
        model_format = 'OTIS'
        EPSG = '4326'
        TYPE = 'z'
    elif (TIDE_MODEL == 'TPXO7.2_load'):
        grid_file = os.path.join(tide_dir,'TPXO7.2_load','grid_tpxo6.2')
        model_file = os.path.join(tide_dir,'TPXO7.2_load','h_tpxo7.2_load')
        reference = 'http://volkov.oce.orst.edu/tides/global.html'
        variable = 'tide_load'
        long_name = "Load Tide"
        description = "Local displacement due to Ocean Loading (-6 to 0 cm)"
        model_format = 'OTIS'
        EPSG = '4326'
        TYPE = 'z'
    elif (TIDE_MODEL == 'AODTM-5'):
        grid_file = os.path.join(tide_dir,'aodtm5_tmd','grid_Arc5km')
        model_file = os.path.join(tide_dir,'aodtm5_tmd','h0_Arc5km.oce')
        reference = ('https://www.esr.org/research/polar-tide-models/'
            'list-of-polar-tide-models/aodtm-5/')
        variable = 'tide_ocean'
        long_name = "Ocean Tide"
        description = ("Ocean Tides including diurnal and semi-diurnal "
            "(harmonic analysis), and longer period tides (dynamic and "
            "self-consistent equilibrium).")
        model_format = 'OTIS'
        EPSG = 'PSNorth'
        TYPE = 'z'
    elif (TIDE_MODEL == 'AOTIM-5'):
        grid_file = os.path.join(tide_dir,'aotim5_tmd','grid_Arc5km')
        model_file = os.path.join(tide_dir,'aotim5_tmd','h_Arc5km.oce')
        reference = ('https://www.esr.org/research/polar-tide-models/'
            'list-of-polar-tide-models/aotim-5/')
        variable = 'tide_ocean'
        long_name = "Ocean Tide"
        description = ("Ocean Tides including diurnal and semi-diurnal "
            "(harmonic analysis), and longer period tides (dynamic and "
            "self-consistent equilibrium).")
        model_format = 'OTIS'
        EPSG = 'PSNorth'
        TYPE = 'z'
    elif (TIDE_MODEL == 'AOTIM-5-2018'):
        grid_file = os.path.join(tide_dir,'Arc5km2018','grid_Arc5km2018')
        model_file = os.path.join(tide_dir,'Arc5km2018','h_Arc5km2018')
        reference = ('https://www.esr.org/research/polar-tide-models/'
            'list-of-polar-tide-models/aotim-5/')
        variable = 'tide_ocean'
        long_name = "Ocean Tide"
        description = ("Ocean Tides including diurnal and semi-diurnal "
            "(harmonic analysis), and longer period tides (dynamic and "
            "self-consistent equilibrium).")
        model_format = 'OTIS'
        EPSG = 'PSNorth'
        TYPE = 'z'
    elif (TIDE_MODEL == 'GOT4.7'):
        model_directory = os.path.join(tide_dir,'GOT4.7','grids_oceantide')
        model_files = ['q1.d.gz','o1.d.gz','p1.d.gz','k1.d.gz','n2.d.gz',
            'm2.d.gz','s2.d.gz','k2.d.gz','s1.d.gz','m4.d.gz']
        model_file = [os.path.join(model_directory,m) for m in model_files]
        c = ['q1','o1','p1','k1','n2','m2','s2','k2','s1','m4']
        reference = ('https://denali.gsfc.nasa.gov/personal_pages/ray/'
            'MiscPubs/19990089548_1999150788.pdf')
        variable = 'tide_ocean'
        long_name = "Ocean Tide"
        description = ("Ocean Tides including diurnal and semi-diurnal "
            "(harmonic analysis), and longer period tides (dynamic and "
            "self-consistent equilibrium).")
        model_format = 'GOT'
        SCALE = 1.0/100.0
        GZIP = True
    elif (TIDE_MODEL == 'GOT4.7_load'):
        model_directory = os.path.join(tide_dir,'GOT4.7','grids_loadtide')
        model_files = ['q1load.d.gz','o1load.d.gz','p1load.d.gz','k1load.d.gz',
            'n2load.d.gz','m2load.d.gz','s2load.d.gz','k2load.d.gz',
            's1load.d.gz','m4load.d.gz']
        model_file = [os.path.join(model_directory,m) for m in model_files]
        c = ['q1','o1','p1','k1','n2','m2','s2','k2','s1','m4']
        reference = ('https://denali.gsfc.nasa.gov/personal_pages/ray/'
            'MiscPubs/19990089548_1999150788.pdf')
        variable = 'tide_load'
        long_name = "Load Tide"
        description = "Local displacement due to Ocean Loading (-6 to 0 cm)"
        model_format = 'GOT'
        SCALE = 1.0/1000.0
        GZIP = True
    elif (TIDE_MODEL == 'GOT4.8'):
        model_directory = os.path.join(tide_dir,'got4.8','grids_oceantide')
        model_files = ['q1.d.gz','o1.d.gz','p1.d.gz','k1.d.gz','n2.d.gz',
            'm2.d.gz','s2.d.gz','k2.d.gz','s1.d.gz','m4.d.gz']
        model_file = [os.path.join(model_directory,m) for m in model_files]
        c = ['q1','o1','p1','k1','n2','m2','s2','k2','s1','m4']
        reference = ('https://denali.gsfc.nasa.gov/personal_pages/ray/'
            'MiscPubs/19990089548_1999150788.pdf')
        variable = 'tide_ocean'
        long_name = "Ocean Tide"
        description = ("Ocean Tides including diurnal and semi-diurnal "
            "(harmonic analysis), and longer period tides (dynamic and "
            "self-consistent equilibrium).")
        model_format = 'GOT'
        SCALE = 1.0/100.0
        GZIP = True
    elif (TIDE_MODEL == 'GOT4.8_load'):
        model_directory = os.path.join(tide_dir,'got4.8','grids_loadtide')
        model_files = ['q1load.d.gz','o1load.d.gz','p1load.d.gz','k1load.d.gz',
            'n2load.d.gz','m2load.d.gz','s2load.d.gz','k2load.d.gz',
            's1load.d.gz','m4load.d.gz']
        model_file = [os.path.join(model_directory,m) for m in model_files]
        c = ['q1','o1','p1','k1','n2','m2','s2','k2','s1','m4']
        reference = ('https://denali.gsfc.nasa.gov/personal_pages/ray/'
            'MiscPubs/19990089548_1999150788.pdf')
        variable = 'tide_load'
        long_name = "Load Tide"
        description = "Local displacement due to Ocean Loading (-6 to 0 cm)"
        model_format = 'GOT'
        SCALE = 1.0/1000.0
        GZIP = True
    elif (TIDE_MODEL == 'GOT4.10'):
        model_directory = os.path.join(tide_dir,'GOT4.10c','grids_oceantide')
        model_files = ['q1.d.gz','o1.d.gz','p1.d.gz','k1.d.gz','n2.d.gz',
            'm2.d.gz','s2.d.gz','k2.d.gz','s1.d.gz','m4.d.gz']
        model_file = [os.path.join(model_directory,m) for m in model_files]
        c = ['q1','o1','p1','k1','n2','m2','s2','k2','s1','m4']
        reference = ('https://denali.gsfc.nasa.gov/personal_pages/ray/'
            'MiscPubs/19990089548_1999150788.pdf')
        variable = 'tide_ocean'
        long_name = "Ocean Tide"
        description = ("Ocean Tides including diurnal and semi-diurnal "
            "(harmonic analysis), and longer period tides (dynamic and "
            "self-consistent equilibrium).")
        model_format = 'GOT'
        SCALE = 1.0/100.0
        GZIP = True
    elif (TIDE_MODEL == 'GOT4.10_load'):
        model_directory = os.path.join(tide_dir,'GOT4.10c','grids_loadtide')
        model_files = ['q1load.d.gz','o1load.d.gz','p1load.d.gz','k1load.d.gz',
            'n2load.d.gz','m2load.d.gz','s2load.d.gz','k2load.d.gz',
            's1load.d.gz','m4load.d.gz']
        model_file = [os.path.join(model_directory,m) for m in model_files]
        c = ['q1','o1','p1','k1','n2','m2','s2','k2','s1','m4']
        reference = ('https://denali.gsfc.nasa.gov/personal_pages/ray/'
            'MiscPubs/19990089548_1999150788.pdf')
        variable = 'tide_load'
        long_name = "Load Tide"
        description = "Local displacement due to Ocean Loading (-6 to 0 cm)"
        model_format = 'GOT'
        SCALE = 1.0/1000.0
        GZIP = True
    elif (TIDE_MODEL == 'FES2014'):
        model_directory = os.path.join(tide_dir,'fes2014','ocean_tide')
        model_files = ['2n2.nc.gz','eps2.nc.gz','j1.nc.gz','k1.nc.gz',
            'k2.nc.gz','l2.nc.gz','la2.nc.gz','m2.nc.gz','m3.nc.gz','m4.nc.gz',
            'm6.nc.gz','m8.nc.gz','mf.nc.gz','mks2.nc.gz','mm.nc.gz',
            'mn4.nc.gz','ms4.nc.gz','msf.nc.gz','msqm.nc.gz','mtm.nc.gz',
            'mu2.nc.gz','n2.nc.gz','n4.nc.gz','nu2.nc.gz','o1.nc.gz','p1.nc.gz',
            'q1.nc.gz','r2.nc.gz','s1.nc.gz','s2.nc.gz','s4.nc.gz','sa.nc.gz',
            'ssa.nc.gz','t2.nc.gz']
        model_file = [os.path.join(model_directory,m) for m in model_files]
        c = ['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']
        reference = ('https://www.aviso.altimetry.fr/en/data/products'
            'auxiliary-products/global-tide-fes.html')
        variable = 'tide_ocean'
        long_name = "Ocean Tide"
        description = ("Ocean Tides including diurnal and semi-diurnal "
            "(harmonic analysis), and longer period tides (dynamic and "
            "self-consistent equilibrium).")
        model_format = 'FES'
        TYPE = 'z'
        SCALE = 1.0/100.0
        GZIP = True
    elif (TIDE_MODEL == 'FES2014_load'):
        model_directory = os.path.join(tide_dir,'fes2014','load_tide')
        model_files = ['2n2.nc.gz','eps2.nc.gz','j1.nc.gz','k1.nc.gz',
            'k2.nc.gz','l2.nc.gz','la2.nc.gz','m2.nc.gz','m3.nc.gz','m4.nc.gz',
            'm6.nc.gz','m8.nc.gz','mf.nc.gz','mks2.nc.gz','mm.nc.gz',
            'mn4.nc.gz','ms4.nc.gz','msf.nc.gz','msqm.nc.gz','mtm.nc.gz',
            'mu2.nc.gz','n2.nc.gz','n4.nc.gz','nu2.nc.gz','o1.nc.gz','p1.nc.gz',
            'q1.nc.gz','r2.nc.gz','s1.nc.gz','s2.nc.gz','s4.nc.gz','sa.nc.gz',
            'ssa.nc.gz','t2.nc.gz']
        model_file = [os.path.join(model_directory,m) for m in model_files]
        c = ['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']
        reference = ('https://www.aviso.altimetry.fr/en/data/products'
            'auxiliary-products/global-tide-fes.html')
        variable = 'tide_load'
        long_name = "Load Tide"
        description = "Local displacement due to Ocean Loading (-6 to 0 cm)"
        model_format = 'FES'
        TYPE = 'z'
        SCALE = 1.0/100.0
        GZIP = True

    #-- read data from FILE
    print('{0} -->'.format(os.path.basename(FILE))) if VERBOSE else None
    IS2_atl06_mds,IS2_atl06_attrs,IS2_atl06_beams = read_HDF5_ATL06(FILE,
        ATTRIBUTES=True)
    DIRECTORY = os.path.dirname(FILE)
    #-- extract parameters from ICESat-2 ATLAS HDF5 file name
    rx = re.compile(r'(processed_)?(ATL\d{2})_(\d{4})(\d{2})(\d{2})(\d{2})'
        r'(\d{2})(\d{2})_(\d{4})(\d{2})(\d{2})_(\d{3})_(\d{2})(.*?).h5$')
    SUB,PRD,YY,MM,DD,HH,MN,SS,TRK,CYCL,GRAN,RL,VERS,AUX = rx.findall(FILE).pop()

    #-- number of GPS seconds between the GPS epoch
    #-- and ATLAS Standard Data Product (SDP) epoch
    atlas_sdp_gps_epoch = IS2_atl06_mds['ancillary_data']['atlas_sdp_gps_epoch']
    #-- delta time (TT - UT1) file
    delta_file = pyTMD.utilities.get_data_path(['data','merged_deltat.data'])

    #-- copy variables for outputting to HDF5 file
    IS2_atl06_tide = {}
    IS2_atl06_fill = {}
    IS2_atl06_dims = {}
    IS2_atl06_tide_attrs = {}
    #-- number of GPS seconds between the GPS epoch (1980-01-06T00:00:00Z UTC)
    #-- and ATLAS Standard Data Product (SDP) epoch (2018-01-01T00:00:00Z UTC)
    #-- Add this value to delta time parameters to compute full gps_seconds
    IS2_atl06_tide['ancillary_data'] = {}
    IS2_atl06_tide_attrs['ancillary_data'] = {}
    for key in ['atlas_sdp_gps_epoch']:
        #-- get each HDF5 variable
        IS2_atl06_tide['ancillary_data'][key] = IS2_atl06_mds['ancillary_data'][key]
        #-- Getting attributes of group and included variables
        IS2_atl06_tide_attrs['ancillary_data'][key] = {}
        for att_name,att_val in IS2_atl06_attrs['ancillary_data'][key].items():
            IS2_atl06_tide_attrs['ancillary_data'][key][att_name] = att_val

    #-- for each input beam within the file
    for gtx in sorted(IS2_atl06_beams):
        #-- output data dictionaries for beam
        IS2_atl06_tide[gtx] = dict(land_ice_segments={})
        IS2_atl06_fill[gtx] = dict(land_ice_segments={})
        IS2_atl06_dims[gtx] = dict(land_ice_segments={})
        IS2_atl06_tide_attrs[gtx] = dict(land_ice_segments={})

        #-- number of segments
        val = IS2_atl06_mds[gtx]['land_ice_segments']
        n_seg = len(val['segment_id'])
        #-- find valid segments for beam
        fv = IS2_atl06_attrs[gtx]['land_ice_segments']['h_li']['_FillValue']

        #-- convert time from ATLAS SDP to days relative to Jan 1, 1992
        gps_seconds = atlas_sdp_gps_epoch + val['delta_time']
        leap_seconds = pyTMD.time.count_leap_seconds(gps_seconds)
        tide_time = pyTMD.time.convert_delta_time(gps_seconds-leap_seconds,
            epoch1=(1980,1,6,0,0,0), epoch2=(1992,1,1,0,0,0), scale=1.0/86400.0)
        #-- read tidal constants and interpolate to grid points
        if model_format in ('OTIS','ATLAS'):
            amp,ph,D,c = extract_tidal_constants(val['longitude'],
                val['latitude'], grid_file, model_file, EPSG, TYPE=TYPE,
                METHOD=METHOD, EXTRAPOLATE=EXTRAPOLATE, GRID=model_format)
            deltat = np.zeros_like(tide_time)
        elif (model_format == 'netcdf'):
            amp,ph,D,c = extract_netcdf_constants(val['longitude'],
                val['latitude'], grid_file, model_file, TYPE=TYPE, METHOD=METHOD,
                EXTRAPOLATE=EXTRAPOLATE, SCALE=SCALE, GZIP=GZIP)
            deltat = np.zeros_like(tide_time)
        elif (model_format == 'GOT'):
            amp,ph,c = extract_GOT_constants(val['longitude'], val['latitude'],
                model_file, METHOD=METHOD, EXTRAPOLATE=EXTRAPOLATE, SCALE=SCALE,
                GZIP=GZIP)
            #-- interpolate delta times from calendar dates to tide time
            deltat = calc_delta_time(delta_file, tide_time)
        elif (model_format == 'FES'):
            amp,ph = extract_FES_constants(val['longitude'], val['latitude'],
                model_file, TYPE=TYPE, VERSION=TIDE_MODEL, METHOD=METHOD,
                EXTRAPOLATE=EXTRAPOLATE, SCALE=SCALE, GZIP=GZIP)
            #-- interpolate delta times from calendar dates to tide time
            deltat = calc_delta_time(delta_file, tide_time)

        #-- calculate complex phase in radians for Euler's
        cph = -1j*ph*np.pi/180.0
        #-- calculate constituent oscillation
        hc = amp*np.exp(cph)

        #-- predict tidal elevations at time and infer minor corrections
        tide = np.ma.empty((n_seg),fill_value=fv)
        tide.mask = np.any(hc.mask,axis=1)
        tide.data[:] = predict_tide_drift(tide_time, hc, c,
            DELTAT=deltat, CORRECTIONS=model_format)
        minor = infer_minor_corrections(tide_time, hc, c,
            DELTAT=deltat, CORRECTIONS=model_format)
        tide.data[:] += minor.data[:]
        #-- replace masked and nan values with fill value
        invalid, = np.nonzero(np.isnan(tide.data) | tide.mask)
        tide.data[invalid] = tide.fill_value
        tide.mask[invalid] = True

        #-- group attributes for beam
        IS2_atl06_tide_attrs[gtx]['Description'] = IS2_atl06_attrs[gtx]['Description']
        IS2_atl06_tide_attrs[gtx]['atlas_pce'] = IS2_atl06_attrs[gtx]['atlas_pce']
        IS2_atl06_tide_attrs[gtx]['atlas_beam_type'] = IS2_atl06_attrs[gtx]['atlas_beam_type']
        IS2_atl06_tide_attrs[gtx]['groundtrack_id'] = IS2_atl06_attrs[gtx]['groundtrack_id']
        IS2_atl06_tide_attrs[gtx]['atmosphere_profile'] = IS2_atl06_attrs[gtx]['atmosphere_profile']
        IS2_atl06_tide_attrs[gtx]['atlas_spot_number'] = IS2_atl06_attrs[gtx]['atlas_spot_number']
        IS2_atl06_tide_attrs[gtx]['sc_orientation'] = IS2_atl06_attrs[gtx]['sc_orientation']
        #-- group attributes for land_ice_segments
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['Description'] = ("The land_ice_segments group "
            "contains the primary set of derived products. This includes geolocation, height, and "
            "standard error and quality measures for each segment. This group is sparse, meaning "
            "that parameters are provided only for pairs of segments for which at least one beam "
            "has a valid surface-height measurement.")
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['data_rate'] = ("Data within this group are "
            "sparse.  Data values are provided only for those ICESat-2 20m segments where at "
            "least one beam has a valid land ice height measurement.")

        #-- geolocation, time and segment ID
        #-- delta time
        delta_time = np.ma.array(val['delta_time'], fill_value=fv,
            mask=(val['delta_time']==fv))
        IS2_atl06_tide[gtx]['land_ice_segments']['delta_time'] = delta_time
        IS2_atl06_fill[gtx]['land_ice_segments']['delta_time'] = delta_time.fill_value
        IS2_atl06_dims[gtx]['land_ice_segments']['delta_time'] = None
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['delta_time'] = {}
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['delta_time']['units'] = "seconds since 2018-01-01"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['delta_time']['long_name'] = "Elapsed GPS seconds"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['delta_time']['standard_name'] = "time"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['delta_time']['calendar'] = "standard"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['delta_time']['description'] = ("Number of GPS "
            "seconds since the ATLAS SDP epoch. The ATLAS Standard Data Products (SDP) epoch offset "
            "is defined within /ancillary_data/atlas_sdp_gps_epoch as the number of GPS seconds "
            "between the GPS epoch (1980-01-06T00:00:00.000000Z UTC) and the ATLAS SDP epoch. By "
            "adding the offset contained within atlas_sdp_gps_epoch to delta time parameters, the "
            "time in gps_seconds relative to the GPS epoch can be computed.")
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['delta_time']['coordinates'] = \
            "segment_id latitude longitude"
        #-- latitude
        latitude = np.ma.array(val['latitude'], fill_value=fv,
            mask=(val['latitude']==fv))
        IS2_atl06_tide[gtx]['land_ice_segments']['latitude'] = latitude
        IS2_atl06_fill[gtx]['land_ice_segments']['latitude'] = latitude.fill_value
        IS2_atl06_dims[gtx]['land_ice_segments']['latitude'] = ['delta_time']
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude'] = {}
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude']['units'] = "degrees_north"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude']['contentType'] = "physicalMeasurement"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude']['long_name'] = "Latitude"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude']['standard_name'] = "latitude"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude']['description'] = ("Latitude of "
            "segment center")
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude']['valid_min'] = -90.0
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude']['valid_max'] = 90.0
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['latitude']['coordinates'] = \
            "segment_id delta_time longitude"
        #-- longitude
        longitude = np.ma.array(val['longitude'], fill_value=fv,
            mask=(val['longitude']==fv))
        IS2_atl06_tide[gtx]['land_ice_segments']['longitude'] = longitude
        IS2_atl06_fill[gtx]['land_ice_segments']['longitude'] = longitude.fill_value
        IS2_atl06_dims[gtx]['land_ice_segments']['longitude'] = ['delta_time']
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude'] = {}
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude']['units'] = "degrees_east"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude']['contentType'] = "physicalMeasurement"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude']['long_name'] = "Longitude"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude']['standard_name'] = "longitude"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude']['description'] = ("Longitude of "
            "segment center")
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude']['valid_min'] = -180.0
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude']['valid_max'] = 180.0
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['longitude']['coordinates'] = \
            "segment_id delta_time latitude"
        #-- segment ID
        IS2_atl06_tide[gtx]['land_ice_segments']['segment_id'] = val['segment_id']
        IS2_atl06_fill[gtx]['land_ice_segments']['segment_id'] = None
        IS2_atl06_dims[gtx]['land_ice_segments']['segment_id'] = ['delta_time']
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['segment_id'] = {}
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['segment_id']['units'] = "1"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['segment_id']['contentType'] = "referenceInformation"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['segment_id']['long_name'] = "Along-track segment ID number"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['segment_id']['description'] = ("A 7 digit number "
            "identifying the along-track geolocation segment number.  These are sequential, starting with "
            "1 for the first segment after an ascending equatorial crossing node. Equal to the segment_id for "
            "the second of the two 20m ATL03 segments included in the 40m ATL06 segment")
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['segment_id']['coordinates'] = \
            "delta_time latitude longitude"

        #-- geophysical variables
        IS2_atl06_tide[gtx]['land_ice_segments']['geophysical'] = {}
        IS2_atl06_fill[gtx]['land_ice_segments']['geophysical'] = {}
        IS2_atl06_dims[gtx]['land_ice_segments']['geophysical'] = {}
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical'] = {}
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical']['Description'] = ("The geophysical group "
            "contains parameters used to correct segment heights for geophysical effects, parameters "
            "related to solar background and parameters indicative of the presence or absence of clouds.")
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical']['data_rate'] = ("Data within this group "
            "are stored at the land_ice_segments segment rate.")
        #-- computed tide
        IS2_atl06_tide[gtx]['land_ice_segments']['geophysical'][variable] = tide
        IS2_atl06_fill[gtx]['land_ice_segments']['geophysical'][variable] = tide.fill_value
        IS2_atl06_dims[gtx]['land_ice_segments']['geophysical'][variable] = ['delta_time']
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical'][variable] = {}
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical'][variable]['units'] = "meters"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical'][variable]['contentType'] = "referenceInformation"
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical'][variable]['long_name'] = long_name
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical'][variable]['description'] = description
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical'][variable]['source'] = TIDE_MODEL
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical'][variable]['reference'] = reference
        IS2_atl06_tide_attrs[gtx]['land_ice_segments']['geophysical'][variable]['coordinates'] = \
            "../segment_id ../delta_time ../latitude ../longitude"

    #-- output tidal HDF5 file
    args = (PRD,TIDE_MODEL,YY,MM,DD,HH,MN,SS,TRK,CYCL,GRAN,RL,VERS,AUX)
    file_format = '{0}_{1}_TIDES_{2}{3}{4}{5}{6}{7}_{8}{9}{10}_{11}_{12}{13}.h5'
    #-- print file information
    print('\t{0}'.format(file_format.format(*args))) if VERBOSE else None
    HDF5_ATL06_tide_write(IS2_atl06_tide, IS2_atl06_tide_attrs,
        CLOBBER=True, INPUT=os.path.basename(FILE),
        FILL_VALUE=IS2_atl06_fill, DIMENSIONS=IS2_atl06_dims,
        FILENAME=os.path.join(DIRECTORY,file_format.format(*args)))
    #-- change the permissions mode
    os.chmod(os.path.join(DIRECTORY,file_format.format(*args)), MODE)
Example #5
0
def interp_sea_level_ICESat2(base_dir, FILE, VERBOSE=False, MODE=0o775):

    #-- create logger
    loglevel = logging.INFO if VERBOSE else logging.CRITICAL
    logging.basicConfig(level=loglevel)

    #-- read data from input file
    logging.info('{0} -->'.format(os.path.basename(FILE)))
    IS2_atl06_mds,IS2_atl06_attrs,IS2_atl06_beams = read_HDF5_ATL06(FILE,
        ATTRIBUTES=True)
    DIRECTORY = os.path.dirname(FILE)
    #-- extract parameters from ICESat-2 ATLAS HDF5 file name
    rx = re.compile(r'(processed_)?(ATL\d{2})_(\d{4})(\d{2})(\d{2})(\d{2})'
        r'(\d{2})(\d{2})_(\d{4})(\d{2})(\d{2})_(\d{3})_(\d{2})(.*?).h5$')
    SUB,PRD,YY,MM,DD,HH,MN,SS,TRK,CYCL,GRAN,RL,VERS,AUX = rx.findall(FILE).pop()
    #-- set the hemisphere flag based on ICESat-2 granule
    HEM = set_hemisphere(GRAN)

    #-- HDF5 file attributes
    attrib = {}
    #-- mean dynamic topography
    attrib['h_mdt'] = {}
    attrib['h_mdt']['long_name'] = 'Mean Dynamic Topography'
    attrib['h_mdt']['description'] = 'Sea surface height above geoid'
    attrib['h_mdt']['reference'] = ('https://www.aviso.altimetry.fr/en/data/'
        'products/sea-surface-height-products/global/msla-h.html')
    #-- sea level anomalies
    attrib['h_sla'] = {}
    attrib['h_sla']['long_name'] = 'Sea Level Anomaly'
    attrib['h_sla']['description'] = 'Sea surface anomalies'
    attrib['h_sla']['reference'] = ('https://www.aviso.altimetry.fr/en/data/'
        'products/sea-surface-height-products/global/msla-h.html')
    #-- absolute dynamic topography
    attrib['h_adt'] = {}
    attrib['h_adt']['long_name'] = 'Absolute Dynamic Topography'
    attrib['h_adt']['description'] = ('Sea surface height above geoid calculated '
        'by adding the mean dynamic topography to the sea level anomalies')
    attrib['h_adt']['reference'] = ('https://www.aviso.altimetry.fr/en/data/'
        'products/sea-surface-height-products/global/msla-h.html')

    #-- EPSG projections for converting lat/lon to polar stereographic
    EPSG = dict(N=3413,S=3031)
    #-- pyproj transformer for converting to polar stereographic
    crs1 = pyproj.CRS.from_string("epsg:{0:d}".format(4326))
    crs2 = pyproj.CRS.from_string("epsg:{0:d}".format(EPSG[HEM]))
    transformer = pyproj.Transformer.from_crs(crs1, crs2, always_xy=True)

    #-- number of GPS seconds between the GPS epoch
    #-- and ATLAS Standard Data Product (SDP) epoch
    atlas_sdp_gps_epoch = IS2_atl06_mds['ancillary_data']['atlas_sdp_gps_epoch']

    #-- copy variables for outputting to HDF5 file
    IS2_atl06_corr = {}
    IS2_atl06_fill = {}
    IS2_atl06_dims = {}
    IS2_atl06_corr_attrs = {}
    #-- number of GPS seconds between the GPS epoch (1980-01-06T00:00:00Z UTC)
    #-- and ATLAS Standard Data Product (SDP) epoch (2018-01-01T00:00:00Z UTC)
    #-- Add this value to delta time parameters to compute full gps_seconds
    IS2_atl06_corr['ancillary_data'] = {}
    IS2_atl06_corr_attrs['ancillary_data'] = {}
    for key in ['atlas_sdp_gps_epoch']:
        #-- get each HDF5 variable
        IS2_atl06_corr['ancillary_data'][key] = IS2_atl06_mds['ancillary_data'][key]
        #-- Getting attributes of group and included variables
        IS2_atl06_corr_attrs['ancillary_data'][key] = {}
        for att_name,att_val in IS2_atl06_attrs['ancillary_data'][key].items():
            IS2_atl06_corr_attrs['ancillary_data'][key][att_name] = att_val

    #-- for each input beam within the file
    for gtx in sorted(IS2_atl06_beams):
        #-- output data dictionaries for beam
        IS2_atl06_corr[gtx] = dict(land_ice_segments={})
        IS2_atl06_fill[gtx] = dict(land_ice_segments={})
        IS2_atl06_dims[gtx] = dict(land_ice_segments={})
        IS2_atl06_corr_attrs[gtx] = dict(land_ice_segments={})

        #-- number of segments
        val = IS2_atl06_mds[gtx]['land_ice_segments']
        n_seg = len(val['segment_id'])
        #-- find valid segments for beam
        fv = IS2_atl06_attrs[gtx]['land_ice_segments']['h_li']['_FillValue']
        mask = (val['h_li'] == fv)
        valid, = np.nonzero(np.logical_not(mask))

        #-- convert time from ATLAS SDP to CNES JD
        #-- days relative to 1950-01-01T00:00:00
        gps_seconds = atlas_sdp_gps_epoch + val['delta_time']
        leap_seconds = icesat2_toolkit.time.count_leap_seconds(gps_seconds)
        cnes_time = icesat2_toolkit.time.convert_delta_time(gps_seconds-leap_seconds,
            epoch1=(1980,1,6,0,0,0), epoch2=(1950,1,1,0,0,0), scale=1.0/86400.0)

        #-- extract lat/lon and convert to polar stereographic
        X,Y = transformer.transform(val['longitude'],val['latitude'])

        #-- interpolate sea level anomalies and dynamic topographies
        interp = interpolate_sea_level(base_dir,X,Y,cnes_time,HEM)

        #-- group attributes for beam
        IS2_atl06_corr_attrs[gtx]['Description'] = IS2_atl06_attrs[gtx]['Description']
        IS2_atl06_corr_attrs[gtx]['atlas_pce'] = IS2_atl06_attrs[gtx]['atlas_pce']
        IS2_atl06_corr_attrs[gtx]['atlas_beam_type'] = IS2_atl06_attrs[gtx]['atlas_beam_type']
        IS2_atl06_corr_attrs[gtx]['groundtrack_id'] = IS2_atl06_attrs[gtx]['groundtrack_id']
        IS2_atl06_corr_attrs[gtx]['atmosphere_profile'] = IS2_atl06_attrs[gtx]['atmosphere_profile']
        IS2_atl06_corr_attrs[gtx]['atlas_spot_number'] = IS2_atl06_attrs[gtx]['atlas_spot_number']
        IS2_atl06_corr_attrs[gtx]['sc_orientation'] = IS2_atl06_attrs[gtx]['sc_orientation']
        #-- group attributes for land_ice_segments
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['Description'] = ("The land_ice_segments group "
            "contains the primary set of derived products. This includes geolocation, height, and "
            "standard error and quality measures for each segment. This group is sparse, meaning "
            "that parameters are provided only for pairs of segments for which at least one beam "
            "has a valid surface-height measurement.")
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['data_rate'] = ("Data within this group are "
            "sparse.  Data values are provided only for those ICESat-2 20m segments where at "
            "least one beam has a valid land ice height measurement.")

        #-- geolocation, time and segment ID
        #-- delta time
        IS2_atl06_corr[gtx]['land_ice_segments']['delta_time'] = val['delta_time'].copy()
        IS2_atl06_fill[gtx]['land_ice_segments']['delta_time'] = None
        IS2_atl06_dims[gtx]['land_ice_segments']['delta_time'] = None
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['delta_time'] = {}
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['delta_time']['units'] = "seconds since 2018-01-01"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['delta_time']['long_name'] = "Elapsed GPS seconds"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['delta_time']['standard_name'] = "time"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['delta_time']['calendar'] = "standard"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['delta_time']['description'] = ("Number of GPS "
            "seconds since the ATLAS SDP epoch. The ATLAS Standard Data Products (SDP) epoch offset "
            "is defined within /ancillary_data/atlas_sdp_gps_epoch as the number of GPS seconds "
            "between the GPS epoch (1980-01-06T00:00:00.000000Z UTC) and the ATLAS SDP epoch. By "
            "adding the offset contained within atlas_sdp_gps_epoch to delta time parameters, the "
            "time in gps_seconds relative to the GPS epoch can be computed.")
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['delta_time']['coordinates'] = \
            "segment_id latitude longitude"
        #-- latitude
        IS2_atl06_corr[gtx]['land_ice_segments']['latitude'] = val['latitude'].copy()
        IS2_atl06_fill[gtx]['land_ice_segments']['latitude'] = None
        IS2_atl06_dims[gtx]['land_ice_segments']['latitude'] = ['delta_time']
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude'] = {}
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude']['units'] = "degrees_north"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude']['contentType'] = "physicalMeasurement"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude']['long_name'] = "Latitude"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude']['standard_name'] = "latitude"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude']['description'] = ("Latitude of "
            "segment center")
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude']['valid_min'] = -90.0
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude']['valid_max'] = 90.0
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['latitude']['coordinates'] = \
            "segment_id delta_time longitude"
        #-- longitude
        IS2_atl06_corr[gtx]['land_ice_segments']['longitude'] = val['longitude'].copy()
        IS2_atl06_fill[gtx]['land_ice_segments']['longitude'] = None
        IS2_atl06_dims[gtx]['land_ice_segments']['longitude'] = ['delta_time']
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude'] = {}
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude']['units'] = "degrees_east"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude']['contentType'] = "physicalMeasurement"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude']['long_name'] = "Longitude"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude']['standard_name'] = "longitude"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude']['description'] = ("Longitude of "
            "segment center")
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude']['valid_min'] = -180.0
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude']['valid_max'] = 180.0
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['longitude']['coordinates'] = \
            "segment_id delta_time latitude"
        #-- segment ID
        IS2_atl06_corr[gtx]['land_ice_segments']['segment_id'] = val['segment_id']
        IS2_atl06_fill[gtx]['land_ice_segments']['segment_id'] = None
        IS2_atl06_dims[gtx]['land_ice_segments']['segment_id'] = ['delta_time']
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['segment_id'] = {}
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['segment_id']['units'] = "1"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['segment_id']['contentType'] = "referenceInformation"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['segment_id']['long_name'] = "Along-track segment ID number"
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['segment_id']['description'] = ("A 7 digit number "
            "identifying the along-track geolocation segment number.  These are sequential, starting with "
            "1 for the first segment after an ascending equatorial crossing node. Equal to the segment_id for "
            "the second of the two 20m ATL03 segments included in the 40m ATL06 segment")
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['segment_id']['coordinates'] = \
            "delta_time latitude longitude"

        #-- geophysical variables
        IS2_atl06_corr[gtx]['land_ice_segments']['geophysical'] = {}
        IS2_atl06_fill[gtx]['land_ice_segments']['geophysical'] = {}
        IS2_atl06_dims[gtx]['land_ice_segments']['geophysical'] = {}
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical'] = {}
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical']['Description'] = ("The geophysical group "
            "contains parameters used to correct segment heights for geophysical effects, parameters "
            "related to solar background and parameters indicative of the presence or absence of clouds.")
        IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical']['data_rate'] = ("Data within this group "
            "are stored at the land_ice_segments segment rate.")

        #-- interpolated sea level products
        for key,val in interp.items():
            #-- map to original segments
            sea_level = np.ma.zeros((n_seg),fill_value=fv)
            sea_level.data[valid] = np.copy(val)
            #-- replace nan values with fill value
            sea_level.mask = np.copy(mask)
            sea_level.mask |= np.isnan(sea_level.data)
            sea_level.data[sea_level.mask] = sea_level.fill_value
            #-- add to output
            IS2_atl06_corr[gtx]['land_ice_segments']['geophysical'][key] = sea_level.copy()
            IS2_atl06_corr[gtx]['land_ice_segments']['geophysical'][key] = sea_level.fill_value
            IS2_atl06_dims[gtx]['land_ice_segments']['geophysical'][key] = ['delta_time']
            IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical'][key] = {}
            IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical'][key]['units'] = "meters"
            IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical'][key]['contentType'] = "referenceInformation"
            IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical'][key]['long_name'] = attrib[key]['long_name']
            IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical'][key]['description'] = attrib[key]['description']
            IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical'][key]['source'] = 'AVISO/Copernicus'
            IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical'][key]['reference'] = attrib[key]['reference']
            IS2_atl06_corr_attrs[gtx]['land_ice_segments']['geophysical'][key]['coordinates'] = \
                "../segment_id ../delta_time ../latitude ../longitude"

    #-- output HDF5 files with interpolated sea level data
    fargs = (PRD,'AVISO_SEA_LEVEL',YY,MM,DD,HH,MN,SS,TRK,CYCL,GRAN,RL,VERS,AUX)
    file_format = '{0}_{1}_{2}{3}{4}{5}{6}{7}_{8}{9}{10}_{11}_{12}{13}.h5'
    output_file = os.path.join(DIRECTORY,file_format.format(*fargs))
    #-- print file information
    logging.info('\t{0}'.format(output_file))
    HDF5_ATL06_corr_write(IS2_atl06_corr, IS2_atl06_corr_attrs,
        CLOBBER=True, INPUT=os.path.basename(FILE),
        FILL_VALUE=IS2_atl06_fill, DIMENSIONS=IS2_atl06_dims,
        FILENAME=output_file)
    #-- change the permissions mode
    os.chmod(output_file, MODE)
Example #6
0
def reduce_ICESat2_ATL06_raster(FILE,
                                MASK=None,
                                FORMAT=None,
                                VARIABLES=[],
                                OUTPUT=None,
                                PROJECTION=None,
                                VERBOSE=False,
                                MODE=0o775):

    #-- create logger
    loglevel = logging.INFO if VERBOSE else logging.CRITICAL
    logging.basicConfig(level=loglevel)

    #-- read data from input file
    logging.info('{0} -->'.format(os.path.basename(FILE)))
    IS2_atl06_mds, IS2_atl06_attrs, IS2_atl06_beams = read_HDF5_ATL06(
        FILE, ATTRIBUTES=True)
    DIRECTORY = os.path.dirname(FILE)
    #-- extract parameters from ICESat-2 ATLAS HDF5 file name
    rx = re.compile(
        r'(processed_)?(ATL\d{2})_(\d{4})(\d{2})(\d{2})(\d{2})'
        r'(\d{2})(\d{2})_(\d{4})(\d{2})(\d{2})_(\d{3})_(\d{2})(.*?).h5$')
    SUB, PRD, YY, MM, DD, HH, MN, SS, TRK, CYCL, GRAN, RL, VERS, AUX = rx.findall(
        FILE).pop()

    #-- read raster image for spatial coordinates and data
    dinput = icesat2_toolkit.spatial.from_file(MASK,
                                               FORMAT,
                                               xname=VARIABLES[0],
                                               yname=VARIABLES[1],
                                               varname=VARIABLES[2])
    #-- raster extents
    xmin, xmax, ymin, ymax = np.copy(dinput['attributes']['extent'])
    #-- check that x and y are strictly increasing
    if (np.sign(dinput['attributes']['spacing'][0]) == -1):
        dinput['x'] = dinput['x'][::-1]
        dinput['data'] = dinput['data'][:, ::-1]
    if (np.sign(dinput['attributes']['spacing'][1]) == -1):
        dinput['y'] = dinput['y'][::-1]
        dinput['data'] = dinput['data'][::-1, :]
    #-- find valid points within mask
    indy, indx = np.nonzero(dinput['data'])
    #-- check that input points are within convex hull of valid model points
    gridx, gridy = np.meshgrid(dinput['x'], dinput['y'])
    v, triangle = find_valid_triangulation(gridx[indy, indx], gridy[indy,
                                                                    indx])
    #-- create an interpolator for input raster data
    logging.info('Building Spline Interpolator')
    SPL = scipy.interpolate.RectBivariateSpline(dinput['x'],
                                                dinput['y'],
                                                dinput['data'].T,
                                                kx=1,
                                                ky=1)

    #-- convert projection from input coordinates (EPSG) to data coordinates
    crs1 = pyproj.CRS.from_string("epsg:{0:d}".format(4326))
    crs2 = get_projection(dinput['attributes'], PROJECTION)
    transformer = pyproj.Transformer.from_crs(crs1, crs2, always_xy=True)
    logging.info(crs2.to_proj4())

    #-- copy variables for outputting to HDF5 file
    IS2_atl06_mask = {}
    IS2_atl06_fill = {}
    IS2_atl06_dims = {}
    IS2_atl06_mask_attrs = {}
    #-- number of GPS seconds between the GPS epoch (1980-01-06T00:00:00Z UTC)
    #-- and ATLAS Standard Data Product (SDP) epoch (2018-01-01T00:00:00Z UTC)
    #-- Add this value to delta time parameters to compute full gps_seconds
    IS2_atl06_mask['ancillary_data'] = {}
    IS2_atl06_mask_attrs['ancillary_data'] = {}
    for key in ['atlas_sdp_gps_epoch']:
        #-- get each HDF5 variable
        IS2_atl06_mask['ancillary_data'][key] = IS2_atl06_mds[
            'ancillary_data'][key]
        #-- Getting attributes of group and included variables
        IS2_atl06_mask_attrs['ancillary_data'][key] = {}
        for att_name, att_val in IS2_atl06_attrs['ancillary_data'][key].items(
        ):
            IS2_atl06_mask_attrs['ancillary_data'][key][att_name] = att_val

    #-- for each input beam within the file
    for gtx in sorted(IS2_atl06_beams):
        #-- output data dictionaries for beam
        IS2_atl06_mask[gtx] = dict(land_ice_segments={})
        IS2_atl06_fill[gtx] = dict(land_ice_segments={})
        IS2_atl06_dims[gtx] = dict(land_ice_segments={})
        IS2_atl06_mask_attrs[gtx] = dict(land_ice_segments={})

        #-- number of segments
        val = IS2_atl06_mds[gtx]['land_ice_segments']
        n_seg = len(val['segment_id'])

        #-- convert latitude/longitude to raster image projection
        X, Y = transformer.transform(val['longitude'], val['latitude'])

        #-- check where points are within complex hull of triangulation
        #-- or within the bounds of the input raster image
        if v:
            interp_points = np.concatenate((X[:, None], Y[:, None]), axis=1)
            valid = (triangle.find_simplex(interp_points) >= 0)
        else:
            valid = (X >= xmin) & (X <= xmax) & (Y >= ymin) & (Y <= ymax)

        #-- interpolate raster mask to points
        interp_mask = np.zeros((n_seg), dtype=bool)
        #-- skip beam interpolation if no data within bounds of raster image
        if np.any(valid):
            interp_mask[valid] = SPL.ev(X[valid], Y[valid])

        #-- group attributes for beam
        IS2_atl06_mask_attrs[gtx]['Description'] = IS2_atl06_attrs[gtx][
            'Description']
        IS2_atl06_mask_attrs[gtx]['atlas_pce'] = IS2_atl06_attrs[gtx][
            'atlas_pce']
        IS2_atl06_mask_attrs[gtx]['atlas_beam_type'] = IS2_atl06_attrs[gtx][
            'atlas_beam_type']
        IS2_atl06_mask_attrs[gtx]['groundtrack_id'] = IS2_atl06_attrs[gtx][
            'groundtrack_id']
        IS2_atl06_mask_attrs[gtx]['atmosphere_profile'] = IS2_atl06_attrs[gtx][
            'atmosphere_profile']
        IS2_atl06_mask_attrs[gtx]['atlas_spot_number'] = IS2_atl06_attrs[gtx][
            'atlas_spot_number']
        IS2_atl06_mask_attrs[gtx]['sc_orientation'] = IS2_atl06_attrs[gtx][
            'sc_orientation']
        #-- group attributes for land_ice_segments
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['Description'] = (
            "The land_ice_segments group "
            "contains the primary set of derived products. This includes geolocation, height, and "
            "standard error and quality measures for each segment. This group is sparse, meaning "
            "that parameters are provided only for pairs of segments for which at least one beam "
            "has a valid surface-height measurement.")
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['data_rate'] = (
            "Data within this group are "
            "sparse.  Data values are provided only for those ICESat-2 20m segments where at "
            "least one beam has a valid land ice height measurement.")

        #-- geolocation, time and segment ID
        #-- delta time
        IS2_atl06_mask[gtx]['land_ice_segments']['delta_time'] = val[
            'delta_time'].copy()
        IS2_atl06_fill[gtx]['land_ice_segments']['delta_time'] = None
        IS2_atl06_dims[gtx]['land_ice_segments']['delta_time'] = None
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['delta_time'] = {}
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['delta_time'][
            'units'] = "seconds since 2018-01-01"
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['delta_time'][
            'long_name'] = "Elapsed GPS seconds"
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['delta_time'][
            'standard_name'] = "time"
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['delta_time'][
            'calendar'] = "standard"
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['delta_time'][
            'description'] = (
                "Number of GPS "
                "seconds since the ATLAS SDP epoch. The ATLAS Standard Data Products (SDP) epoch offset "
                "is defined within /ancillary_data/atlas_sdp_gps_epoch as the number of GPS seconds "
                "between the GPS epoch (1980-01-06T00:00:00.000000Z UTC) and the ATLAS SDP epoch. By "
                "adding the offset contained within atlas_sdp_gps_epoch to delta time parameters, the "
                "time in gps_seconds relative to the GPS epoch can be computed."
            )
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['delta_time']['coordinates'] = \
            "segment_id latitude longitude"
        #-- latitude
        IS2_atl06_mask[gtx]['land_ice_segments']['latitude'] = val[
            'latitude'].copy()
        IS2_atl06_fill[gtx]['land_ice_segments']['latitude'] = None
        IS2_atl06_dims[gtx]['land_ice_segments']['latitude'] = ['delta_time']
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['latitude'] = {}
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['latitude'][
            'units'] = "degrees_north"
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['latitude'][
            'contentType'] = "physicalMeasurement"
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['latitude'][
            'long_name'] = "Latitude"
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['latitude'][
            'standard_name'] = "latitude"
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['latitude'][
            'description'] = ("Latitude of "
                              "segment center")
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['latitude'][
            'valid_min'] = -90.0
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['latitude'][
            'valid_max'] = 90.0
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['latitude']['coordinates'] = \
            "segment_id delta_time longitude"
        #-- longitude
        IS2_atl06_mask[gtx]['land_ice_segments']['longitude'] = val[
            'longitude'].copy()
        IS2_atl06_fill[gtx]['land_ice_segments']['longitude'] = None
        IS2_atl06_dims[gtx]['land_ice_segments']['longitude'] = ['delta_time']
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['longitude'] = {}
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['longitude'][
            'units'] = "degrees_east"
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['longitude'][
            'contentType'] = "physicalMeasurement"
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['longitude'][
            'long_name'] = "Longitude"
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['longitude'][
            'standard_name'] = "longitude"
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['longitude'][
            'description'] = ("Longitude of "
                              "segment center")
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['longitude'][
            'valid_min'] = -180.0
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['longitude'][
            'valid_max'] = 180.0
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['longitude']['coordinates'] = \
            "segment_id delta_time latitude"
        #-- segment ID
        IS2_atl06_mask[gtx]['land_ice_segments']['segment_id'] = val[
            'segment_id']
        IS2_atl06_fill[gtx]['land_ice_segments']['segment_id'] = None
        IS2_atl06_dims[gtx]['land_ice_segments']['segment_id'] = ['delta_time']
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['segment_id'] = {}
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['segment_id'][
            'units'] = "1"
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['segment_id'][
            'contentType'] = "referenceInformation"
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['segment_id'][
            'long_name'] = "Along-track segment ID number"
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['segment_id'][
            'description'] = (
                "A 7 digit number "
                "identifying the along-track geolocation segment number.  These are sequential, starting with "
                "1 for the first segment after an ascending equatorial crossing node. Equal to the segment_id for "
                "the second of the two 20m ATL03 segments included in the 40m ATL06 segment"
            )
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['segment_id']['coordinates'] = \
            "delta_time latitude longitude"

        #-- subsetting variables
        IS2_atl06_mask[gtx]['land_ice_segments']['subsetting'] = {}
        IS2_atl06_fill[gtx]['land_ice_segments']['subsetting'] = {}
        IS2_atl06_dims[gtx]['land_ice_segments']['subsetting'] = {}
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['subsetting'] = {}
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['subsetting'][
            'Description'] = (
                "The subsetting group "
                "contains parameters used to reduce land ice segments to specific regions of interest."
            )
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['subsetting'][
            'data_rate'] = (
                "Data within this group "
                "are stored at the land_ice_segments segment rate.")

        #-- output mask to HDF5
        IS2_atl06_mask[gtx]['land_ice_segments']['subsetting'][
            'mask'] = interp_mask.copy()
        IS2_atl06_fill[gtx]['land_ice_segments']['subsetting']['mask'] = None
        IS2_atl06_dims[gtx]['land_ice_segments']['subsetting']['mask'] = [
            'delta_time'
        ]
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['subsetting'][
            'mask'] = {}
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['subsetting']['mask']['contentType'] = \
            "referenceInformation"
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['subsetting']['mask'][
            'long_name'] = 'Mask'
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['subsetting']['mask']['description'] = \
            'Mask calculated using raster image'
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['subsetting']['mask']['source'] = \
            os.path.basename(MASK)
        IS2_atl06_mask_attrs[gtx]['land_ice_segments']['subsetting']['mask']['coordinates'] = \
            "../segment_id ../delta_time ../latitude ../longitude"

    #-- use default output file name and path
    if OUTPUT:
        output_file = os.path.expanduser(OUTPUT)
    else:
        fargs = (PRD, 'MASK', YY, MM, DD, HH, MN, SS, TRK, CYCL, GRAN, RL,
                 VERS, AUX)
        file_format = '{0}_{1}_{2}{3}{4}{5}{6}{7}_{8}{9}{10}_{11}_{12}{13}.h5'
        output_file = os.path.join(DIRECTORY, file_format.format(*fargs))
    #-- print file information
    logging.info('\t{0}'.format(output_file))
    #-- write to output HDF5 file
    HDF5_ATL06_mask_write(IS2_atl06_mask,
                          IS2_atl06_mask_attrs,
                          CLOBBER=True,
                          INPUT=os.path.basename(FILE),
                          FILL_VALUE=IS2_atl06_fill,
                          DIMENSIONS=IS2_atl06_dims,
                          FILENAME=output_file)
    #-- change the permissions mode
    os.chmod(output_file, MODE)