Exemplo n.º 1
0
def read_AOD1b_geocenter(AOD1B_file, calendar_month):
    """
    Reads monthly non-tidal ocean and atmospheric variation geocenter files

    Arguments
    ---------
    AOD1B_file: de-aliasing data file
    calendar_month: calendar month of data
    """
    #-- check that file exists
    if not os.access(AOD1B_file, os.F_OK):
        raise IOError('AOD1b File {0} not in File System'.format(AOD1B_file))
    #-- read AOD1b geocenter skipping over commented header text
    with open(AOD1B_file, 'r') as f:
        file_contents = [
            i for i in f.read().splitlines() if not re.match(r'#', i)
        ]
    #-- extract X,Y,Z from each line in the file
    #-- first column: ISO-formatted date and time
    #-- second-fourth columns: X, Y and Z geocenter variations
    n_lines = len(file_contents)
    X = np.zeros((n_lines))
    Y = np.zeros((n_lines))
    Z = np.zeros((n_lines))
    month = np.zeros((n_lines), dtype=np.int)
    for i, line in enumerate(file_contents):
        line_contents = line.split()
        AOD1B_time = time.strptime(line_contents[0], '%Y-%m-%dT%H:%M:%S')
        month[i] = AOD1B_time.tm_mon
        X[i], Y[i], Z[i] = np.array(line_contents[1:], dtype=np.float)
    #-- use only dates within month (should be all)
    ii, = np.nonzero(month == calendar_month)
    #-- convert mean X,Y,Z into spherical harmonics
    return geocenter(X=X[ii].mean(),
                     Y=Y[ii].mean(),
                     Z=Z[ii].mean(),
                     INVERSE=True)
def geocenter_processing_centers(grace_dir, PROC, DREL, START_MON, END_MON,
                                 MISSING):
    #-- GRACE months
    GAP = [187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197]
    months = sorted(set(np.arange(START_MON, END_MON + 1)) - set(MISSING))
    #-- labels for each scenario
    input_flags = [
        '', 'iter', 'SLF_iter', 'SLF_iter_wSLR21', 'SLF_iter_wSLR21_wSLR22'
    ]
    input_labels = ['Static', 'Iterated', 'Iterated SLF']
    #-- labels for Release-6
    model_str = 'OMCT' if DREL in ('RL04', 'RL05') else 'MPIOM'
    #-- degree one coefficient labels
    fig_labels = ['C11', 'S11', 'C10']
    axes_labels = dict(C10='c)', C11='a)', S11='b)')
    ylabels = dict(C10='z', C11='x', S11='y')

    #-- plot colors for each dataset
    plot_colors = dict(CSR='darkorange',
                       GFZ='darkorchid',
                       JPL='mediumseagreen')
    plot_colors['GFZwPT'] = 'dodgerblue'
    plot_colors['GFZ+CS21'] = 'darkorchid'
    plot_colors['GFZ+CS21+CS22'] = 'darkorchid'

    #-- 3 row plot (C10, C11 and S11)
    ax = {}
    fig, (ax[0], ax[1], ax[2]) = plt.subplots(num=1,
                                              ncols=3,
                                              sharey=True,
                                              figsize=(9, 4))
    #-- plot geocenter estimates for each processing center
    for k, pr in enumerate(PROC):
        #-- additionally plot GFZ with SLR replaced pole tide
        if pr in ('GFZwPT', 'GFZ+CS21'):
            fargs = ('GFZ', DREL, model_str, input_flags[3])
        elif (pr == 'GFZ+CS21+CS22'):
            fargs = ('GFZ', DREL, model_str, input_flags[4])
        else:
            fargs = (pr, DREL, model_str, input_flags[2])
        #-- read geocenter file for processing center and model
        grace_file = '{0}_{1}_{2}_{3}.txt'.format(*fargs)
        DEG1 = geocenter().from_UCI(os.path.join(grace_dir, grace_file))
        #-- indices for mean months
        kk, = np.nonzero((DEG1.month >= START_MON) & (DEG1.month <= 176))
        DEG1.mean(apply=True, indices=kk)
        #-- setting Load Love Number (kl) to 0.021 to match Swenson et al. (2008)
        DEG1.to_cartesian(kl=0.021)
        #-- plot each coefficient
        for j, key in enumerate(fig_labels):
            #-- create a time series with nans for missing months
            tdec = np.full_like(months, np.nan, dtype=np.float64)
            data = np.full_like(months, np.nan, dtype=np.float64)
            val = getattr(DEG1, ylabels[key].upper())
            for i, m in enumerate(months):
                valid = np.count_nonzero(DEG1.month == m)
                if valid:
                    mm, = np.nonzero(DEG1.month == m)
                    tdec[i] = DEG1.time[mm]
                    data[i] = val[mm]
            #-- plot all dates
            ax[j].plot(tdec, data, color=plot_colors[pr], label=pr)

    #-- add axis labels and adjust font sizes for axis ticks
    for j, key in enumerate(fig_labels):
        #-- vertical line denoting the accelerometer shutoff
        acc = convert_calendar_decimal(2016, 9, day=3, hour=12, minute=12)
        ax[j].axvline(acc, color='0.5', ls='dashed', lw=0.5, dashes=(8, 4))
        #-- vertical lines for end of the GRACE mission and start of GRACE-FO
        jj, = np.flatnonzero(DEG1.month == 186)
        kk, = np.flatnonzero(DEG1.month == 198)
        vs = ax[j].axvspan(DEG1.time[jj],
                           DEG1.time[kk],
                           color='0.5',
                           ls='dashed',
                           alpha=0.15)
        vs._dashes = (4, 2)
        #-- axis label
        ax[j].set_title(ylabels[key], style='italic', fontsize=14)
        ax[j].add_artist(
            AnchoredText(axes_labels[key],
                         pad=0.,
                         prop=dict(size=16, weight='bold'),
                         frameon=False,
                         loc=2))
        ax[j].set_xlabel('Time [Yr]', fontsize=14)
        #-- set ticks
        xmin = 2002 + (START_MON + 1.0) // 12.0
        xmax = 2002 + (END_MON + 1.0) / 12.0
        major_ticks = np.arange(2005, xmax, 5)
        ax[j].xaxis.set_ticks(major_ticks)
        minor_ticks = sorted(set(np.arange(xmin, xmax, 1)) - set(major_ticks))
        ax[j].xaxis.set_ticks(minor_ticks, minor=True)
        ax[j].set_xlim(xmin, xmax)
        ax[j].set_ylim(-9.5, 8.5)
        #-- axes tick adjustments
        ax[j].get_xaxis().set_tick_params(which='both', direction='in')
        ax[j].get_yaxis().set_tick_params(which='both', direction='in')
        for tick in ax[j].xaxis.get_major_ticks():
            tick.label.set_fontsize(14)
        for tick in ax[j].yaxis.get_major_ticks():
            tick.label.set_fontsize(14)

    #-- add legend
    lgd = ax[0].legend(loc=3, frameon=False)
    lgd.get_frame().set_alpha(1.0)
    for line in lgd.get_lines():
        line.set_linewidth(6)
    for i, text in enumerate(lgd.get_texts()):
        text.set_weight('bold')
        text.set_color(plot_colors[text.get_text()])
    #-- labels and set limits
    ax[0].set_ylabel('Geocenter Variation [mm]', fontsize=14)
    #-- adjust locations of subplots
    fig.subplots_adjust(left=0.06,
                        right=0.98,
                        bottom=0.12,
                        top=0.94,
                        wspace=0.05)
    #-- save figure to file
    OUTPUT_FIGURE = 'SV19_{0}_centers.pdf'.format(DREL)
    plt.savefig(os.path.join(grace_dir, OUTPUT_FIGURE), format='pdf', dpi=300)
    plt.clf()
Exemplo n.º 3
0
def read_SLR_geocenter(
        geocenter_file,
        RADIUS=None,
        HEADER=0,
        COLUMNS=['time', 'X', 'Y', 'Z', 'X_sigma', 'Y_sigma', 'Z_sigma']):
    """
    Reads monthly geocenter files from satellite laser ranging

    Arguments
    ---------
    geocenter_file: Satellite Laser Ranging file

    Keyword arguments
    -----------------
    RADIUS: Earth's radius for calculating spherical harmonics from SLR data
    HEADER: rows of data to skip when importing data
    COLUMNS: column names of ascii file
        time: date in decimal-years
        X: X-component of geocenter variation
        Y: Y-component of geocenter variation
        Z: Z-component of geocenter variation
        X_sigma: X-component uncertainty
        Y_sigma: Y-component uncertainty
        Z_sigma: Z-component uncertainty

    Returns
    -------
    C10: cosine d1/o0 spherical harmonic coefficients
    C11: cosine d1/o1 spherical harmonic coefficients
    S11: sine d1/o1 spherical harmonic coefficients
    eC10: cosine d1/o0 spherical harmonic coefficient error
    eC11: cosine d1/o1 spherical harmonic coefficient error
    eS11: sine d1/o1 spherical harmonic coefficient error
    month: GRACE/GRACE-FO month
    time: date of each month in year-decimal
    """

    #-- check that geocenter file exists
    if not os.access(os.path.expanduser(geocenter_file), os.F_OK):
        raise IOError('Geocenter file not found in file system')

    #-- Input geocenter file and split lines
    with open(os.path.expanduser(geocenter_file), 'r') as f:
        file_contents = f.read().splitlines()
    ndate = len(file_contents) - HEADER

    #-- compile regular expression operator to find numerical instances
    regex_pattern = r'[-+]?(?:(?:\d*\.\d+)|(?:\d+\.?))(?:[Ee][+-]?\d+)?'
    rx = re.compile(regex_pattern, re.VERBOSE)

    #-- initializing output data
    #-- Degree 1 Stokes Coefficients
    C10 = np.zeros((ndate))
    C11 = np.zeros((ndate))
    S11 = np.zeros((ndate))
    #-- Degree 1 Stokes Coefficient Errors
    eC10 = np.zeros((ndate))
    eC11 = np.zeros((ndate))
    eS11 = np.zeros((ndate))
    #-- Date information
    date = np.zeros((ndate))
    JD = np.zeros((ndate))
    mon = np.zeros((ndate), dtype=np.int32)

    #-- for each date
    for t, file_line in enumerate(file_contents[HEADER:]):
        #-- find numerical instances in line
        #-- replacing fortran double precision exponential
        line_contents = rx.findall(file_line.replace('D', 'E'))
        #-- extract date
        date[t] = np.float(line_contents[COLUMNS.index('time')])
        #-- extract geocenter variations
        X = np.float(line_contents[COLUMNS.index('X')])
        Y = np.float(line_contents[COLUMNS.index('Y')])
        Z = np.float(line_contents[COLUMNS.index('Z')])
        X_sigma = np.float(line_contents[COLUMNS.index('X_sigma')])
        Y_sigma = np.float(line_contents[COLUMNS.index('Y_sigma')])
        Z_sigma = np.float(line_contents[COLUMNS.index('Z_sigma')])
        #-- converting from geocenter into spherical harmonics
        CS1 = geocenter(X=X, Y=Y, Z=Z, RADIUS=RADIUS, INVERSE=True)
        dCS1 = geocenter(X=X_sigma,
                         Y=Y_sigma,
                         Z=Z_sigma,
                         RADIUS=RADIUS,
                         INVERSE=True)
        #-- output harmonics
        C10[t], C11[t], S11[t] = (CS1['C10'], CS1['C11'], CS1['S11'])
        eC10[t], eC11[t], eS11[t] = (dCS1['C10'], dCS1['C11'], dCS1['S11'])

        #-- calendar year of date
        year = np.floor(date[t])
        #-- check if year is a leap year
        dpy = 366.0 if ((year % 4) == 0) else 365.0
        #-- calculation of day of the year (with decimals for fraction of day)
        DofY = dpy * (date[t] % 1)
        #-- Calculation of the Julian date from year and DofY
        JD[t] = np.float(367.0 * year -
                         np.floor(7.0 * (year + np.floor(10.0 / 12.0)) / 4.0) -
                         np.floor(3.0 * (np.floor(
                             (year - 8.0 / 7.0) / 100.0) + 1.0) / 4.0) +
                         np.floor(275.0 / 9.0) + DofY + 1721028.5)
        #-- convert the julian date into calendar dates (hour, day, month, year)
        cal_date = convert_julian(JD[t])
        #-- calculate the GRACE/GRACE-FO month (Apr02 == 004)
        #-- https://grace.jpl.nasa.gov/data/grace-months/
        mon[t] = 12 * (cal_date['year'] - 2002) + cal_date['month']

    return {
        'C10': C10,
        'C11': C11,
        'S11': S11,
        'eC10': eC10,
        'eC11': eC11,
        'eS11': eS11,
        'month': mon,
        'time': date
    }
Exemplo n.º 4
0
def aod_corrected_SLR_geocenter(geocenter_file,
                                DREL,
                                RADIUS=None,
                                HEADER=0,
                                COLUMNS=[]):
    """
    Reads monthly geocenter files from satellite laser ranging corrected
    for non-tidal ocean and atmospheric variation

    Arguments
    ---------
    geocenter_file: Satellite Laser Ranging file
    DREL: GRACE/GRACE-FO data release

    Keyword arguments
    -----------------
    RADIUS: Earth's radius for calculating spherical harmonics from SLR data
    HEADER: rows of data to skip when importing data
    COLUMNS: column names of ascii file
        time: date in decimal-years
        X: X-component of geocenter variation
        Y: Y-component of geocenter variation
        Z: Z-component of geocenter variation
        X_sigma: X-component uncertainty
        Y_sigma: Y-component uncertainty
        Z_sigma: Z-component uncertainty

    Returns
    -------
    C10: cosine d1/o0 spherical harmonic coefficients
    C11: cosine d1/o1 spherical harmonic coefficients
    S11: sine d1/o1 spherical harmonic coefficients
    eC10: cosine d1/o0 spherical harmonic coefficient error
    eC11: cosine d1/o1 spherical harmonic coefficient error
    eS11: sine d1/o1 spherical harmonic coefficient error
    month: GRACE/GRACE-FO month
    time: date of each month in year-decimal
    """
    #-- directory setup for AOD1b data starting with input degree 1 file
    #-- this will verify that the input paths work
    AOD1B_dir = os.path.abspath(
        os.path.join(geocenter_file, os.path.pardir, os.path.pardir, 'AOD1B',
                     DREL, 'geocenter'))

    #-- Input geocenter file and split lines
    with open(os.path.expanduser(geocenter_file), 'r') as f:
        file_contents = f.read().splitlines()
    ndate = len(file_contents) - HEADER

    #-- compile regular expression operator to find numerical instances
    regex_pattern = r'[-+]?(?:(?:\d*\.\d+)|(?:\d+\.?))(?:[Ee][+-]?\d+)?'
    rx = re.compile(regex_pattern, re.VERBOSE)

    #-- initializing output data
    #-- Degree 1 Stokes Coefficients
    C10 = np.zeros((ndate))
    C11 = np.zeros((ndate))
    S11 = np.zeros((ndate))
    #-- Degree 1 Stokes Coefficient Errors
    eC10 = np.zeros((ndate))
    eC11 = np.zeros((ndate))
    eS11 = np.zeros((ndate))
    #-- Date information
    date = np.zeros((ndate))
    JD = np.zeros((ndate))
    mon = np.zeros((ndate), dtype=np.int32)

    #-- for each date
    for t, file_line in enumerate(file_contents[HEADER:]):
        #-- find numerical instances in line
        #-- replacing fortran double precision exponential
        line_contents = rx.findall(file_line.replace('D', 'E'))
        #-- extract date
        date[t] = np.float(line_contents[COLUMNS.index('time')])
        #-- extract geocenter variations
        X = np.float(line_contents[COLUMNS.index('X')])
        Y = np.float(line_contents[COLUMNS.index('Y')])
        Z = np.float(line_contents[COLUMNS.index('Z')])
        X_sigma = np.float(line_contents[COLUMNS.index('X_sigma')])
        Y_sigma = np.float(line_contents[COLUMNS.index('Y_sigma')])
        Z_sigma = np.float(line_contents[COLUMNS.index('Z_sigma')])
        #-- converting from geocenter into spherical harmonics
        CS1 = geocenter(X=X, Y=Y, Z=Z, RADIUS=RADIUS, INVERSE=True)
        dCS1 = geocenter(X=X_sigma,
                         Y=Y_sigma,
                         Z=Z_sigma,
                         RADIUS=RADIUS,
                         INVERSE=True)

        #-- calendar year of date
        year = np.floor(date[t])
        #-- check if year is a leap year
        dpy = 366.0 if ((year % 4) == 0) else 365.0
        #-- calculation of day of the year (with decimals for fraction of day)
        DofY = dpy * (date[t] % 1)
        #-- Calculation of the Julian date from year and DofY
        JD[t] = np.float(367. * year -
                         np.floor(7. * (year + np.floor(10. / 12.)) / 4.) -
                         np.floor(3.0 * (np.floor(
                             (year - 8.0 / 7.0) / 100.0) + 1.0) / 4.0) +
                         np.floor(275.0 / 9.0) + DofY + 1721028.5)
        #-- convert the julian date into calendar dates (hour, day, month, year)
        cal_date = convert_julian(JD[t], ASTYPE=np.int)
        #-- full path to AOD geocenter for month (using glo coefficients)
        args = (DREL, 'glo', cal_date['year'], cal_date['month'])
        AOD1B_file = 'AOD1B_{0}_{1}_{2:4d}_{3:02d}.txt'.format(*args)
        Ylms = read_AOD1b_geocenter(os.path.join(AOD1B_dir, AOD1B_file),
                                    cal_date['month'])
        #-- remove AOD from output harmonics
        C10[t] = CS1['C10'] - Ylms['C10']
        C11[t] = CS1['C11'] - Ylms['C11']
        S11[t] = CS1['S11'] - Ylms['S11']
        eC10[t], eC11[t], eS11[t] = (dCS1['C10'], dCS1['C11'], dCS1['S11'])
        #-- calculate the GRACE/GRACE-FO month (Apr02 == 004)
        #-- https://grace.jpl.nasa.gov/data/grace-months/
        mon[t] = 12 * (cal_date['year'] - 2002) + cal_date['month']

    return {
        'C10': C10,
        'C11': C11,
        'S11': S11,
        'eC10': eC10,
        'eC11': eC11,
        'eS11': eS11,
        'month': mon,
        'time': date
    }
def aod1b_geocenter(base_dir, DREL='', DSET='', CLOBBER=False, MODE=0o775):
    """
    Creates monthly files of geocenter variations at 6-hour or 3-hour intervals from
    GRACE/GRACE-FO level-1b dealiasing data files

    Arguments
    ---------
    base_dir: working data directory

    Keyword arguments
    -----------------
    DREL: GRACE/GRACE-FO data release
    DSET: GRACE/GRACE-FO dataset
        atm: atmospheric loading from ECMWF
        ocn: oceanic loading from OMCT/MPIOM
        glo: global atmospheric and oceanic loading
        oba: ocean bottom pressure from OMCT/MPIOM
    CLOBBER: overwrite existing data
    MODE: Permission mode of directories and files
    """

    #-- compile regular expressions operators for file dates
    #-- will extract the year and month from the tar file (.tar.gz)
    tx = re.compile(r'AOD1B_(\d+)-(\d+)_\d+\.(tar\.gz|tgz)$', re.VERBOSE)
    #-- and the calendar day from the ascii file (.asc or gzipped .asc.gz)
    fx = re.compile(r'AOD1B_\d+-\d+-(\d+)_X_\d+.asc(.gz)?$', re.VERBOSE)
    #-- compile regular expressions operator for the clm/slm headers
    #-- for the specific AOD1b product
    hx = re.compile(r'^DATA.*SET.*{0}'.format(DSET), re.VERBOSE)
    #-- compile regular expression operator to find numerical instances
    #-- will extract the data from the file
    regex_pattern = r'[-+]?(?:(?:\d*\.\d+)|(?:\d+\.?))(?:[Ee][+-]?\d+)?'
    rx = re.compile(regex_pattern, re.VERBOSE)
    #-- output formatting string
    fstr = '{0:4d}-{1:02d}-{2:02d}T{3:02d}:00:00 {4:12.8f} {5:12.8f} {6:12.8f}'

    #-- set number of hours in a file
    #-- set the ocean model for a given release
    if DREL in ('RL01', 'RL02', 'RL03', 'RL04', 'RL05'):
        #-- for 00, 06, 12 and 18
        n_time = 4
        ATMOSPHERE = 'ECMWF'
        OCEAN_MODEL = 'OMCT'
        LMAX = 100
    elif DREL in ('RL06', ):
        #-- for 00, 03, 06, 09, 12, 15, 18 and 21
        n_time = 8
        ATMOSPHERE = 'ECMWF'
        OCEAN_MODEL = 'MPIOM'
        LMAX = 180
    else:
        raise ValueError('Invalid data release')
    #-- Calculating the number of cos and sin harmonics up to LMAX
    n_harm = (LMAX**2 + 3 * LMAX) // 2 + 1

    #-- AOD1B data products
    product = {}
    product['atm'] = 'Atmospheric loading from {0}'.format(ATMOSPHERE)
    product['ocn'] = 'Oceanic loading from {0}'.format(OCEAN_MODEL)
    product['glo'] = 'Global atmospheric and oceanic loading'
    product['oba'] = 'Ocean bottom pressure from {0}'.format(OCEAN_MODEL)

    #-- AOD1B directory and output geocenter directory
    grace_dir = os.path.join(base_dir, 'AOD1B', DREL)
    output_dir = os.path.join(grace_dir, 'geocenter')
    if not os.access(output_dir, os.F_OK):
        os.mkdir(output_dir, MODE)

    #-- finding all of the tar files in the AOD1b directory
    input_tar_files = [tf for tf in os.listdir(grace_dir) if tx.match(tf)]

    #-- for each tar file
    for i in sorted(input_tar_files):
        #-- extract the year and month from the file
        YY, MM, SFX = tx.findall(i).pop()
        YY, MM = np.array([YY, MM], dtype=np.int64)
        #-- output monthly geocenter file
        FILE = 'AOD1B_{0}_{1}_{2:4d}_{3:02d}.txt'.format(DREL, DSET, YY, MM)
        #-- if output file exists: check if input tar file is newer
        TEST = False
        OVERWRITE = ' (clobber)'
        #-- check if output file exists
        if os.access(os.path.join(output_dir, FILE), os.F_OK):
            #-- check last modification time of input and output files
            input_mtime = os.stat(os.path.join(grace_dir, i)).st_mtime
            output_mtime = os.stat(os.path.join(output_dir, FILE)).st_mtime
            #-- if input tar file is newer: overwrite the output file
            if (input_mtime > output_mtime):
                TEST = True
                OVERWRITE = ' (overwrite)'
        else:
            TEST = True
            OVERWRITE = ' (new)'
        #-- As there are so many files.. this will only read the new files
        #-- or will rewrite if CLOBBER is set (if wanting something changed)
        if TEST or CLOBBER:
            #-- if verbose: output information about the geocenter file
            logging.info('{0}{1}'.format(os.path.join(output_dir, FILE),
                                         OVERWRITE))
            #-- open output monthly geocenter file
            f = open(os.path.join(output_dir, FILE), 'w')
            args = ('Geocenter time series', DREL, DSET)
            print('# {0} from {1} AOD1b {2} Product'.format(*args), file=f)
            print('# {0}'.format(product[DSET]), file=f)
            args = ('ISO-Time', 'X', 'Y', 'Z')
            print('# {0:^15}    {1:^12} {2:^12} {3:^12}'.format(*args), file=f)

            #-- open the AOD1B monthly tar file
            tar = tarfile.open(name=os.path.join(grace_dir, i), mode='r:gz')

            #-- Iterate over every member within the tar file
            for member in tar.getmembers():
                #-- get calendar day from file
                DD, SFX = fx.findall(member.name).pop()
                DD = np.int64(DD)
                #-- open data file for day
                if (SFX == '.gz'):
                    fid = gzip.GzipFile(fileobj=tar.extractfile(member))
                else:
                    fid = tar.extractfile(member)
                #-- degree 1 spherical harmonics for day and hours
                DEG1 = geocenter()
                DEG1.C10 = np.zeros((n_time))
                DEG1.C11 = np.zeros((n_time))
                DEG1.S11 = np.zeros((n_time))
                hours = np.zeros((n_time), dtype=np.int64)

                #-- create counter for hour in dataset
                c = 0
                #-- while loop ends when dataset is read
                while (c < n_time):
                    #-- read line
                    file_contents = fid.readline().decode('ISO-8859-1')
                    #-- find file header for data product
                    if bool(hx.search(file_contents)):
                        #-- extract hour from header and convert to float
                        HH, = re.findall(r'(\d+):\d+:\d+', file_contents)
                        hours[c] = np.int64(HH)
                        #-- read each line of spherical harmonics
                        for k in range(0, n_harm):
                            file_contents = fid.readline().decode('ISO-8859-1')
                            #-- find numerical instances in the data line
                            line_contents = rx.findall(file_contents)
                            #-- spherical harmonic degree and order
                            l1 = np.int64(line_contents[0])
                            m1 = np.int64(line_contents[1])
                            if (l1 == 1) and (m1 == 0):
                                DEG1.C10[c] = np.float64(line_contents[2])
                            elif (l1 == 1) and (m1 == 1):
                                DEG1.C11[c] = np.float64(line_contents[2])
                                DEG1.S11[c] = np.float64(line_contents[3])
                        #-- add 1 to hour counter
                        c += 1
                #-- close the input file for day
                fid.close()
                #-- convert from spherical harmonics into geocenter
                DEG1.to_cartesian()
                #-- write to file for each hour (iterates each 6-hour block)
                for h, X, Y, Z in zip(hours, DEG1.X, DEG1.Y, DEG1.Z):
                    print(fstr.format(YY, MM, DD, h, X, Y, Z), file=f)

            #-- close the tar file
            tar.close()
            #-- close the output file
            f.close()
            #-- set the permissions mode of the output file
            os.chmod(os.path.join(output_dir, FILE), MODE)