Exemple #1
0
def test_gfz_icgem_costg_download_and_read():
    #-- attempt to download from ftp server
    try:
        HOST = [
            'icgem.gfz-potsdam.de', '02_COST-G', 'Grace-FO',
            'GSM-2_2018152-2018181_GRFO_COSTG_BF01_0100.gfc'
        ]
        FILE = gravity_toolkit.utilities.from_ftp(HOST, verbose=True)
    except:
        pass
    #-- attempt to download from http server
    try:
        HOST = [
            'http://icgem.gfz-potsdam.de', 'getseries', '02_COST-G',
            'Grace-FO', 'GSM-2_2018152-2018181_GRFO_COSTG_BF01_0100.gfc'
        ]
        FILE = gravity_toolkit.utilities.from_http(HOST, verbose=True)
    except:
        return
    #-- read as virtual file object
    Ylms = read_GRACE_harmonics(FILE, 60)
    keys = ['time', 'start', 'end', 'clm', 'slm', 'eclm', 'eslm', 'header']
    test = dict(start=2458270.5, end=2458299.5)
    assert all((key in Ylms.keys()) for key in keys)
    assert all((Ylms[key] == val) for key, val in test.items())
    assert (Ylms['clm'][2, 0] == -0.484165436067e-03)
Exemple #2
0
 def from_SHM(self, filename, **kwargs):
     """
     Read a harmonics object from a spherical harmonic model file
     Inputs: full path of input SHM file
     Options:
         keyword arguments for SHM input
     """
     #-- set filename
     self.case_insensitive_filename(filename)
     #-- set default verbosity
     kwargs.setdefault('verbose', False)
     #-- read data from SHM file
     Ylms = read_GRACE_harmonics(self.filename, self.lmax, **kwargs)
     #-- Output file information
     logging.info(self.filename)
     logging.info(list(Ylms.keys()))
     #-- copy variables for gravity model
     self.clm = Ylms['clm'].copy()
     self.slm = Ylms['slm'].copy()
     self.time = Ylms['time'].copy()
     self.month = np.int64(calendar_to_grace(self.time))
     #-- copy header information for gravity model
     self.header = Ylms['header']
     #-- assign shape and ndim attributes
     self.update_dimensions()
     return self
def test_gfz_ftp_download_and_read(username, webdav):
    HOST = [
        'isdcftp.gfz-potsdam.de', 'grace', 'Level-2', 'CSR', 'RL06',
        'GSM-2_2002095-2002120_GRAC_UTCSR_BA01_0600.gz'
    ]
    #-- download and read as virtual file object
    FILE = gravity_toolkit.utilities.from_ftp(HOST, verbose=True)
    Ylms = read_GRACE_harmonics(FILE, 60)
    keys = ['time', 'start', 'end', 'clm', 'slm', 'eclm', 'eslm', 'header']
    test = dict(start=2452369.5, end=2452394.5)
    assert all((key in Ylms.keys()) for key in keys)
    assert all((Ylms[key] == val) for key, val in test.items())
    assert (Ylms['clm'][2, 0] == -0.484169355584e-03)
def test_podaac_download_and_read(username, webdav):
    HOST = [
        'https://podaac-tools.jpl.nasa.gov', 'drive', 'files', 'allData',
        'grace', 'L2', 'CSR', 'RL06',
        'GSM-2_2002095-2002120_GRAC_UTCSR_BA01_0600.gz'
    ]
    #-- download and read as virtual file object
    FILE = gravity_toolkit.utilities.from_drive(HOST,
                                                username=username,
                                                password=webdav,
                                                verbose=True)
    Ylms = read_GRACE_harmonics(FILE, 60)
    keys = ['time', 'start', 'end', 'clm', 'slm', 'eclm', 'eslm', 'header']
    test = dict(start=2452369.5, end=2452394.5)
    assert all((key in Ylms.keys()) for key in keys)
    assert all((Ylms[key] == val) for key, val in test.items())
    assert (Ylms['clm'][2, 0] == -0.484169355584e-03)
Exemple #5
0
def grace_input_months(base_dir,
                       PROC,
                       DREL,
                       DSET,
                       LMAX,
                       start_mon,
                       end_mon,
                       missing,
                       SLR_C20,
                       DEG1,
                       MMAX=None,
                       SLR_C30='',
                       MODEL_DEG1=False,
                       DEG1_GIA='',
                       ATM=False,
                       POLE_TIDE=False):
    """
    Reads GRACE/GRACE-FO files for a spherical harmonic degree and order
        and a date range
    Replaces Degree 1 with with input values (if specified)
    Replaces C20 with SLR values (if specified)
    Replaces C30 with SLR values for months 179+ (if specified)
    Corrects for ECMWF atmospheric "jumps" using the GAE, GAF and GAG files
    Corrects for Pole Tide drift following Wahr et al. (2015)

    Arguments
    ---------
    base_dir: Working data directory for GRACE/GRACE-FO data
    PROC: (CSR/CNES/JPL/GFZ) data processing center
    DREL: (RL01,RL02,RL03,RL04,RL05,RL06) data release
    DSET: (GAA/GAB/GAC/GAD/GSM) data product
    LMAX: Upper bound of Spherical Harmonic Degrees
    start_mon: starting month to consider in analysis
    end_mon: ending month to consider in analysis
    missing: missing months to not consider in analysis
    SLR_C20: Replaces C20 with SLR values
        N: use original values
        CSR: use values from CSR (TN-07,TN-09,TN-11)
        GSFC: use values from GSFC (TN-14)
    DEG1: Use Degree 1 coefficients
        None: No degree 1
        Tellus: GRACE/GRACE-FO TN-13 coefficients from PO.DAAC
        SLR: satellite laser ranging coefficients from CSR
        SLF: Sutterley and Velicogna coefficients, Remote Sensing (2019)

    Keyword arguments
    -----------------
    MMAX: Upper bound of Spherical Harmonic Orders
    SLR_C30: replaces C30 with SLR values
        None: use original values
        CSR: use values from CSR (5x5 with 6,1)
        GSFC: use values from GSFC (TN-14)
    POLE_TIDE: correct GSM data with pole tides following Wahr et al (2015)
    ATM: correct data with ECMWF "jump" corrections GAE, GAF and GAG
    MODEL_DEG1: least-squares model missing degree 1 coefficients (True/False)
    DEG1_GIA: GIA-correction used when calculating degree 1 coefficients

    Returns
    -------
    clm: GRACE/GRACE-FO cosine spherical harmonics
    slm: GRACE/GRACE-FO sine spherical harmonics
    time: time of each GRACE/GRACE-FO measurement (mid-month)
    month: GRACE/GRACE-FO months of input datasets
    l: spherical harmonic degree to LMAX
    m: spherical harmonic order to MMAX
    title: string denoting low degree zonals replacement, geocenter usage and corrections
    directory: directory of exact GRACE/GRACE-FO product
    """

    #-- Directory of exact GRACE product
    grace_dir = os.path.join(base_dir, PROC, DREL, DSET)

    #-- upper bound of spherical harmonic orders (default = LMAX)
    MMAX = np.copy(LMAX) if (MMAX is None) else MMAX

    #-- Replacing C2,0 with SLR C2,0
    #-- Running function read_SLR_C20.py
    #-- reading SLR C2,0 file for given release if specified
    if (SLR_C20 == 'CSR'):
        if (DREL == 'RL04'):
            SLR_file = os.path.join(base_dir, 'TN-05_C20_SLR.txt')
        elif (DREL == 'RL05'):
            SLR_file = os.path.join(base_dir, 'TN-07_C20_SLR.txt')
        elif (DREL == 'RL06'):
            SLR_file = os.path.join(base_dir, 'TN-11_C20_SLR.txt')
        C20_input = read_SLR_C20(SLR_file)
        C20_str = '_wCSR_C20'
    elif (SLR_C20 == 'GSFC'):
        SLR_file = os.path.join(base_dir, 'TN-14_C30_C20_GSFC_SLR.txt')
        C20_input = read_SLR_C20(SLR_file)
        C20_str = '_wGSFC_C20'
    else:
        C20_str = ''

    #-- Replacing C3,0 with SLR C3,0
    #-- Running function read_SLR_C30.py
    if (SLR_C30 == 'CSR'):
        SLR_file = os.path.join(base_dir,
                                'CSR_Monthly_5x5_Gravity_Harmonics.txt')
        C30_input = read_SLR_C30(SLR_file)
        C30_str = '_wCSR_C30'
    elif (SLR_C30 == 'LARES'):
        SLR_file = os.path.join(base_dir, 'C30_LARES_filtered.txt')
        C30_input = read_SLR_C30(SLR_file)
        C30_str = '_wLARES_C30'
    elif (SLR_C30 == 'GSFC'):
        SLR_file = os.path.join(base_dir, 'TN-14_C30_C20_GSFC_SLR.txt')
        C30_input = read_SLR_C30(SLR_file)
        C30_str = '_wGSFC_C30'
    else:
        C30_str = ''

    #-- Correcting for Degree 1 (geocenter variations)
    #-- reading degree 1 file for given release if specified
    if (DEG1 == 'Tellus'):
        #-- Tellus (PO.DAAC) degree 1
        if DREL in ('RL04', 'RL05'):
            DEG1_file = os.path.join(base_dir, 'geocenter',
                                     'deg1_coef_{0}.txt'.format(DREL))
            JPL = False
        else:
            DEG1_file = os.path.join(
                base_dir, 'geocenter',
                'TN-13_GEOC_{0}_{1}.txt'.format(PROC, DREL))
            JPL = True
        #-- Running function read_tellus_geocenter.py
        DEG1_input = read_tellus_geocenter(DEG1_file, JPL=JPL)
        DEG1_str = '_w{0}_DEG1'.format(DEG1)
    elif (DEG1 == 'SLR'):
        #-- CSR Satellite Laser Ranging (SLR) degree 1
        # #-- SLR-derived degree-1 mass variations
        # #-- ftp://ftp.csr.utexas.edu/pub/slr/geocenter/
        # DEG1_file=os.path.join(base_dir,'geocenter','GCN_{0}.txt'.format(DREL))
        # DEG1_input=aod_corrected_SLR_geocenter(DEG1_file,DREL,HEADER=16,
        #     COLUMNS=['time','X','Y','Z','X_sigma','Y_sigma','Z_sigma'])

        # #-- new CF-CM file of degree-1 mass variations
        # #-- https://cddis.nasa.gov/lw20/docs/2016/papers/14-Ries_paper.pdf
        # #-- http://download.csr.utexas.edu/pub/slr/geocenter/GCN_L1_L2_30d_CF-CM.txt
        # DEG1_file = os.path.join(base_dir,'geocenter','GCN_L1_L2_30d_CF-CM.txt')
        # DEG1_input = aod_corrected_SLR_geocenter(DEG1_file,DREL,HEADER=111,
        #     COLUMNS=['time','X','Y','Z','X_sigma','Y_sigma','Z_sigma'])

        #-- new file of degree-1 mass variations from Minkang Cheng
        #-- http://download.csr.utexas.edu/outgoing/cheng/gct2est.220_5s
        DEG1_file = os.path.join(base_dir, 'geocenter', 'gct2est.220_5s')
        DEG1_input = aod_corrected_SLR_geocenter(DEG1_file,
                                                 HEADER=15,
                                                 RADIUS=6.378136e9,
                                                 COLUMNS=[
                                                     'MJD', 'time', 'X', 'Y',
                                                     'Z', 'XM', 'YM', 'ZM',
                                                     'X_sigma', 'Y_sigma',
                                                     'Z_sigma', 'XM_sigma',
                                                     'YM_sigma', 'ZM_sigma'
                                                 ])
        DEG1_str = '_w{0}_DEG1'.format(DEG1)
    elif (DEG1 == 'SLF'):
        #-- read iterated degree one files from Sutterley and Velicogna (2019)
        #-- that includes self-attraction and loading effects
        #-- include flag for datasets with different GIA corrections
        MODEL = dict(RL04='OMCT', RL05='OMCT', RL06='MPIOM')
        args = (PROC, DREL, MODEL[DREL], 'SLF_iter', DEG1_GIA)
        DEG1_file = os.path.join(base_dir, 'geocenter',
                                 '{0}_{1}_{2}_{3}{4}.txt'.format(*args))
        DEG1_input = read_GRACE_geocenter(DEG1_file)
        DEG1_str = '_w{0}_DEG1'.format(DEG1)
    else:  #-- not using a degree 1 file (non-GSM or only using degree 2+)
        DEG1_str = ''

    #-- atmospheric flag if correcting ECMWF "jumps" (using GAE/GAF/GAG files)
    atm_str = '_wATM' if ATM else ''
    #-- pole tide flag if correcting for pole tide drift (Wahr et al. 2015)
    pt_str = '_wPT' if POLE_TIDE else ''
    #-- full output string (C20, C30, geocenter and atmospheric flags)
    out_str = C20_str + C30_str + DEG1_str + atm_str + pt_str

    #-- Range of months from start_mon to end_mon (end_mon+1 to include end_mon)
    #-- Removing the missing months and months not to consider
    months = sorted(set(np.arange(start_mon, end_mon + 1)) - set(missing))
    #-- number of months to consider in analysis
    n_cons = len(months)

    #-- Initializing input data matrices
    grace_clm = np.zeros((LMAX + 1, MMAX + 1, n_cons))
    grace_slm = np.zeros((LMAX + 1, MMAX + 1, n_cons))
    tdec = np.zeros((n_cons))
    mon = np.zeros((n_cons), dtype=np.int)
    #-- output dimensions
    lout = np.arange(LMAX + 1)
    mout = np.arange(MMAX + 1)

    #-- associate GRACE/GRACE-FO files with each GRACE/GRACE-FO month
    grace_files = grace_date(base_dir,
                             PROC=PROC,
                             DREL=DREL,
                             DSET=DSET,
                             OUTPUT=False)

    #-- importing data from GRACE/GRACE-FO files
    for i, grace_month in enumerate(months):
        #-- Effects of Pole tide drift will be compensated if soecified
        infile = grace_files[grace_month]
        Ylms = read_GRACE_harmonics(infile,
                                    LMAX,
                                    MMAX=MMAX,
                                    POLE_TIDE=POLE_TIDE)
        grace_clm[:, :, i] = Ylms['clm'][0:LMAX + 1, 0:MMAX + 1]
        grace_slm[:, :, i] = Ylms['slm'][0:LMAX + 1, 0:MMAX + 1]
        tdec[i] = Ylms['time']
        mon[i] = np.int(grace_month)

    #-- Replace C20 with SLR coefficients
    if SLR_C20 in ('CSR', 'GSFC'):
        #-- verify that there are replacement C20 months for specified range
        months_test = sorted(set(months) - set(C20_input['month']))
        if months_test:
            gm = ','.join('{0:03d}'.format(gm) for gm in months_test)
            raise IOError('No Matching C20 Months ({0})'.format(gm))
        #-- replace C20 with SLR coefficients
        for i, grace_month in enumerate(months):
            count = np.count_nonzero(C20_input['month'] == grace_month)
            if (count != 0):
                k, = np.nonzero(C20_input['month'] == grace_month)
                grace_clm[2, 0, i] = C20_input['data'][k]

    #-- Replace C30 with SLR coefficients for single-accelerometer months
    if SLR_C30 in ('CSR', 'GSFC', 'LARES'):
        #-- verify that there are replacement C30 months for specified range
        months_test = sorted(set(mon[mon > 176]) - set(C30_input['month']))
        if months_test:
            gm = ','.join('{0:03d}'.format(gm) for gm in months_test)
            raise IOError('No Matching C30 Months ({0})'.format(gm))
        #-- replace C30 with SLR coefficients
        for i, grace_month in enumerate(months):
            count = np.count_nonzero(C30_input['month'] == grace_month)
            if (count != 0) and (grace_month > 176):
                k, = np.nonzero(C30_input['month'] == grace_month)
                grace_clm[3, 0, i] = C30_input['data'][k]

    #-- Use Degree 1 coefficients
    #-- Tellus: Tellus Degree 1 (PO.DAAC following Sun et al., 2016)
    #-- SLR: CSR Satellite Laser Ranging (SLR) Degree 1 - GRACE AOD
    #-- SLF: OMCT/MPIOM coefficients with Sea Level Fingerprint land-water mass
    if DEG1 in ('Tellus', 'SLR', 'SLF'):
        #-- check if modeling degree 1 or if all months are available
        if MODEL_DEG1:
            #-- least-squares modeling the degree 1 coefficients
            #-- fitting annual, semi-annual, linear and quadratic terms
            C10_model = regress_model(DEG1_input['time'],
                                      DEG1_input['C10'],
                                      tdec,
                                      ORDER=2,
                                      CYCLES=[0.5, 1.0],
                                      RELATIVE=2003.3)
            C11_model = regress_model(DEG1_input['time'],
                                      DEG1_input['C11'],
                                      tdec,
                                      ORDER=2,
                                      CYCLES=[0.5, 1.0],
                                      RELATIVE=2003.3)
            S11_model = regress_model(DEG1_input['time'],
                                      DEG1_input['S11'],
                                      tdec,
                                      ORDER=2,
                                      CYCLES=[0.5, 1.0],
                                      RELATIVE=2003.3)
        else:
            #-- check that all months are available for a given geocenter
            months_test = sorted(set(months) - set(DEG1_input['month']))
            if months_test:
                gm = ','.join('{0:03d}'.format(gm) for gm in months_test)
                raise IOError('No Matching Geocenter Months ({0})'.format(gm))
        #-- for each considered date
        for i, grace_month in enumerate(months):
            k, = np.nonzero(DEG1_input['month'] == grace_month)
            count = np.count_nonzero(DEG1_input['month'] == grace_month)
            #-- Degree 1 is missing for particular month
            if (count == 0) and MODEL_DEG1:
                #-- using least-squares modeled coefficients from
                #-- lsq_model_degree_one.py
                grace_clm[1, 0, i] = C10_model[i]
                grace_clm[1, 1, i] = C11_model[i]
                grace_slm[1, 1, i] = S11_model[i]
            else:  #-- using coefficients from data file
                grace_clm[1, 0, i] = DEG1_input['C10'][k]
                grace_clm[1, 1, i] = DEG1_input['C11'][k]
                grace_slm[1, 1, i] = DEG1_input['S11'][k]

    #-- read and add/remove the GAE and GAF atmospheric correction coefficients
    if ATM:
        #-- read ECMWF correction files from Fagiolini et al. (2015)
        atm_corr = read_ecmwf_corrections(base_dir, LMAX, months, MMAX=MMAX)
        #-- Removing GAE/GAF/GAG from RL05 GSM Products
        if (DSET == 'GSM'):
            for m in range(0, MMAX + 1):  #-- MMAX+1 to include l
                for l in range(m, LMAX + 1):  #-- LMAX+1 to include LMAX
                    grace_clm[l, m, :] -= atm_corr['clm'][l, m, :]
                    grace_slm[l, m, :] -= atm_corr['slm'][l, m, :]
        #-- Adding GAE/GAF/GAG to RL05 Atmospheric Products (GAA,GAC)
        elif DSET in ('GAC', 'GAA'):
            for m in range(0, MMAX + 1):  #-- MMAX+1 to include l
                for l in range(m, LMAX + 1):  #-- LMAX+1 to include LMAX
                    grace_clm[l, m, :] += atm_corr['clm'][l, m, :]
                    grace_slm[l, m, :] += atm_corr['slm'][l, m, :]

    return {
        'clm': grace_clm,
        'slm': grace_slm,
        'time': tdec,
        'month': mon,
        'l': lout,
        'm': mout,
        'title': out_str,
        'directory': grace_dir
    }
Exemple #6
0
def grace_input_months(base_dir, PROC, DREL, DSET, LMAX, start_mon, end_mon,
                       missing, SLR_C20, DEG1, **kwargs):
    """
    Reads GRACE/GRACE-FO files for a spherical harmonic degree and order
        and a date range
    Can include geocenter values for degree 1 coefficients
    Can replace C20 with SLR values for all months
    Can replace C21,S21,C22,S22,C30,C50 with SLR values for months 179+
    Can correct for ECMWF atmospheric "jumps" using GAE/GAF/GAG files
    Can correct for Pole Tide drift following Wahr et al. (2015)

    Arguments
    ---------
    base_dir: Working data directory for GRACE/GRACE-FO data
    PROC: (CSR/CNES/JPL/GFZ) data processing center
    DREL: (RL01/RL02/RL03/RL04/RL05/RL06) data release
    DSET: (GAA/GAB/GAC/GAD/GSM) data product
    LMAX: Upper bound of Spherical Harmonic Degrees
    start_mon: starting month to consider in analysis
    end_mon: ending month to consider in analysis
    missing: missing months to not consider in analysis
    SLR_C20: Replaces C20 with SLR values
        N: use original values
        CSR: use values from CSR (TN-07,TN-09,TN-11)
        GFZ: use values from GFZ
        GSFC: use values from GSFC (TN-14)
    DEG1: Use Degree 1 coefficients
        None: No degree 1
        Tellus: GRACE/GRACE-FO TN-13 coefficients from PO.DAAC
        SLR: satellite laser ranging coefficients from CSR
        SLF: Sutterley and Velicogna coefficients, Remote Sensing (2019)
        Swenson: GRACE-derived coefficients from Sean Swenson
        GFZ: GRACE/GRACE-FO coefficients from GFZ GravIS

    Keyword arguments
    -----------------
    MMAX: Upper bound of Spherical Harmonic Orders
    SLR_21: replaces C21 and S21 with SLR values
        None: use original values
        CSR: use values from CSR
        GFZ: use values from GFZ GravIS
        GSFC: use values from GSFC
    SLR_22: replaces C22 and S22 with SLR values
        None: use original values
        CSR: use values from CSR
    SLR_C30: replaces C30 with SLR values
        None: use original values
        CSR: use values from CSR (5x5 with 6,1)
        GFZ: use values from GFZ GravIS
        GSFC: use values from GSFC (TN-14)
    SLR_C50: replaces C50 with SLR values
        None: use original values
        CSR: use values from CSR (5x5 with 6,1)
        GSFC: use values from GSFC
    POLE_TIDE: correct GSM data with pole tides following Wahr et al (2015)
    ATM: correct data with ECMWF "jump" corrections GAE, GAF and GAG
    DEG1_FILE: full path to degree 1 coefficients file
    MODEL_DEG1: least-squares model missing degree 1 coefficients

    Returns
    -------
    clm: GRACE/GRACE-FO cosine spherical harmonics
    slm: GRACE/GRACE-FO sine spherical harmonics
    eclm: GRACE/GRACE-FO uncalibrated cosine spherical harmonic errors
    eslm: GRACE/GRACE-FO uncalibrated sine spherical harmonic errors
    time: time of each GRACE/GRACE-FO measurement (mid-month)
    month: GRACE/GRACE-FO months of input datasets
    l: spherical harmonic degree to LMAX
    m: spherical harmonic order to MMAX
    title: string denoting low degree zonals replacement, geocenter usage and corrections
    directory: directory of exact GRACE/GRACE-FO product
    """
    #-- set default keyword arguments
    kwargs.setdefault('MMAX', LMAX)
    kwargs.setdefault('SLR_21', '')
    kwargs.setdefault('SLR_22', '')
    kwargs.setdefault('SLR_C30', '')
    kwargs.setdefault('SLR_C50', '')
    kwargs.setdefault('DEG1_FILE', None)
    kwargs.setdefault('MODEL_DEG1', False)
    kwargs.setdefault('ATM', False)
    kwargs.setdefault('POLE_TIDE', False)

    #-- Directory of exact GRACE product
    grace_dir = os.path.join(base_dir, PROC, DREL, DSET)
    #-- test if GRACE product directory exists
    if not os.access(grace_dir, os.F_OK):
        raise FileNotFoundError(grace_dir)

    #-- upper bound of spherical harmonic orders (default = LMAX)
    MMAX = kwargs.get('MMAX') or np.copy(LMAX)

    #-- Range of months from start_mon to end_mon (end_mon+1 to include end_mon)
    #-- Removing the missing months and months not to consider
    months = sorted(set(np.arange(start_mon, end_mon + 1)) - set(missing))
    #-- number of months to consider in analysis
    n_cons = len(months)

    #-- Initializing input data matrices
    grace_Ylms = {}
    grace_Ylms['clm'] = np.zeros((LMAX + 1, MMAX + 1, n_cons))
    grace_Ylms['slm'] = np.zeros((LMAX + 1, MMAX + 1, n_cons))
    grace_Ylms['eclm'] = np.zeros((LMAX + 1, MMAX + 1, n_cons))
    grace_Ylms['eslm'] = np.zeros((LMAX + 1, MMAX + 1, n_cons))
    grace_Ylms['time'] = np.zeros((n_cons))
    grace_Ylms['month'] = np.zeros((n_cons), dtype=np.int64)
    #-- output dimensions
    grace_Ylms['l'] = np.arange(LMAX + 1)
    grace_Ylms['m'] = np.arange(MMAX + 1)
    #-- input directory for product
    grace_Ylms['directory'] = copy.copy(grace_dir)

    #-- associate GRACE/GRACE-FO files with each GRACE/GRACE-FO month
    grace_files = grace_date(base_dir,
                             PROC=PROC,
                             DREL=DREL,
                             DSET=DSET,
                             OUTPUT=False)

    #-- importing data from GRACE/GRACE-FO files
    for i, grace_month in enumerate(months):
        #-- read spherical harmonic data products
        infile = grace_files[grace_month]
        if PROC in ('GRAZ', 'Swarm'):
            #-- Degree 2 zonals will be converted to a tide free state
            Ylms = read_gfc_harmonics(infile, TIDE='tide_free')
        else:
            #-- Effects of Pole tide drift will be compensated if specified
            Ylms = read_GRACE_harmonics(infile,
                                        LMAX,
                                        MMAX=MMAX,
                                        POLE_TIDE=kwargs['POLE_TIDE'])
        #-- truncate harmonics to degree and order
        grace_Ylms['clm'][:, :, i] = Ylms['clm'][0:LMAX + 1, 0:MMAX + 1]
        grace_Ylms['slm'][:, :, i] = Ylms['slm'][0:LMAX + 1, 0:MMAX + 1]
        #-- truncate harmonic errors to degree and order
        grace_Ylms['eclm'][:, :, i] = Ylms['eclm'][0:LMAX + 1, 0:MMAX + 1]
        grace_Ylms['eslm'][:, :, i] = Ylms['eslm'][0:LMAX + 1, 0:MMAX + 1]
        #-- copy date variables
        grace_Ylms['time'][i] = np.copy(Ylms['time'])
        grace_Ylms['month'][i] = np.int64(grace_month)
    #-- single accelerometer months
    single_acc_months = np.copy(grace_Ylms['month'][grace_Ylms['month'] > 176])

    #-- SLR low-degree harmonic, geocenter and correction flags
    FLAGS = []

    #-- Replacing C2,0 with SLR C2,0
    #-- Running function read_SLR_C20.py
    #-- reading SLR C2,0 file for given release if specified
    if (SLR_C20 == 'CSR'):
        if (DREL == 'RL04'):
            SLR_file = os.path.join(base_dir, 'TN-05_C20_SLR.txt')
        elif (DREL == 'RL05'):
            SLR_file = os.path.join(base_dir, 'TN-07_C20_SLR.txt')
        elif (DREL == 'RL06'):
            # SLR_file = os.path.join(base_dir,'TN-11_C20_SLR.txt')
            SLR_file = os.path.join(base_dir, 'C20_RL06.txt')
        C20_input = read_SLR_C20(SLR_file)
        FLAGS.append('_wCSR_C20')
    elif (SLR_C20 == 'GFZ'):
        SLR_file = os.path.join(base_dir, 'GFZ_{0}_C20_SLR.dat'.format(DREL))
        C20_input = read_SLR_C20(SLR_file)
        FLAGS.append('_wGFZ_C20')
    elif (SLR_C20 == 'GSFC'):
        SLR_file = os.path.join(base_dir, 'TN-14_C30_C20_GSFC_SLR.txt')
        C20_input = read_SLR_C20(SLR_file)
        FLAGS.append('_wGSFC_C20')

    #-- Replacing C2,1/S2,1 with SLR
    #-- Running function read_SLR_CS2.py
    if (kwargs['SLR_21'] == 'CSR'):
        SLR_file = os.path.join(base_dir, 'C21_S21_{0}.txt'.format(DREL))
        C21_input = read_SLR_CS2(SLR_file)
        FLAGS.append('_wCSR_21')
    elif (kwargs['SLR_21'] == 'GFZ'):
        GravIS_file = 'GRAVIS-2B_GFZOP_GRACE+SLR_LOW_DEGREES_0002.dat'
        SLR_file = os.path.join(base_dir, GravIS_file)
        C21_input = read_SLR_CS2(SLR_file)
        FLAGS.append('_wGFZ_21')
    elif (kwargs['SLR_21'] == 'GSFC'):
        #-- calculate monthly averages from 7-day arcs
        # SLR_file = os.path.join(base_dir,'GSFC_C21_S21.txt')
        SLR_file = os.path.join(base_dir, 'gsfc_slr_5x5c61s61.txt')
        C21_input = read_SLR_CS2(SLR_file, DATE=grace_Ylms['time'], ORDER=1)
        FLAGS.append('_wGSFC_21')

    #-- Replacing C2,2/S2,2 with SLR
    #-- Running function read_SLR_CS2.py
    if (kwargs['SLR_22'] == 'CSR'):
        SLR_file = os.path.join(base_dir, 'C22_S22_{0}.txt'.format(DREL))
        C22_input = read_SLR_CS2(SLR_file)
        FLAGS.append('_wCSR_22')
    elif (kwargs['SLR_22'] == 'GSFC'):
        SLR_file = os.path.join(base_dir, 'gsfc_slr_5x5c61s61.txt')
        C22_input = read_SLR_CS2(SLR_file, DATE=grace_Ylms['time'], ORDER=2)
        FLAGS.append('_wGSFC_22')

    #-- Replacing C3,0 with SLR C3,0
    #-- Running function read_SLR_C30.py
    if (kwargs['SLR_C30'] == 'CSR'):
        SLR_file = os.path.join(base_dir,
                                'CSR_Monthly_5x5_Gravity_Harmonics.txt')
        C30_input = read_SLR_C30(SLR_file)
        FLAGS.append('_wCSR_C30')
    elif (kwargs['SLR_C30'] == 'LARES'):
        SLR_file = os.path.join(base_dir, 'C30_LARES_filtered.txt')
        C30_input = read_SLR_C30(SLR_file)
        FLAGS.append('_wLARES_C30')
    elif (kwargs['SLR_C30'] == 'GSFC'):
        SLR_file = os.path.join(base_dir, 'TN-14_C30_C20_GSFC_SLR.txt')
        C30_input = read_SLR_C30(SLR_file)
        FLAGS.append('_wGSFC_C30')
    elif (kwargs['SLR_C30'] == 'GFZ'):
        GravIS_file = 'GRAVIS-2B_GFZOP_GRACE+SLR_LOW_DEGREES_0002.dat'
        SLR_file = os.path.join(base_dir, GravIS_file)
        C30_input = read_SLR_C30(SLR_file)
        FLAGS.append('_wGFZ_C30')

    #-- Replacing C5,0 with SLR C5,0
    #-- Running function read_SLR_C50.py
    if (kwargs['SLR_C50'] == 'CSR'):
        SLR_file = os.path.join(base_dir,
                                'CSR_Monthly_5x5_Gravity_Harmonics.txt')
        C50_input = read_SLR_C50(SLR_file)
        FLAGS.append('_wCSR_C50')
    elif (kwargs['SLR_C50'] == 'LARES'):
        SLR_file = os.path.join(base_dir, 'C50_LARES_filtered.txt')
        C50_input = read_SLR_C50(SLR_file)
        FLAGS.append('_wLARES_C50')
    elif (kwargs['SLR_C50'] == 'GSFC'):
        # SLR_file=os.path.join(base_dir,'GSFC_SLR_C20_C30_C50_GSM_replacement.txt')
        SLR_file = os.path.join(base_dir, 'gsfc_slr_5x5c61s61.txt')
        C50_input = read_SLR_C50(SLR_file, DATE=grace_Ylms['time'])
        FLAGS.append('_wGSFC_C50')

    #-- Correcting for Degree 1 (geocenter variations)
    #-- reading degree 1 file for given release if specified
    if (DEG1 == 'Tellus'):
        #-- Tellus (PO.DAAC) degree 1
        if DREL in ('RL04', 'RL05'):
            #-- old degree one files
            default_geocenter = os.path.join(base_dir, 'geocenter',
                                             'deg1_coef_{0}.txt'.format(DREL))
            JPL = False
        else:
            #-- new TN-13 degree one files
            default_geocenter = os.path.join(
                base_dir, 'geocenter',
                'TN-13_GEOC_{0}_{1}.txt'.format(PROC, DREL))
            JPL = True
        #-- read degree one files from JPL GRACE Tellus
        DEG1_file = kwargs.get('DEG1_FILE') or default_geocenter
        DEG1_input = gravity_toolkit.geocenter().from_tellus(DEG1_file,
                                                             JPL=JPL)
        FLAGS.append('_w{0}_DEG1'.format(DEG1))
    elif (DEG1 == 'SLR'):
        #-- CSR Satellite Laser Ranging (SLR) degree 1
        # #-- SLR-derived degree-1 mass variations
        # #-- ftp://ftp.csr.utexas.edu/pub/slr/geocenter/
        # DEG1_file=os.path.join(base_dir,'geocenter','GCN_{0}.txt'.format(DREL))
        # COLUMNS = ['time','X','Y','Z','X_sigma','Y_sigma','Z_sigma']
        # DEG1_input = gravity_toolkit.geocenter().from_SLR(DEG1_file,
        #      AOD=True, release=DREL, header=16, COLUMNS=COLUMNS)

        # #-- new CF-CM file of degree-1 mass variations
        # #-- https://cddis.nasa.gov/lw20/docs/2016/papers/14-Ries_paper.pdf
        # #-- http://download.csr.utexas.edu/pub/slr/geocenter/GCN_L1_L2_30d_CF-CM.txt
        # DEG1_file = os.path.join(base_dir,'geocenter','GCN_L1_L2_30d_CF-CM.txt')
        # COLUMNS = ['time','X','Y','Z','X_sigma','Y_sigma','Z_sigma']
        # DEG1_input = gravity_toolkit.geocenter().from_SLR(DEG1_file,
        #     AOD=True, release=DREL, header=111, columns=COLUMNS)

        #-- new file of degree-1 mass variations from Minkang Cheng
        #-- http://download.csr.utexas.edu/outgoing/cheng/gct2est.220_5s
        DEG1_file = os.path.join(base_dir, 'geocenter', 'gct2est.220_5s')
        COLUMNS = [
            'MJD', 'time', 'X', 'Y', 'Z', 'XM', 'YM', 'ZM', 'X_sigma',
            'Y_sigma', 'Z_sigma', 'XM_sigma', 'YM_sigma', 'ZM_sigma'
        ]
        #-- read degree one files from CSR satellite laser ranging
        DEG1_input = gravity_toolkit.geocenter(radius=6.378136e9).from_SLR(
            DEG1_file, AOD=True, release=DREL, header=15, columns=COLUMNS)
        FLAGS.append('_w{0}_DEG1'.format(DEG1))
    elif (DEG1 == 'SLF'):
        #-- degree one files from Sutterley and Velicogna (2019)
        #-- default: iterated and with self-attraction and loading effects
        MODEL = dict(RL04='OMCT', RL05='OMCT', RL06='MPIOM')
        args = (PROC, DREL, MODEL[DREL], 'SLF_iter')
        default_geocenter = os.path.join(base_dir, 'geocenter',
                                         '{0}_{1}_{2}_{3}.txt'.format(*args))
        #-- read degree one files from Sutterley and Velicogna (2019)
        DEG1_file = kwargs.get('DEG1_FILE') or default_geocenter
        DEG1_input = gravity_toolkit.geocenter().from_UCI(DEG1_file)
        FLAGS.append('_w{0}_DEG1'.format(DEG1))
    elif (DEG1 == 'Swenson'):
        #-- degree 1 coefficients provided by Sean Swenson in mm w.e.
        default_geocenter = os.path.join(base_dir, 'geocenter',
                                         'gad_gsm.{0}.txt'.format(DREL))
        #-- read degree one files from Swenson et al. (2008)
        DEG1_file = kwargs.get('DEG1_FILE') or default_geocenter
        DEG1_input = gravity_toolkit.geocenter().from_swenson(DEG1_file)
        FLAGS.append('_w{0}_DEG1'.format(DEG1))
    elif (DEG1 == 'GFZ'):
        #-- degree 1 coefficients provided by GFZ GravIS
        #-- http://gravis.gfz-potsdam.de/corrections
        default_geocenter = os.path.join(base_dir, 'geocenter',
                                         'GRAVIS-2B_GFZOP_GEOCENTER_0002.dat')
        #-- read degree one files from GFZ GravIS
        DEG1_file = kwargs.get('DEG1_FILE') or default_geocenter
        DEG1_input = gravity_toolkit.geocenter().from_gravis(DEG1_file)
        FLAGS.append('_w{0}_DEG1'.format(DEG1))

    #-- atmospheric flag if correcting ECMWF "jumps" (using GAE/GAF/GAG files)
    if kwargs['ATM']:
        FLAGS.append('_wATM')
    #-- pole tide flag if correcting for pole tide drift (Wahr et al. 2015)
    if kwargs['POLE_TIDE']:
        FLAGS.append('_wPT')
    #-- full output string (SLR, geocenter and correction flags)
    grace_Ylms['title'] = ''.join(FLAGS)

    #-- Replace C20 with SLR coefficients
    if SLR_C20 in ('CSR', 'GFZ', 'GSFC'):
        #-- verify that there are replacement C20 months for specified range
        months_test = sorted(set(months) - set(C20_input['month']))
        if months_test:
            gm = ','.join('{0:03d}'.format(gm) for gm in months_test)
            raise IOError('No Matching C20 Months ({0})'.format(gm))
        #-- replace C20 with SLR coefficients
        for i, grace_month in enumerate(months):
            count = np.count_nonzero(C20_input['month'] == grace_month)
            if (count != 0):
                k, = np.nonzero(C20_input['month'] == grace_month)
                grace_Ylms['clm'][2, 0, i] = np.copy(C20_input['data'][k])
                grace_Ylms['eclm'][2, 0, i] = np.copy(C20_input['error'][k])

    #-- Replace C21/S21 with SLR coefficients for single-accelerometer months
    if kwargs['SLR_21'] in ('CSR', 'GFZ', 'GSFC'):
        #-- verify that there are replacement C21/S21 months for specified range
        months_test = sorted(set(single_acc_months) - set(C21_input['month']))
        if months_test:
            gm = ','.join('{0:03d}'.format(gm) for gm in months_test)
            raise IOError('No Matching C21/S21 Months ({0})'.format(gm))
        #-- replace C21/S21 with SLR coefficients
        for i, grace_month in enumerate(months):
            count = np.count_nonzero(C21_input['month'] == grace_month)
            if (count != 0) and (grace_month > 176):
                k, = np.nonzero(C21_input['month'] == grace_month)
                grace_Ylms['clm'][2, 1, i] = np.copy(C21_input['C2m'][k])
                grace_Ylms['slm'][2, 1, i] = np.copy(C21_input['S2m'][k])
                grace_Ylms['eclm'][2, 1, i] = np.copy(C21_input['eC2m'][k])
                grace_Ylms['eslm'][2, 1, i] = np.copy(C21_input['eS2m'][k])

    #-- Replace C22/S22 with SLR coefficients for single-accelerometer months
    if kwargs['SLR_22'] in ('CSR', 'GSFC'):
        #-- verify that there are replacement C22/S22 months for specified range
        months_test = sorted(set(single_acc_months) - set(C22_input['month']))
        if months_test:
            gm = ','.join('{0:03d}'.format(gm) for gm in months_test)
            raise IOError('No Matching C22/S22 Months ({0})'.format(gm))
        #-- replace C22/S22 with SLR coefficients
        for i, grace_month in enumerate(months):
            count = np.count_nonzero(C22_input['month'] == grace_month)
            if (count != 0) and (grace_month > 176):
                k, = np.nonzero(C22_input['month'] == grace_month)
                grace_Ylms['clm'][2, 2, i] = np.copy(C22_input['C2m'][k])
                grace_Ylms['slm'][2, 2, i] = np.copy(C22_input['S2m'][k])
                grace_Ylms['eclm'][2, 2, i] = np.copy(C22_input['eC2m'][k])
                grace_Ylms['eslm'][2, 2, i] = np.copy(C22_input['eS2m'][k])

    #-- Replace C30 with SLR coefficients for single-accelerometer months
    if kwargs['SLR_C30'] in ('CSR', 'GFZ', 'GSFC', 'LARES'):
        #-- verify that there are replacement C30 months for specified range
        months_test = sorted(set(single_acc_months) - set(C30_input['month']))
        if months_test:
            gm = ','.join('{0:03d}'.format(gm) for gm in months_test)
            raise IOError('No Matching C30 Months ({0})'.format(gm))
        #-- replace C30 with SLR coefficients
        for i, grace_month in enumerate(months):
            count = np.count_nonzero(C30_input['month'] == grace_month)
            if (count != 0) and (grace_month > 176):
                k, = np.nonzero(C30_input['month'] == grace_month)
                grace_Ylms['clm'][3, 0, i] = np.copy(C30_input['data'][k])
                grace_Ylms['eclm'][3, 0, i] = np.copy(C30_input['error'][k])

    #-- Replace C50 with SLR coefficients for single-accelerometer months
    if kwargs['SLR_C50'] in ('CSR', 'GSFC', 'LARES'):
        #-- verify that there are replacement C50 months for specified range
        months_test = sorted(set(single_acc_months) - set(C50_input['month']))
        if months_test:
            gm = ','.join('{0:03d}'.format(gm) for gm in months_test)
            raise IOError('No Matching C50 Months ({0})'.format(gm))
        #-- replace C50 with SLR coefficients
        for i, grace_month in enumerate(months):
            count = np.count_nonzero(C50_input['month'] == grace_month)
            if (count != 0) and (grace_month > 176):
                k, = np.nonzero(C50_input['month'] == grace_month)
                grace_Ylms['clm'][5, 0, i] = np.copy(C50_input['data'][k])
                grace_Ylms['eclm'][5, 0, i] = np.copy(C50_input['error'][k])

    #-- Use Degree 1 coefficients
    #-- Tellus: Tellus Degree 1 (PO.DAAC following Sun et al., 2016)
    #-- SLR: CSR Satellite Laser Ranging (SLR) Degree 1 - GRACE AOD
    #-- SLF: OMCT/MPIOM coefficients with Sea Level Fingerprint land-water mass
    #-- Swenson: GRACE-derived coefficients from Sean Swenson
    #-- GFZ: GRACE/GRACE-FO coefficients from GFZ GravIS
    if DEG1 in ('Tellus', 'SLR', 'SLF', 'Swenson', 'GFZ'):
        #-- check if modeling degree 1 or if all months are available
        if kwargs['MODEL_DEG1']:
            #-- least-squares modeling the degree 1 coefficients
            #-- fitting annual, semi-annual, linear and quadratic terms
            C10_model = regress_model(DEG1_input.time,
                                      DEG1_input.C10,
                                      grace_Ylms['time'],
                                      ORDER=2,
                                      CYCLES=[0.5, 1.0],
                                      RELATIVE=2003.3)
            C11_model = regress_model(DEG1_input.time,
                                      DEG1_input.C11,
                                      grace_Ylms['time'],
                                      ORDER=2,
                                      CYCLES=[0.5, 1.0],
                                      RELATIVE=2003.3)
            S11_model = regress_model(DEG1_input.time,
                                      DEG1_input.S11,
                                      grace_Ylms['time'],
                                      ORDER=2,
                                      CYCLES=[0.5, 1.0],
                                      RELATIVE=2003.3)
        else:
            #-- check that all months are available for a given geocenter
            months_test = sorted(set(months) - set(DEG1_input.month))
            if months_test:
                gm = ','.join('{0:03d}'.format(gm) for gm in months_test)
                raise IOError('No Matching Geocenter Months ({0})'.format(gm))
        #-- for each considered date
        for i, grace_month in enumerate(months):
            k, = np.nonzero(DEG1_input.month == grace_month)
            count = np.count_nonzero(DEG1_input.month == grace_month)
            #-- Degree 1 is missing for particular month
            if (count == 0) and kwargs['MODEL_DEG1']:
                #-- using least-squares modeled coefficients from regress_model
                grace_Ylms['clm'][1, 0, i] = np.copy(C10_model[i])
                grace_Ylms['clm'][1, 1, i] = np.copy(C11_model[i])
                grace_Ylms['slm'][1, 1, i] = np.copy(S11_model[i])
            else:  #-- using coefficients from data file
                grace_Ylms['clm'][1, 0, i] = np.copy(DEG1_input.C10[k])
                grace_Ylms['clm'][1, 1, i] = np.copy(DEG1_input.C11[k])
                grace_Ylms['slm'][1, 1, i] = np.copy(DEG1_input.S11[k])

    #-- read and add/remove the GAE and GAF atmospheric correction coefficients
    if kwargs['ATM']:
        #-- read ECMWF correction files from Fagiolini et al. (2015)
        atm_corr = read_ecmwf_corrections(base_dir, LMAX, months, MMAX=MMAX)
        #-- Removing GAE/GAF/GAG from RL05 GSM Products
        if (DSET == 'GSM'):
            for m in range(0, MMAX + 1):  #-- MMAX+1 to include l
                for l in range(m, LMAX + 1):  #-- LMAX+1 to include LMAX
                    grace_Ylms['clm'][l, m, :] -= atm_corr['clm'][l, m, :]
                    grace_Ylms['slm'][l, m, :] -= atm_corr['slm'][l, m, :]
        #-- Adding GAE/GAF/GAG to RL05 Atmospheric Products (GAA,GAC)
        elif DSET in ('GAC', 'GAA'):
            for m in range(0, MMAX + 1):  #-- MMAX+1 to include l
                for l in range(m, LMAX + 1):  #-- LMAX+1 to include LMAX
                    grace_Ylms['clm'][l, m, :] += atm_corr['clm'][l, m, :]
                    grace_Ylms['slm'][l, m, :] += atm_corr['slm'][l, m, :]

    #-- return the harmonic solutions with possible low-degree replacements
    #-- return the harmonic dimensions (spectral and temporal)
    #-- return string specifying processing and correction flags
    #-- return directory of exact GRACE/GRACE-FO product
    return grace_Ylms