#!/usr/bin/env python u""" MPI_ICESat2_ATL03.py (06/2020) Read ICESat-2 ATL03 and ATL09 data files to calculate average segment surfaces ATL03 datasets: Global Geolocated Photons ATL09 datasets: Atmospheric Characteristics CALLING SEQUENCE: mpiexec -np 6 python MPI_ICESat2_ATL03.py ATL03_file ATL09_file COMMAND LINE OPTIONS: -O X, --output=X: Name and path of output file -V, --verbose: Verbose output to track progress -M X, --mode=X: Permission mode of files created REQUIRES MPI PROGRAM MPI: standardized and portable message-passing system https://www.open-mpi.org/ http://mpitutorial.com/ PYTHON DEPENDENCIES: numpy: Scientific Computing Tools For Python https://numpy.org https://numpy.org/doc/stable/user/numpy-for-matlab-users.html scipy: Scientific Tools for Python https://docs.scipy.org/doc/ mpi4py: MPI for Python http://pythonhosted.org/mpi4py/ http://mpi4py.readthedocs.org/en/stable/ h5py: Python interface for Hierarchal Data Format 5 (HDF5) https://h5py.org http://docs.h5py.org/en/stable/mpi.html scikit-learn: Machine Learning in Python http://scikit-learn.org/stable/index.html https://github.com/scikit-learn/scikit-learn PROGRAM DEPENDENCIES: convert_julian.py: returns the calendar date and time given a Julian date count_leap_seconds: determines the number of leap seconds for a GPS time UPDATE HISTORY: Updated 06/2020: verify that complementary beam pair is in list of beams set masks of output arrays after reading from HDF5 add additional beam check within heights groups Updated 10/2019: changing Y/N flags to True/False Updated 09/2019: adding segment quality summary variable Updated 04/2019: updated backup algorithm for when the surface fit fails estimate both mean and median first photon bias corrections estimate both mean and median transmit pulse shape corrections Updated 03/2019: extract a set of ATL09 parameters for each ATL03 segment_ID Updated 02/2019: procedures following ATBD for first ATL03 release Written 05/2017 """ from __future__ import print_function, division import sys import os import re import h5py import getopt import datetime import operator import itertools import numpy as np import scipy.stats import scipy.signal import scipy.interpolate import sklearn.neighbors from mpi4py import MPI from icesat2_toolkit.convert_julian import convert_julian from icesat2_toolkit.count_leap_seconds import count_leap_seconds #-- PURPOSE: keep track of MPI threads def info(rank, size): print('Rank {0:d} of {1:d}'.format(rank+1,size)) print('module name: {0}'.format(__name__)) if hasattr(os, 'getppid'): print('parent process: {0:d}'.format(os.getppid())) print('process id: {0:d}'.format(os.getpid())) #-- PURPOSE: calculate the interquartile range (Pritchard et al, 2009) and #-- robust dispersion estimator (Smith et al, 2017) of the model residuals def filter_elevation(r0): #-- calculate percentiles for IQR and RDE #-- IQR: first and third quartiles (25th and 75th percentiles) #-- RDE: 16th and 84th percentiles #-- median: 50th percentile Q1,Q3,P16,P84,MEDIAN = np.percentile(r0,[25,75,16,84,50]) #-- calculate interquartile range IQR = Q3 - Q1 #-- calculate robust dispersion estimator (RDE) RDE = P84 - P16 #-- IQR pass: residual-(median value) is within 75% of IQR #-- RDE pass: residual-(median value) is within 50% of P84-P16 return (0.75*IQR,0.5*RDE,MEDIAN) #-- PURPOSE: try fitting a surface to the signal photons with progressively #-- less confidence if no valid surface is found def try_surface_fit(x, y, z, confidence_mask, dist_along, SURF_TYPE='linear', ITERATE=25, CONFIDENCE=[4,3,2,1,0]): #-- try with progressively less confidence for i,conf in enumerate(CONFIDENCE): ind, = np.nonzero(confidence_mask >= conf) centroid = dict(x=dist_along, y=np.mean(y[ind])) try: surf = reduce_surface_fit(x[ind], y[ind], z[ind], centroid, ind, SURF_TYPE=SURF_TYPE, ITERATE=ITERATE) except (ValueError, np.linalg.linalg.LinAlgError): pass else: return (i+1,surf,centroid) #-- if still no values found: return infinite values #-- will need to attempt a backup algorithm surf = dict(error=np.full(1,np.inf)) centroid = None return (None,surf,centroid) #-- PURPOSE: iteratively fit a polynomial surface to the elevation data to #-- reduce to within a valid window def reduce_surface_fit(x, y, z, centroid, ind, SURF_TYPE='linear', ITERATE=25): #-- calculate x and y relative to centroid point rel_x = x - centroid['x'] #-- Constant Term Z0 = np.ones_like((z)) if (SURF_TYPE == 'linear'):#-- linear fit SURFMAT = np.transpose([Z0,rel_x]) elif (SURF_TYPE == 'quadratic'):#-- quadratic fit SURFMAT = np.transpose([Z0,rel_x,rel_x**2]) #-- number of points for fit and number of terms in fit n_max,n_terms = np.shape(SURFMAT) #-- run only if number of points is above number of terms FLAG1 = ((n_max - n_terms) > 10) #-- maximum allowable window size H_win_max = 20.0 #-- minimum allowable window size H_win_min = 3.0 #-- set initial window to the full z range window = z.max() - z.min() window_p1 = np.copy(window) #-- initial indices for reducing to window filt = np.arange(n_max) filt_p1 = np.copy(filt) filt_p2 = np.copy(filt_p1) if FLAG1: #-- save initial indices for fitting all photons for confidence level indices = ind.copy() #-- run fit program for polynomial type fit = fit_surface(x, y, z, centroid, SURF_TYPE=SURF_TYPE) #-- number of iterations performed n_iter = 1 #-- save beta coefficients beta_mat = np.copy(fit['beta']) error_mat = np.copy(fit['error']) #-- residuals of model fit resid = z - np.dot(SURFMAT,beta_mat) #-- standard deviation of the residuals resid_std = np.std(resid) #-- save MSE and DOF for error analysis MSE = np.copy(fit['MSE']) DOF = np.copy(fit['DOF']) #-- Root mean square error RMSE = np.sqrt(fit['MSE']) #-- Normalized root mean square error NRMSE = RMSE/(np.max(z)-np.min(z)) #-- IQR pass: residual-(median value) is within 75% of IQR #-- RDE pass: residual-(median value) is within 50% of P84-P16 IQR,RDE,MEDIAN = filter_elevation(resid) #-- checking if any residuals are outside of the window window = np.max([H_win_min,6.0*RDE,0.5*window_p1]) filt, = np.nonzero(np.abs(resid-MEDIAN) <= (window/2.0)) #-- save iteration of window window_p1 = np.copy(window) #-- run only if number of points is above number of terms n_rem = np.count_nonzero(np.abs(resid-MEDIAN) <= (window/2.0)) FLAG1 = ((n_rem - n_terms) > 10) #-- maximum number of iterations to prevent infinite loops FLAG2 = (n_iter <= ITERATE) #-- compare indices over two iterations to prevent false stoppages FLAG3 = (set(filt) != set(filt_p1)) | (set(filt_p1) != set(filt_p2)) #-- iterate until there are no additional removed photons while FLAG1 & FLAG2 & FLAG3: #-- fit selected photons for window x_filt,y_filt,z_filt,indices = (x[filt],y[filt],z[filt],ind[filt]) #-- run fit program for polynomial type fit = fit_surface(x_filt,y_filt,z_filt,centroid,SURF_TYPE=SURF_TYPE) #-- add to number of iterations performed n_iter += 1 #-- save model coefficients beta_mat = np.copy(fit['beta']) error_mat = np.copy(fit['error']) #-- save MSE and DOF for error analysis MSE = np.copy(fit['MSE']) DOF = np.copy(fit['DOF']) #-- Root mean square error RMSE = np.sqrt(fit['MSE']) #-- Normalized root mean square error NRMSE = RMSE/(np.max(z_filt)-np.min(z_filt)) #-- save number of points n_max = len(z_filt) #-- residuals of model fit resid = z - np.dot(SURFMAT,beta_mat) #-- standard deviation of the residuals resid_std = np.std(resid) #-- IQR pass: residual-(median value) is within 75% of IQR #-- RDE pass: residual-(median value) is within 50% of P84-P16 IQR,RDE,MEDIAN = filter_elevation(resid) #-- checking if any residuals are outside of the window window = np.max([H_win_min,6.0*RDE,0.5*window_p1]) #-- filter out using median statistics and refit filt_p2 = np.copy(filt_p1) filt_p1 = np.copy(filt) filt, = np.nonzero(np.abs(resid-MEDIAN) <= (window/2.0)) #-- save iteration of window window_p1 = np.copy(window) #-- run only if number of points is above number of terms n_rem = np.count_nonzero(np.abs(resid-MEDIAN) <= (window/2.0)) FLAG1 = ((n_rem - n_terms) > 10) #-- maximum number of iterations to prevent infinite loops FLAG2 = (n_iter <= ITERATE) #-- compare indices over two iterations to prevent false stoppages FLAG3 = (set(filt) != set(filt_p1)) | (set(filt_p1) != set(filt_p2)) #-- return reduced model fit FLAG3 = (set(filt) == set(filt_p1)) if FLAG1 & FLAG3 & (window <= H_win_max): return {'beta':beta_mat, 'error':error_mat, 'MSE':MSE, 'NRMSE':NRMSE, 'DOF':DOF, 'count':n_max, 'indices':indices, 'iterations':n_iter, 'window':window, 'RDE':RDE} else: raise ValueError('No valid data points found') #-- PURPOSE: fit a polynomial surface to the elevation data def fit_surface(x, y, z, centroid, SURF_TYPE='linear'): #-- calculate x and y relative to centroid point rel_x = x - centroid['x'] #-- Constant Term Z0 = np.ones_like((z)) #-- Surface design matrix if (SURF_TYPE == 'linear'):#-- linear fit SURFMAT = np.transpose([Z0,rel_x]) elif (SURF_TYPE == 'quadratic'):#-- quadratic fit SURFMAT = np.transpose([Z0,rel_x,rel_x**2]) #-- number of points for fit and number of terms in fit n_max,n_terms = np.shape(SURFMAT) #-- Standard Least-Squares fitting (the [0] denotes coefficients output) beta_mat = np.linalg.lstsq(SURFMAT,z,rcond=-1)[0] #-- modelled surface elevation model = np.dot(SURFMAT,beta_mat) #-- residual of fit res = z - model #-- nu = Degrees of Freedom = number of measurements-number of parameters nu = n_max - n_terms #-- Mean square error #-- MSE = (1/nu)*sum((Y-X*B)**2) MSE = np.dot(np.transpose(z - model),(z - model))/nu #-- elevation surface error analysis Hinv = np.linalg.inv(np.dot(np.transpose(SURFMAT),SURFMAT)) #-- Taking the diagonal components of the cov matrix hdiag = np.diag(Hinv) #-- Default is 95% confidence interval alpha = 1.0 - (0.95) #-- Student T-Distribution with D.O.F. nu #-- t.ppf parallels tinv in matlab tstar = scipy.stats.t.ppf(1.0-(alpha/2.0),nu) #-- beta_err = t(nu,1-alpha/2)*standard error std_error = np.sqrt(MSE*hdiag) model_error = np.dot(SURFMAT,tstar*std_error) return {'beta':beta_mat, 'error':tstar*std_error, 'model':model, 'model_error': model_error, 'residuals':res, 'MSE':MSE, 'DOF':nu} #-- PURPOSE: calculate delta_time, latitude and longitude of the segment center def fit_geolocation(var, distance_along_X, X_atc): #-- calculate x relative to centroid point rel_x = distance_along_X - X_atc #-- design matrix XMAT = np.transpose([np.ones_like((distance_along_X)),rel_x]) #-- Standard Least-Squares fitting (the [0] denotes coefficients output) beta_mat = np.linalg.lstsq(XMAT,var,rcond=-1)[0] #-- return the fitted geolocation return beta_mat[0] #-- PURPOSE: estimate mean and median first photon bias corrections def calc_first_photon_bias(temporal_residuals,n_pulses,n_pixels,dead_time,dt, METHOD='direct',ITERATE=20): #-- create a histogram of the temporal residuals t_full = np.arange(temporal_residuals.min(),temporal_residuals.max()+dt,dt) nt = len(t_full) #-- number of input photon events cnt = len(temporal_residuals) #-- using kernel density functions from scikit-learn neighbors #-- gaussian kernels will reflect more accurate distributions of the data #-- with less sensitivity to sampling width than histograms (tophat kernels) kde = sklearn.neighbors.KernelDensity(bandwidth=dt,kernel='gaussian') kde.fit(temporal_residuals[:,None]) #-- kde score_samples outputs are normalized log density functions hist = np.exp(kde.score_samples(t_full[:,None]) + np.log(cnt*dt)) N0_full = hist/(n_pulses*n_pixels) #-- centroid of initial histogram hist_centroid = np.sum(t_full*hist)/np.sum(hist) #-- cumulative probability distribution function of initial histogram hist_cpdf = np.cumsum(hist/np.sum(hist)) #-- linearly interpolate to 10th, 50th, and 90th percentiles H10,hist_median,H90 = np.interp([0.1,0.5,0.9],hist_cpdf,t_full) #-- calculate moving total of normalized histogram #-- average number of pixels in the detector that were inactive P_dead = np.zeros((nt)) #-- dead time as a function of the number of bins n_dead = np.int(dead_time/dt) #-- calculate moving total of last n_dead bins kernel = np.triu(np.tri(nt,nt,0),k=-n_dead) P_dead[:] = np.dot(kernel,N0_full[:,None]).flatten() #-- estimate gain directly if (METHOD == 'direct'): #-- estimate gain G_est_full = 1.0 - P_dead #-- parameters for calculating first photon bias from calibration products width = np.abs(H90 - H10) strength = np.sum(N0_full) #-- calculate corrected histogram of photon events N_PEcorr = (n_pulses*n_pixels)*N0_full/G_est_full N_PE = np.sum(N_PEcorr) N_sigma = np.sqrt(n_pulses*n_pixels*N0_full)/G_est_full #-- calculate mean corrected estimate FPB_mean_corr = np.sum(t_full*N_PEcorr)/N_PE FPB_mean_sigma = np.sqrt(np.sum((N_sigma*(t_full-FPB_mean_corr)/N_PE)**2)) #-- calculate median corrected estimate PEcorr_cpdf = np.cumsum(N_PEcorr/N_PE) sigma_cpdf = np.sqrt(np.cumsum(N_sigma**2))/N_PE #-- calculate median first photon bias correction #-- linearly interpolate to 40th, 50th and 60th percentiles PE40,FPB_median_corr,PE60 = np.interp([0.4,0.5,0.6],PEcorr_cpdf,t_full) FPB_median_sigma = (PE60-PE40)*np.interp(0.5,PEcorr_cpdf,sigma_cpdf)/0.2 elif (METHOD == 'logarithmic') and np.count_nonzero(P_dead > 0.01): #-- find indices above threshold for computing correction ii, = np.nonzero(P_dead > 0.01) #-- complete gain over entire histogram G_est_full = np.ones((nt)) #-- segment indices (above threshold and +/- dead time) imin,imax = (np.min(ii)-n_dead,np.max(ii)+n_dead) #-- truncate values to range of segment N0 = N0_full[imin:imax+1] N_corr = np.copy(N0) nr = len(N0) #-- calculate gain for segment gain = np.ones((nr)) gain_prev = np.zeros((nr)) kernel = np.triu(np.tri(nr,nr,0),k=-n_dead) #-- counter for number of iterations for segment n_iter = 0 #-- iterate until convergence or until reaching limit of iterations #-- using matrix algebra to avoid using a nested loop while np.any(np.abs(gain-gain_prev) > 0.001) & (n_iter <= ITERATE): gain_prev=np.copy(gain) gain=np.exp(np.dot(kernel,np.log(1.0-N_corr[:,None]))).flatten() N_corr=N0/gain n_iter += 1 #-- add segment to complete gain array G_est_full[imin:imax+1] = gain[:] #-- calculate corrected histogram of photon events N_PEcorr = (n_pulses*n_pixels)*N0_full/G_est_full N_PE = np.sum(N_PEcorr) N_sigma = np.sqrt(n_pulses*n_pixels*N0_full)/G_est_full #-- calculate mean corrected estimate FPB_mean_corr = np.sum(t_full*N_PEcorr)/N_PE FPB_mean_sigma = np.sqrt(np.sum((N_sigma*(t_full-FPB_mean_corr)/N_PE)**2)) #-- calculate median corrected estimate PEcorr_cpdf = np.cumsum(N_PEcorr/N_PE) sigma_cpdf = np.sqrt(np.cumsum(N_sigma**2))/N_PE #-- calculate median first photon bias correction #-- linearly interpolate to 40th, 50th and 60th percentiles PE40,FPB_median_corr,PE60 = np.interp([0.4,0.5,0.6],PEcorr_cpdf,t_full) FPB_median_sigma = (PE60-PE40)*np.interp(0.5,PEcorr_cpdf,sigma_cpdf)/0.2 else: #-- possible that no first photon bias correction is necessary FPB_mean_corr = 0.0 FPB_mean_sigma = 0.0 FPB_median_corr = 0.0 FPB_median_sigma = 0.0 N_PE = np.sum(hist) #-- return first photon bias corrections return {'mean':FPB_mean_corr, 'mean_sigma':np.abs(FPB_mean_sigma), 'median':FPB_median_corr, 'median_sigma':np.abs(FPB_median_sigma), 'width':width, 'strength':strength, 'count':N_PE} #-- PURPOSE: compress complete list of valid indices into a set of ranges def compress_list(i,n): for a,b in itertools.groupby(enumerate(i), lambda v: ((v[1]-v[0])//n)*n): group = list(map(operator.itemgetter(1),b)) yield (group[0], group[-1]) #-- PURPOSE: centers the transmit-echo-path histogram reported by ATL03 #-- using an iterative edit to distinguish between signal and noise def extract_tep_histogram(tep_hist_time,tep_hist,tep_range_prim): #-- ATL03 recommends subset between 15-30 ns to avoid secondary #-- using primary histogram range values from ATL03 tep attributes i, = np.nonzero((tep_hist_time >= tep_range_prim[0]) & (tep_hist_time < tep_range_prim[1])) t_tx = np.copy(tep_hist_time[i]) n_tx = len(t_tx) #-- noise samples of tep_hist (first 5ns and last 10 ns) ns,ne = (tep_range_prim[0]+5e-9,tep_range_prim[1]-10e-9) noise, = np.nonzero((t_tx <= ns) | (t_tx >= ne)) noise_p1 = [] #-- signal samples of tep_hist signal = sorted(set(np.arange(n_tx)) - set(noise)) #-- number of iterations n_iter = 0 while (set(noise) != set(noise_p1)) & (n_iter < 10): #-- value of noise in tep histogram tep_noise_value = np.sqrt(np.sum(tep_hist[i][noise]**2)/n_tx) p_tx = np.abs(np.copy(tep_hist[i]) - tep_noise_value) #-- calculate centroid of tep_hist t0_tx = np.sum(t_tx[signal]*p_tx[signal])/np.sum(p_tx[signal]) #-- calculate cumulative distribution function TX_cpdf = np.cumsum(p_tx[signal]/np.sum(p_tx[signal])) #-- linearly interpolate to 16th and 84th percentile for RDE TX16,TX84 = np.interp([0.16,0.84],TX_cpdf,t_tx[signal]-t0_tx) #-- calculate width of transmitted pulse (RDE) W_TX = 0.5*(TX84 - TX16) #-- recalculate noise noise_p1 = np.copy(noise) ns,ne = (t0_tx-6.0*W_TX,t0_tx+6.0*W_TX) noise, = np.nonzero((t_tx <= ns) | (t_tx >= ne)) signal = sorted(set(np.arange(n_tx)) - set(noise)) #-- add 1 to counter n_iter += 1 #-- valid primary TEP return has full-width at half max < 3 ns mx = np.argmax(p_tx[signal]) halfmax = np.max(p_tx[signal])/2.0 H1 = np.interp(halfmax,p_tx[signal][:mx],t_tx[signal][:mx]) H2 = np.interp(halfmax,p_tx[signal][:mx:-1],t_tx[signal][:mx:-1]) FWHM = H2 - H1 #-- return values return (t_tx[signal]-t0_tx,p_tx[signal],W_TX,FWHM,ns,ne) #-- PURPOSE: Estimate transmit-pulse-shape correction def calc_transmit_pulse_shape(t_TX,p_TX,W_TX,W_RX,dt_W,SNR,ITERATE=50): #-- length of the transmit pulse nt = len(p_TX) #-- average time step of the transmit pulse dt = np.abs(t_TX[1]-t_TX[0]) #-- calculate broadening of the received pulse W_spread = np.sqrt(np.max([W_RX**2 - W_TX**2,1e-22])) #-- create zero padded transmit and received pulses (by 4*W_spread samples) dw = np.ceil(W_spread/dt) wmn = -np.int(np.min([0,np.round((-t_TX[0])/dt)-4*dw])) wmx = np.int(np.max([nt,np.round((-t_TX[0])/dt)+4*dw])-nt) t_RX = np.arange(t_TX[0]-wmn*dt,t_TX[-1]+(wmx+1)*dt,dt) nr = len(t_RX) TX = np.zeros((nr)) TX[wmn:wmn+nt] = np.copy(p_TX) #-- smooth the transmit pulse by the spread gw = scipy.signal.gaussian(nr, W_spread/dt) RX = scipy.signal.convolve(TX/TX.sum(), gw/gw.sum(), mode='same') #-- normalize and add a random noise estimate RX /= np.sum(RX) RX += (1.0-2.0*np.random.rand(nr))*(dt/dt_W)/SNR #-- verify that all values of the synthetic received pulse are positive RX = np.abs(RX) #-- calculate median estimate of the synthetic received pulse RX_cpdf = np.cumsum(RX/np.sum(RX)) #-- linearly interpolate to 50th percentile to calculate median t_synthetic_med = np.interp(0.5,RX_cpdf,t_RX) #-- calculate centroid for mean of the synthetic received pulse t_synthetic_mean = np.sum(t_RX*RX)/np.sum(RX) #-- number of iterations n_iter = 0 #-- threshold for stopping iteration threshold = 2e-4/299792458.0 #-- iterate until convergence of both mean and median FLAG1,FLAG2 = (False,False) while (FLAG1 | FLAG2) and (n_iter < ITERATE): #-- copy previous mean and median times tmd_prev = np.copy(t_synthetic_med) tmn_prev = np.copy(t_synthetic_mean) #-- truncate to within window i,=np.nonzero((t_RX >= (tmn-0.5*dt_W)) & (t_RX <= (tmn+0.5*dt_W))) #-- linearly interpolate to 50th percentile to calculate median t_synthetic_med = np.interp(0.5,np.cumsum(RX[i]/np.sum(RX[i])),t_RX[i]) #-- calculate mean time for window t_synthetic_mean = np.sum(t_RX[i]*RX[i])/np.sum(RX[i]) #-- add to iteration n_iter += 1 #-- check iteration FLAG1 = (np.abs(t_synthetic_med - tmd_prev) > threshold) FLAG2 = (np.abs(t_synthetic_mean - tmn_prev) > threshold) #-- return estimated transmit pulse corrections corrections return {'mean':t_synthetic_mean,'median':t_synthetic_med,'spread':W_spread} #-- PURPOSE: reads ICESat-2 ATL03 and ATL09 HDF5 files #-- and computes average heights over segments def main(): #-- start MPI communicator comm = MPI.COMM_WORLD #-- get input files and options from system arguments long_options = ['output=','verbose','mode='] optlist,arglist = getopt.getopt(sys.argv[1:],'O:VM:',long_options) #-- If no system arguments: exit with an error if not arglist: raise IOError('No input files entered as system arguments') #-- command line parameters #-- use default output file name output_file = None #-- verbose output of processing run VERBOSE = False #-- permissions mode of the output file (number in octal) MODE = 0o775 for opt, arg in optlist: if opt in ("-V","--verbose"): #-- output module information for process print(arglist[0]) if (comm.rank == 0) else None info(comm.rank,comm.size) VERBOSE = True elif opt in ("-O","--output"): #-- explicitly define output file output_file = os.path.expanduser(arg) elif opt in ("-M","--mode"): #-- set permission mode of output HDF5 datasets MODE = int(arg, 8) #-- list of input files for processing (tilde-expand paths) #-- first file listed contains the ATL03 file #-- second file listed is the associated ATL09 file ATL03_file = os.path.expanduser(arglist[0]) ATL09_file = os.path.expanduser(arglist[1]) #-- directory setup ATL03_dir = os.path.dirname(ATL03_file) #-- compile regular expression operator for extracting data from ATL03 files rx1 = re.compile(r'(processed)?(ATL\d+)_(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})' r'(\d{2})_(\d{4})(\d{2})(\d{2})_(\d{3})_(\d{2})(.*?).h5$') #-- universal variables #-- speed of light c = 299792458.0 #-- associated beam pairs associated_beam_pair = dict(gt1l='gt1r',gt1r='gt1l',gt2l='gt2r',gt2r='gt2l', gt3l='gt3r',gt3r='gt3l') #-- read ICESat-2 ATL03 HDF5 files (extract base parameters) SUB,PRD,YY,MM,DD,HH,MN,SS,TRK,CYCL,GRAN,RL,VERS,AUX=rx1.findall(ATL03_file).pop() #-- Open the HDF5 file for reading fileID = h5py.File(ATL03_file, 'r', driver='mpio', comm=comm) #-- read each input beam within the file IS2_atl03_beams = [] for gtx in [k for k in fileID.keys() if bool(re.match(r'gt\d[lr]',k))]: #-- check if subsetted beam contains data #-- check in both the geolocation and heights groups try: fileID[gtx]['geolocation']['segment_id'] fileID[gtx]['heights']['delta_time'] except KeyError: pass else: IS2_atl03_beams.append(gtx) #-- number of GPS seconds between the GPS epoch #-- and ATLAS Standard Data Product (SDP) epoch atlas_sdp_gps_epoch = fileID['ancillary_data']['atlas_sdp_gps_epoch'][:] #-- which TEP to use for a given spot (convert to 0-based index) tep_valid_spot = fileID['ancillary_data']['tep']['tep_valid_spot'][:] - 1 tep_pce = ['pce1_spot1','pce2_spot3'] #-- valid range of times for each TEP histogram tep_range_prim = fileID['ancillary_data']['tep']['tep_range_prim'][:] #-- save tep parameters for a given beam tep = {} #-- variables of interest for generating corrected elevation estimates Segment_ID = {} Segment_Index_begin = {} Segment_PE_count = {} Segment_Distance = {} Segment_Length = {} Segment_Background = {} #-- fit parameters Segment_delta_time = {} Segment_Height = {} Segment_Land_Ice = {} Segment_dH_along = {} Segment_dH_across = {} Segment_Height_Error = {} Segment_Land_Ice_Error = {} Segment_dH_along_Error = {} Segment_dH_across_Error = {} Segment_Mean_Median = {} Segment_X_atc = {} Segment_X_spread = {} Segment_Y_atc = {} Segment_sigma_geo = {} Segment_Longitude = {} Segment_Latitude = {} Segment_N_Fit = {} Segment_Window = {} Segment_RDE = {} Segment_SNR = {} Segment_Summary = {} Segment_Iterations = {} Segment_Source = {} Segment_Pulses = {} #-- correction parameters FPB_mean_corr = {} FPB_mean_sigma = {} FPB_median_corr = {} FPB_median_sigma = {} mean_dead_time = {} FPB_n_corr = {} FPB_cal_corr = {} TPS_mean_corr = {} TPS_median_corr = {} #-- for each input beam within the file for gtx in sorted(IS2_atl03_beams): print(gtx) if VERBOSE and (comm.rank == 0) else None #-- beam type (weak versus strong) for time atlas_beam_type = fileID[gtx].attrs['atlas_beam_type'].decode('utf-8') n_pixels = 16.0 if (atlas_beam_type == "strong") else 4.0 #-- ATL03 Segment ID Segment_ID[gtx] = fileID[gtx]['geolocation']['segment_id'][:] #-- number of valid overlapping ATL03 segments n_seg = len(Segment_ID[gtx]) - 1 #-- first photon in the segment (convert to 0-based indexing) Segment_Index_begin[gtx] = fileID[gtx]['geolocation']['ph_index_beg'][:] - 1 #-- number of photon events in the segment Segment_PE_count[gtx] = fileID[gtx]['geolocation']['segment_ph_cnt'][:] #-- along-track distance for each ATL03 segment Segment_Distance[gtx] = fileID[gtx]['geolocation']['segment_dist_x'][:] #-- along-track length for each ATL03 segment Segment_Length[gtx] = fileID[gtx]['geolocation']['segment_length'][:] #-- ocean tide fv = fileID[gtx]['geophys_corr']['tide_ocean'].attrs['_FillValue'] tide_ocean = np.ma.array(fileID[gtx]['geophys_corr']['tide_ocean'][:], fill_value=fv) tide_ocean.mask = tide_ocean.data == tide_ocean.fill_value #-- interpolate background photon rate based on 50-shot summation background_delta_time = fileID[gtx]['bckgrd_atlas']['delta_time'][:] SPL = scipy.interpolate.UnivariateSpline(background_delta_time, fileID[gtx]['bckgrd_atlas']['bckgrd_rate'][:],k=3,s=0) Segment_Background[gtx] = SPL(fileID[gtx]['geolocation']['delta_time'][:]) #-- ATLAS spot number for beam in current orientation spot = np.int(fileID[gtx].attrs['atlas_spot_number']) #-- get ATLAS impulse response variables for the transmitter echo path (TEP) tep1,tep2 = ('atlas_impulse_response','tep_histogram') #-- get appropriate transmitter-echo-path histogram for spot associated_pce = tep_valid_spot[spot-1] pce = tep_pce[associated_pce] #-- delta time of TEP histogram tep_tod, = fileID[tep1][pce][tep2]['tep_tod'][:] #-- truncate tep to primary histogram (reflection 43-50 ns) #-- and extract signal tep from noise tep. calculate width of tep #-- ATL03 recommends subsetting between 15-30 ns to avoid secondary tep_hist_time = np.copy(fileID[tep1][pce][tep2]['tep_hist_time'][:]) tep_hist = np.copy(fileID[tep1][pce][tep2]['tep_hist'][:]) t_TX,p_TX,W_TX,FWHM,TXs,TXe = extract_tep_histogram(tep_hist_time, tep_hist, tep_range_prim) #-- save tep information and statistics tep[gtx] = {} tep[gtx]['pce'] = pce tep[gtx]['tep_tod'] = tep_tod tep[gtx]['tx_start'] = TXs tep[gtx]['tx_end'] = TXe tep[gtx]['tx_robust_sprd'] = W_TX tep[gtx]['sigma_tx'] = FWHM #-- channel dead time and first photon bias table for beam cal1,cal2 = ('ancillary_data','calibrations') channel_dead_time = fileID[cal1][cal2]['dead_time'][gtx]['dead_time'][:] mean_dead_time[gtx] = np.mean(channel_dead_time) fpb_dead_time = fileID[cal1][cal2]['first_photon_bias'][gtx]['dead_time'][:] fpb_strength = fileID[cal1][cal2]['first_photon_bias'][gtx]['strength'][:] fpb_width = fileID[cal1][cal2]['first_photon_bias'][gtx]['width'][:] fpb_corr = fileID[cal1][cal2]['first_photon_bias'][gtx]['ffb_corr'][:] #-- calculate first photon bias as a function of strength and width #-- for the calculated mean dead time of the beam ndt,ns,nw = np.shape(fpb_corr) fpb_corr_dead_time = np.zeros((ns,nw)) for s in range(ns): for w in range(nw): SPL = scipy.interpolate.UnivariateSpline(fpb_dead_time/1e9, fpb_corr[:,s,w],k=3,s=0) fpb_corr_dead_time[s,w] = SPL(mean_dead_time[gtx]) #-- bivariate spline for estimating first-photon bias using CAL-19 CAL19 = scipy.interpolate.RectBivariateSpline(fpb_strength[0,:], fpb_width[0,:]/1e9, fpb_corr_dead_time/1e12, kx=1, ky=1) #-- allocate for output segment fit data fill_value = fileID[gtx]['geolocation']['sigma_h'].attrs['_FillValue'] #-- delta time of fit photons Distributed_delta_time = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_delta_time.mask = np.ones((n_seg),dtype=np.bool) #-- segment fit heights Distributed_Height = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_Height.mask = np.ones((n_seg),dtype=np.bool) #-- land ice height corrected for first photon bias and transmit-pulse shape Distributed_Land_Ice = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_Land_Ice.mask = np.ones((n_seg),dtype=np.bool) #-- segment fit along-track slopes Distributed_dH_along = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_dH_along.mask = np.ones((n_seg),dtype=np.bool) #-- segment fit height errors Distributed_Height_Error = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_Height_Error.mask = np.ones((n_seg),dtype=np.bool) #-- land ice height errors (max of fit or first photon bias uncertainties) Distributed_Land_Ice_Error = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_Land_Ice_Error.mask = np.ones((n_seg),dtype=np.bool) #-- segment fit along-track slope errors Distributed_dH_along_Error = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_dH_along_Error.mask = np.ones((n_seg),dtype=np.bool) #-- difference between the mean and median of the residuals from fit height Distributed_Mean_Median = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_Mean_Median.mask = np.ones((n_seg),dtype=np.bool) #-- along-track X coordinates of segment fit Distributed_X_atc = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_X_atc.mask = np.ones((n_seg),dtype=np.bool) #-- along-track X coordinate spread of points used in segment fit Distributed_X_spread = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_X_spread.mask = np.ones((n_seg),dtype=np.bool) #-- along-track Y coordinates of segment fit Distributed_Y_atc = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_Y_atc.mask = np.ones((n_seg),dtype=np.bool) #-- longitude of fit photons Distributed_Longitude = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_Longitude.mask = np.ones((n_seg),dtype=np.bool) #-- latitude of fit photons Distributed_Latitude = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_Latitude.mask = np.ones((n_seg),dtype=np.bool) #-- number of photons in fit Distributed_N_Fit = np.ma.zeros((n_seg),fill_value=-1,dtype=np.int) Distributed_N_Fit.mask = np.ones((n_seg),dtype=np.bool) #-- size of the window used in the fit Distributed_Window = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_Window.mask = np.ones((n_seg),dtype=np.bool) #-- robust dispersion estimator Distributed_RDE = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_RDE.mask = np.ones((n_seg),dtype=np.bool) #-- signal-to-noise ratio Distributed_SNR = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_SNR.mask = np.ones((n_seg),dtype=np.bool) #-- segment quality summary Distributed_Summary = np.ma.zeros((n_seg),fill_value=-1,dtype=np.int) Distributed_Summary.mask = np.ones((n_seg),dtype=np.bool) #-- number of iterations for fit Distributed_Iterations = np.ma.zeros((n_seg),fill_value=-1,dtype=np.int) Distributed_Iterations.mask = np.ones((n_seg),dtype=np.bool) #-- signal source selection Distributed_Source = np.ma.zeros((n_seg),fill_value=4,dtype=np.int) Distributed_Source.mask = np.ones((n_seg),dtype=np.bool) #-- number of pulses in segment Distributed_Pulses = np.ma.zeros((n_seg),fill_value=-1,dtype=np.int) Distributed_Pulses.mask = np.ones((n_seg),dtype=np.bool) #-- first photon bias estimates Distributed_FPB_mean_corr = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_FPB_mean_corr.mask = np.ones((n_seg),dtype=np.bool) Distributed_FPB_mean_sigma = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_FPB_mean_sigma.mask = np.ones((n_seg),dtype=np.bool) Distributed_FPB_median_corr = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_FPB_median_corr.mask = np.ones((n_seg),dtype=np.bool) Distributed_FPB_median_sigma = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_FPB_median_sigma.mask = np.ones((n_seg),dtype=np.bool) Distributed_FPB_n_corr = np.ma.zeros((n_seg),fill_value=-1,dtype=np.int) Distributed_FPB_n_corr.mask = np.ones((n_seg),dtype=np.bool) Distributed_FPB_cal_corr = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_FPB_cal_corr.mask = np.ones((n_seg),dtype=np.bool) #-- transmit pulse shape bias estimates Distributed_TPS_mean_corr = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_TPS_mean_corr.mask = np.ones((n_seg),dtype=np.bool) Distributed_TPS_median_corr = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_TPS_median_corr.mask = np.ones((n_seg),dtype=np.bool) #-- iterate over valid ATL03 segments #-- in ATL03 1-based indexing: invalid == 0 #-- here in 0-based indexing: invalid == -1 segment_indices, = np.nonzero((Segment_Index_begin[gtx][:-1] >= 0) & (Segment_Index_begin[gtx][1:] >= 0)) iteration_count = len(segment_indices) #-- run for each geoseg (distributed over comm.size # of processes) for iteration in range(comm.rank, iteration_count, comm.size): #-- indice for iteration (can run through a subset of segments) j = segment_indices[iteration] #-- iterate over valid ATL03 segments #-- in ATL03 1-based indexing: invalid == 0 #-- here in 0-based indexing: invalid == -1 if (Segment_Index_begin[gtx][j] >= 0): #-- index for segment j idx = Segment_Index_begin[gtx][j] #-- number of photons in segment (use 2 ATL03 segments) c1 = np.int(Segment_PE_count[gtx][j]) c2 = np.int(Segment_PE_count[gtx][j+1]) cnt = c1 + c2 #-- time of each Photon event (PE) segment_times = np.copy(fileID[gtx]['heights']['delta_time'][idx:idx+cnt]) #-- Photon event lat/lon and elevation (re-tided WGS84) segment_heights = np.copy(fileID[gtx]['heights']['h_ph'][idx:idx+cnt]) segment_heights[:c1] += tide_ocean[j] segment_heights[c1:] += tide_ocean[j+1] segment_lats = np.copy(fileID[gtx]['heights']['lat_ph'][idx:idx+cnt]) segment_lons = np.copy(fileID[gtx]['heights']['lon_ph'][idx:idx+cnt]) #-- Photon event channel and identification ID_channel = np.copy(fileID[gtx]['heights']['ph_id_channel'][idx:idx+cnt]) ID_pulse = np.copy(fileID[gtx]['heights']['ph_id_pulse'][idx:idx+cnt]) n_pulses = np.unique(ID_pulse).__len__() frame_number = np.copy(fileID[gtx]['heights']['pce_mframe_cnt'][idx:idx+cnt]) #-- vertical noise-photon density background_density = 2.0*n_pulses*Segment_Background[gtx][j]/c #-- along-track X and Y coordinates distance_along_X = np.copy(fileID[gtx]['heights']['dist_ph_along'][idx:idx+cnt]) distance_along_X[:c1] += Segment_Distance[gtx][j] distance_along_X[c1:] += Segment_Distance[gtx][j+1] distance_along_Y = np.copy(fileID[gtx]['heights']['dist_ph_across'][idx:idx+cnt]) #-- check the spread of photons along-track (must be > 20m) along_X_spread = distance_along_X.max() - distance_along_X.min() #-- check confidence level associated with each photon event #-- -2: TEP #-- -1: Events not associated with a specific surface type #-- 0: noise #-- 1: buffer but algorithm classifies as background #-- 2: low #-- 3: medium #-- 4: high #-- Surface types for signal classification confidence #-- 0=Land; 1=Ocean; 2=SeaIce; 3=LandIce; 4=InlandWater ice_sig_conf = np.copy(fileID[gtx]['heights']['signal_conf_ph'][idx:idx+cnt,3]) ice_sig_low_count = np.count_nonzero(ice_sig_conf > 1) #-- check if segment has photon events classified for land ice #-- that are at or above low-confidence threshold #-- and that the spread of photons is greater than 20m if (ice_sig_low_count > 10) & (along_X_spread > 20): #-- perform a surface fit procedure Segment_X = Segment_Distance[gtx][j] + Segment_Length[gtx][j] valid,fit,centroid = try_surface_fit(distance_along_X, distance_along_Y, segment_heights, ice_sig_conf, Segment_X, SURF_TYPE='linear', ITERATE=20, CONFIDENCE=[1,0]) #-- indices of points used in final iterated fit ifit = fit['indices'] if valid else None if bool(valid) & (np.abs(fit['error'][0]) < 20): Distributed_Height.data[j] = fit['beta'][0] Distributed_Height.mask[j] = False Distributed_dH_along.data[j] = fit['beta'][1] Distributed_dH_along.mask[j] = False Distributed_Height_Error.data[j] = fit['error'][0] Distributed_Height_Error.mask[j] = False Distributed_dH_along_Error.data[j] = fit['error'][1] Distributed_dH_along_Error.mask[j] = False #-- along-track and cross-track coordinates Distributed_X_atc.data[j] = np.copy(centroid['x']) Distributed_X_atc.mask[j] = False Distributed_X_spread.data[j] = np.copy(along_X_spread) Distributed_X_spread.mask[j] = False Distributed_Y_atc.data[j] = np.copy(centroid['y']) Distributed_Y_atc.mask[j] = False #-- fit geolocation to the along-track distance of segment Distributed_delta_time.data[j] = fit_geolocation(segment_times[ifit], distance_along_X[ifit], Distributed_X_atc[j]) Distributed_delta_time.mask[j] = False Distributed_Longitude.data[j] = fit_geolocation(segment_lons[ifit], distance_along_X[ifit], Distributed_X_atc[j]) Distributed_Longitude.mask[j] = False Distributed_Latitude.data[j] = fit_geolocation(segment_lats[ifit], distance_along_X[ifit], Distributed_X_atc[j]) Distributed_Latitude.mask[j] = False #-- number of photons used in fit Distributed_N_Fit.data[j] = len(ifit) Distributed_N_Fit.mask[j] = False #-- size of the final window Distributed_Window.data[j] = np.copy(fit['window']) Distributed_Window.mask[j] = False #-- robust dispersion estimator Distributed_RDE.data[j] = np.copy(fit['RDE']) Distributed_RDE.mask[j] = False #-- signal to noise ratio N_BG = background_density*Distributed_Window.data[j] Distributed_SNR.data[j] = Distributed_N_Fit.data[j]/N_BG Distributed_SNR.mask[j] = False #-- number of iterations used in fit Distributed_Iterations.data[j] = np.copy(fit['iterations']) Distributed_Iterations.mask[j] = False Distributed_Source.data[j] = np.copy(valid) Distributed_Source.mask[j] = False Distributed_Pulses.data[j] = np.copy(n_pulses) Distributed_Pulses.mask[j] = False #-- calculate residuals off of fit surface for all data x_slope = Distributed_dH_along[j]*(distance_along_X-Distributed_X_atc[j]) height_residuals = segment_heights-Distributed_Height[j]-x_slope temporal_residuals = -2.0*height_residuals/c #-- calculate difference between the mean and the median from the fit Distributed_Mean_Median.data[j] = np.mean(height_residuals[ifit]) - \ np.median(height_residuals[ifit]) Distributed_Mean_Median.mask[j] = False #-- estimate first photon bias corrections #-- step-size for histograms (50 ps ~ 7.5mm height) ii, = np.nonzero((height_residuals >= -Distributed_Window.data[j]) & (height_residuals <= Distributed_Window.data[j])) FPB = calc_first_photon_bias(temporal_residuals[ii], n_pulses,n_pixels,mean_dead_time[gtx],5e-11,ITERATE=20) Distributed_FPB_mean_corr.data[j] = -0.5*FPB['mean']*c Distributed_FPB_mean_corr.mask[j] = False Distributed_FPB_mean_sigma.data[j] = 0.5*FPB['mean_sigma']*c Distributed_FPB_mean_sigma.mask[j] = False Distributed_FPB_median_corr.data[j] = -0.5*FPB['median']*c Distributed_FPB_median_corr.mask[j] = False Distributed_FPB_median_sigma.data[j] = 0.5*FPB['median_sigma']*c Distributed_FPB_median_sigma.mask[j] = False Distributed_FPB_n_corr.data[j] = np.copy(FPB['count']) Distributed_FPB_n_corr.mask[j] = False #-- first photon bias correction from CAL-19 FPB_calibrated = CAL19.ev(FPB['strength'],FPB['width']) Distributed_FPB_cal_corr.data[j] = -0.5*FPB_calibrated*c Distributed_FPB_cal_corr.mask[j] = False #-- estimate transmit pulse shape correction W_RX = 2.0*Distributed_RDE.data[j]/c dt_W = 2.0*Distributed_Window.data[j]/c TPS = calc_transmit_pulse_shape(t_TX, p_TX, W_TX, W_RX, dt_W, Distributed_SNR.data[j], ITERATE=50) Distributed_TPS_mean_corr.data[j] = 0.5*TPS['mean']*c Distributed_TPS_mean_corr.mask[j] = False Distributed_TPS_median_corr.data[j] = 0.5*TPS['median']*c Distributed_TPS_median_corr.mask[j] = False #-- calculate flags for quality summary VPD = Distributed_N_Fit.data[j]/Distributed_Window.data[j] Distributed_Summary.data[j] = np.int( (Distributed_RDE.data[j] >= 1) | (Distributed_Height_Error.data[j] >= 1) | (VPD <= (n_pixels/4.0))) Distributed_Summary.mask[j] = False #-- some ATL03 segments will not result in a valid fit #-- backup algorithm uses 4 segments to find a valid surface if (j not in (0,n_seg-2,n_seg-1)) & Distributed_Height.mask[j] & \ (Segment_Index_begin[gtx][j-1] > 0): #-- index for segment j idx = Segment_Index_begin[gtx][j-1] #-- number of photons in segment (use 4 ATL03 segments) c1 = Segment_PE_count[gtx][j-1].astype(np.int) c2 = Segment_PE_count[gtx][j].astype(np.int) c3 = Segment_PE_count[gtx][j+1].astype(np.int) c4 = Segment_PE_count[gtx][j+2].astype(np.int) cnt = c1 + c2 + c3 + c4 #-- time of each Photon event (PE) segment_times = np.copy(fileID[gtx]['heights']['delta_time'][idx:idx+cnt]) #-- Photon event lat/lon and elevation (re-tided WGS84) segment_heights = np.copy(fileID[gtx]['heights']['h_ph'][idx:idx+cnt]) segment_heights[:c1] += tide_ocean[j-1] segment_heights[c1:c1+c2] += tide_ocean[j] segment_heights[c1+c2:c1+c2+c3] += tide_ocean[j+1] segment_heights[c1+c2+c3:] += tide_ocean[j+2] segment_lats = np.copy(fileID[gtx]['heights']['lat_ph'][idx:idx+cnt]) segment_lons = np.copy(fileID[gtx]['heights']['lon_ph'][idx:idx+cnt]) #-- Photon event channel and identification ID_channel = np.copy(fileID[gtx]['heights']['ph_id_channel'][idx:idx+cnt]) ID_pulse = np.copy(fileID[gtx]['heights']['ph_id_pulse'][idx:idx+cnt]) n_pulses = np.unique(ID_pulse).__len__() frame_number = np.copy(fileID[gtx]['heights']['pce_mframe_cnt'][idx:idx+cnt]) #-- vertical noise-photon density background_density = 2.0*n_pulses*Segment_Background[gtx][j]/c #-- along-track X and Y coordinates distance_along_X = np.copy(fileID[gtx]['heights']['dist_ph_along'][idx:idx+cnt]) distance_along_X[:c1] += Segment_Distance[gtx][j-1] distance_along_X[c1:c1+c2] += Segment_Distance[gtx][j] distance_along_X[c1+c2:c1+c2+c3] += Segment_Distance[gtx][j+1] distance_along_X[c1+c2+c3:] += Segment_Distance[gtx][j+2] distance_along_Y = np.copy(fileID[gtx]['heights']['dist_ph_across'][idx:idx+cnt]) #-- check the spread of photons along-track (must be > 40m) along_X_spread = distance_along_X.max() - distance_along_X.min() #-- check confidence level associated with each photon event #-- -2: TEP #-- -1: Events not associated with a specific surface type #-- 0: noise #-- 1: buffer but algorithm classifies as background #-- 2: low #-- 3: medium #-- 4: high #-- Surface types for signal classification confidence #-- 0=Land; 1=Ocean; 2=SeaIce; 3=LandIce; 4=InlandWater ice_sig_conf = np.copy(fileID[gtx]['heights']['signal_conf_ph'][idx:idx+cnt,3]) ice_sig_low_count = np.count_nonzero(ice_sig_conf > 1) #-- check if segment has photon events classified for land ice #-- that are at or above low-confidence threshold #-- and that the spread of photons is greater than 40m if (ice_sig_low_count > 10) & (along_X_spread > 40): #-- perform a surface fit procedure Segment_X = Segment_Distance[gtx][j] + Segment_Length[gtx][j] valid,fit,centroid = try_surface_fit(distance_along_X, distance_along_Y, segment_heights, ice_sig_conf, Segment_X, SURF_TYPE='quadratic', ITERATE=20, CONFIDENCE=[0]) #-- indices of points used in final iterated fit ifit = fit['indices'] if valid else None if bool(valid) & (np.abs(fit['error'][0]) < 20): Distributed_Height.data[j] = fit['beta'][0] Distributed_Height.mask[j] = False Distributed_dH_along.data[j] = fit['beta'][1] Distributed_dH_along.mask[j] = False Distributed_Height_Error.data[j] = fit['error'][0] Distributed_Height_Error.mask[j] = False Distributed_dH_along_Error.data[j] = fit['error'][1] Distributed_dH_along_Error.mask[j] = False #-- along-track and cross-track coordinates Distributed_X_atc.data[j] = np.copy(centroid['x']) Distributed_X_atc.mask[j] = False Distributed_X_spread.data[j] = np.copy(along_X_spread) Distributed_X_spread.mask[j] = False Distributed_Y_atc.data[j] = np.copy(centroid['y']) Distributed_Y_atc.mask[j] = False #-- fit geolocation to the along-track distance of segment Distributed_delta_time.data[j] = fit_geolocation(segment_times[ifit], distance_along_X[ifit], Distributed_X_atc[j]) Distributed_Longitude.data[j] = fit_geolocation(segment_lons[ifit], distance_along_X[ifit], Distributed_X_atc[j]) Distributed_Longitude.mask[j] = False Distributed_Latitude.data[j] = fit_geolocation(segment_lats[ifit], distance_along_X[ifit], Distributed_X_atc[j]) #-- number of photons used in fit Distributed_N_Fit.data[j] = len(ifit) Distributed_N_Fit.mask[j] = False #-- size of the final window Distributed_Window.data[j] = np.copy(fit['window']) Distributed_Window.mask[j] = False #-- robust dispersion estimator Distributed_RDE.data[j] = np.copy(fit['RDE']) Distributed_RDE.mask[j] = False #-- signal to noise ratio N_BG = background_density*Distributed_Window.data[j] Distributed_SNR.data[j] = Distributed_N_Fit.data[j]/N_BG Distributed_SNR.mask[j] = False #-- number of iterations used in fit Distributed_Iterations.data[j] = np.copy(fit['iterations']) Distributed_Iterations.mask[j] = False Distributed_Source.data[j] = 2 + np.copy(valid) Distributed_Source.mask[j] = False Distributed_Pulses.data[j] = np.copy(n_pulses) Distributed_Pulses.mask[j] = False #-- calculate residuals off of fit surface for all data x_slope = Distributed_dH_along[j]*(distance_along_X-Distributed_X_atc[j]) height_residuals = segment_heights-Distributed_Height[j]-x_slope temporal_residuals = -2.0*height_residuals/c #-- calculate difference between the mean and the median from the fit Distributed_Mean_Median.data[j] = np.mean(height_residuals[ifit]) - \ np.median(height_residuals[ifit]) Distributed_Mean_Median.mask[j] = False #-- estimate first photon bias corrections #-- step-size for histograms (50 ps ~ 7.5mm height) ii, = np.nonzero((height_residuals >= -Distributed_Window.data[j]) & (height_residuals <= Distributed_Window.data[j])) FPB = calc_first_photon_bias(temporal_residuals[ii], n_pulses,n_pixels,mean_dead_time[gtx],5e-11,ITERATE=20) Distributed_FPB_mean_corr.data[j] = -0.5*FPB['mean']*c Distributed_FPB_mean_corr.mask[j] = False Distributed_FPB_mean_sigma.data[j] = 0.5*FPB['mean_sigma']*c Distributed_FPB_mean_sigma.mask[j] = False Distributed_FPB_median_corr.data[j] = -0.5*FPB['median']*c Distributed_FPB_median_corr.mask[j] = False Distributed_FPB_median_sigma.data[j] = 0.5*FPB['median_sigma']*c Distributed_FPB_median_sigma.mask[j] = False Distributed_FPB_n_corr.data[j] = np.copy(FPB['count']) Distributed_FPB_n_corr.mask[j] = False #-- first photon bias correction from CAL-19 FPB_calibrated = CAL19.ev(FPB['strength'],FPB['width']) Distributed_FPB_cal_corr.data[j] = -0.5*FPB_calibrated*c Distributed_FPB_cal_corr.mask[j] = False #-- estimate transmit pulse shape correction W_RX = 2.0*Distributed_RDE.data[j]/c dt_W = 2.0*Distributed_Window.data[j]/c TPS = calc_transmit_pulse_shape(t_TX, p_TX, W_TX, W_RX, dt_W, Distributed_SNR.data[j], ITERATE=50) Distributed_TPS_mean_corr.data[j] = 0.5*TPS['mean']*c Distributed_TPS_mean_corr.mask[j] = False Distributed_TPS_median_corr.data[j] = 0.5*TPS['median']*c Distributed_TPS_median_corr.mask[j] = False #-- calculate flags for quality summary VPD = Distributed_N_Fit.data[j]/Distributed_Window.data[j] Distributed_Summary.data[j] = np.int( (Distributed_RDE.data[j] >= 1) | (Distributed_Height_Error.data[j] >= 1) | (VPD <= (n_pixels/4.0))) Distributed_Summary.mask[j] = False #-- if there is a valid land ice height if (~Distributed_Height.mask[j]): #-- land ice height corrected for first photon bias and transmit-pulse shape #-- segment heights have already been "re-tided" Distributed_Land_Ice.data[j] = Distributed_Height.data[j] + \ Distributed_FPB_median_corr.data[j] + Distributed_TPS_median_corr.data[j] Distributed_Land_Ice.mask[j] = False #-- land ice height errors (max of fit or first photon bias uncertainties) Distributed_Land_Ice_Error.data[j] = np.sqrt(np.max([ Distributed_Height_Error.data[j]**2, Distributed_FPB_median_sigma.data[j]**2])) Distributed_Land_Ice_Error.mask[j] = False #-- communicate output MPI matrices between ranks #-- operations are element summations and logical "and" across elements #-- delta time of fit photons Segment_delta_time[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_delta_time[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_delta_time.data, MPI.DOUBLE], \ recvbuf=[Segment_delta_time[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_delta_time.mask, MPI.BOOL], \ recvbuf=[Segment_delta_time[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_delta_time = None #-- segment fit heights Segment_Height[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_Height[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_Height.data, MPI.DOUBLE], \ recvbuf=[Segment_Height[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_Height.mask, MPI.BOOL], \ recvbuf=[Segment_Height[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_Height = None #-- land ice height corrected for first photon bias and transmit-pulse shape Segment_Land_Ice[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_Land_Ice[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_Land_Ice.data, MPI.DOUBLE], \ recvbuf=[Segment_Land_Ice[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_Land_Ice.mask, MPI.BOOL], \ recvbuf=[Segment_Land_Ice[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_Land_Ice = None #-- segment fit along-track slopes Segment_dH_along[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_dH_along[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_dH_along.data, MPI.DOUBLE], \ recvbuf=[Segment_dH_along[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_dH_along.mask, MPI.BOOL], \ recvbuf=[Segment_dH_along[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_dH_along = None #-- segment fit height errors Segment_Height_Error[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_Height_Error[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_Height_Error.data, MPI.DOUBLE], \ recvbuf=[Segment_Height_Error[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_Height_Error.mask, MPI.BOOL], \ recvbuf=[Segment_Height_Error[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_Height_Error = None #-- land ice height errors (max of fit or first photon bias uncertainties) Segment_Land_Ice_Error[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_Land_Ice_Error[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_Land_Ice_Error.data, MPI.DOUBLE], \ recvbuf=[Segment_Land_Ice_Error[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_Land_Ice_Error.mask, MPI.BOOL], \ recvbuf=[Segment_Land_Ice_Error[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_Land_Ice_Error = None #-- segment fit along-track slope errors Segment_dH_along_Error[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_dH_along_Error[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_dH_along_Error.data, MPI.DOUBLE], \ recvbuf=[Segment_dH_along_Error[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_dH_along_Error.mask, MPI.BOOL], \ recvbuf=[Segment_dH_along_Error[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_dH_along_Error = None #-- difference between the mean and median of the residuals from fit height Segment_Mean_Median[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_Mean_Median[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_Mean_Median.data, MPI.DOUBLE], \ recvbuf=[Segment_Mean_Median[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_Mean_Median.mask, MPI.BOOL], \ recvbuf=[Segment_Mean_Median[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_Mean_Median = None #-- along-track X coordinates of segment fit Segment_X_atc[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_X_atc[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_X_atc.data, MPI.DOUBLE], \ recvbuf=[Segment_X_atc[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_X_atc.mask, MPI.BOOL], \ recvbuf=[Segment_X_atc[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_X_atc = None #-- along-track X coordinate spread of points used in segment fit Segment_X_spread[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_X_spread[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_X_spread.data, MPI.DOUBLE], \ recvbuf=[Segment_X_spread[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_X_spread.mask, MPI.BOOL], \ recvbuf=[Segment_X_spread[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_X_spread = None #-- along-track Y coordinates of segment fit Segment_Y_atc[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_Y_atc[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_Y_atc.data, MPI.DOUBLE], \ recvbuf=[Segment_Y_atc[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_Y_atc.mask, MPI.BOOL], \ recvbuf=[Segment_Y_atc[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_Y_atc = None #-- longitude of fit photons Segment_Longitude[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_Longitude[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_Longitude.data, MPI.DOUBLE], \ recvbuf=[Segment_Longitude[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_Longitude.mask, MPI.BOOL], \ recvbuf=[Segment_Longitude[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_Longitude = None #-- latitude of fit photons Segment_Latitude[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_Latitude[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_Latitude.data, MPI.DOUBLE], \ recvbuf=[Segment_Latitude[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_Latitude.mask, MPI.BOOL], \ recvbuf=[Segment_Latitude[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_Latitude = None #-- number of photons in fit Segment_N_Fit[gtx] = np.ma.zeros((n_seg),fill_value=-1,dtype=np.int) Segment_N_Fit[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_N_Fit.data, MPI.INT], \ recvbuf=[Segment_N_Fit[gtx].data, MPI.INT], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_N_Fit.mask, MPI.BOOL], \ recvbuf=[Segment_N_Fit[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_N_Fit = None #-- size of the window used in the fit Segment_Window[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_Window[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_Window.data, MPI.DOUBLE], \ recvbuf=[Segment_Window[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_Window.mask, MPI.BOOL], \ recvbuf=[Segment_Window[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_Window = None #-- robust dispersion estimator Segment_RDE[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_RDE[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_RDE.data, MPI.DOUBLE], \ recvbuf=[Segment_RDE[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_RDE.mask, MPI.BOOL], \ recvbuf=[Segment_RDE[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_RDE = None #-- signal-to-noise ratio Segment_SNR[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_SNR[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_SNR.data, MPI.DOUBLE], \ recvbuf=[Segment_SNR[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_SNR.mask, MPI.BOOL], \ recvbuf=[Segment_SNR[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_SNR = None #-- segment quality summary Segment_Summary[gtx] = np.ma.zeros((n_seg),fill_value=-1,dtype=np.int) Segment_Summary[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_Summary.data, MPI.INT], \ recvbuf=[Segment_Summary[gtx].data, MPI.INT], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_Summary.mask, MPI.BOOL], \ recvbuf=[Segment_Summary[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_Summary = None #-- number of iterations for fit Segment_Iterations[gtx] = np.ma.zeros((n_seg),fill_value=-1,dtype=np.int) Segment_Iterations[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_Iterations.data, MPI.INT], \ recvbuf=[Segment_Iterations[gtx].data, MPI.INT], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_Iterations.mask, MPI.BOOL], \ recvbuf=[Segment_Iterations[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_Iterations = None #-- signal source selection Segment_Source[gtx] = np.ma.zeros((n_seg),fill_value=4,dtype=np.int) Segment_Source[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_Source.data, MPI.INT], \ recvbuf=[Segment_Source[gtx].data, MPI.INT], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_Source.mask, MPI.BOOL], \ recvbuf=[Segment_Source[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_Source = None #-- number of pulses in segment Segment_Pulses[gtx] = np.ma.zeros((n_seg),fill_value=-1,dtype=np.int) Segment_Pulses[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_Pulses.data, MPI.INT], \ recvbuf=[Segment_Pulses[gtx].data, MPI.INT], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_Pulses.mask, MPI.BOOL], \ recvbuf=[Segment_Pulses[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_Pulses = None #-- first photon bias estimates FPB_mean_corr[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) FPB_mean_corr[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_FPB_mean_corr.data, MPI.DOUBLE], \ recvbuf=[FPB_mean_corr[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_FPB_mean_corr.mask, MPI.BOOL], \ recvbuf=[FPB_mean_corr[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_FPB_mean_corr = None FPB_mean_sigma[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) FPB_mean_sigma[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_FPB_mean_sigma.data, MPI.DOUBLE], \ recvbuf=[FPB_mean_sigma[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_FPB_mean_sigma.mask, MPI.BOOL], \ recvbuf=[FPB_mean_sigma[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_FPB_mean_sigma = None FPB_median_corr[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) FPB_median_corr[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_FPB_median_corr.data, MPI.DOUBLE], \ recvbuf=[FPB_median_corr[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_FPB_median_corr.mask, MPI.BOOL], \ recvbuf=[FPB_median_corr[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_FPB_median_corr = None FPB_median_sigma[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) FPB_median_sigma[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_FPB_median_sigma.data, MPI.DOUBLE], \ recvbuf=[FPB_median_sigma[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_FPB_median_sigma.mask, MPI.BOOL], \ recvbuf=[FPB_median_sigma[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_FPB_median_sigma = None FPB_n_corr[gtx] = np.ma.zeros((n_seg),fill_value=-1,dtype=np.int) FPB_n_corr[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_FPB_n_corr.data, MPI.INT], \ recvbuf=[FPB_n_corr[gtx].data, MPI.INT], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_FPB_n_corr.mask, MPI.BOOL], \ recvbuf=[FPB_n_corr[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_FPB_n_corr = None FPB_cal_corr[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) FPB_cal_corr[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_FPB_cal_corr.data, MPI.DOUBLE], \ recvbuf=[FPB_cal_corr[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_FPB_cal_corr.mask, MPI.BOOL], \ recvbuf=[FPB_cal_corr[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_FPB_cal_corr = None #-- transmit pulse shape bias estimates TPS_mean_corr[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) TPS_mean_corr[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_TPS_mean_corr.data, MPI.DOUBLE], \ recvbuf=[TPS_mean_corr[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_TPS_mean_corr.mask, MPI.BOOL], \ recvbuf=[TPS_mean_corr[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_TPS_mean_corr = None TPS_median_corr[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) TPS_median_corr[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_TPS_median_corr.data, MPI.DOUBLE], \ recvbuf=[TPS_median_corr[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_TPS_median_corr.mask, MPI.BOOL], \ recvbuf=[TPS_median_corr[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_TPS_median_corr = None #-- wait for all distributed processes to finish for beam comm.Barrier() #-- copy variables for outputting to HDF5 file IS2_atl03_fit = {} IS2_atl03_fill = {} IS2_atl03_attrs = {} #-- ICESat-2 spacecraft orientation at time IS2_atl03_fit['orbit_info'] = {} IS2_atl03_attrs['orbit_info'] = {} for key,val in fileID['orbit_info'].items(): IS2_atl03_fit['orbit_info'][key] = val[:] #-- Getting attributes of group and included variables #-- Global Group Attributes for att_name,att_val in fileID['orbit_info'].attrs.items(): IS2_atl03_attrs['orbit_info'][att_name] = att_val #-- Variable Attributes IS2_atl03_attrs['orbit_info'][key] = {} for att_name,att_val in val.attrs.items(): IS2_atl03_attrs['orbit_info'][key][att_name] = att_val #-- information ancillary to the data product #-- 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 #-- could alternatively use the Julian day of the ATLAS SDP epoch: 2458119.5 #-- and add leap seconds since 2018-01-01T00:00:00Z UTC (ATLAS SDP epoch) IS2_atl03_fit['ancillary_data'] = {} IS2_atl03_attrs['ancillary_data'] = {} for key in ['atlas_sdp_gps_epoch','data_end_utc','data_start_utc','end_cycle', 'end_geoseg','end_gpssow','end_gpsweek','end_orbit','end_region', 'end_rgt','granule_end_utc','granule_start_utc','release','start_cycle', 'start_geoseg','start_gpssow','start_gpsweek','start_orbit','start_region', 'start_rgt','version']: #-- get each HDF5 variable IS2_atl03_fit['ancillary_data'][key] = fileID['ancillary_data'][key][:] #-- Getting attributes of group and included variables IS2_atl03_attrs['ancillary_data'][key] = {} for att_name,att_val in fileID['ancillary_data'][key].attrs.items(): IS2_atl03_attrs['ancillary_data'][key][att_name] = att_val #-- for each output beam for gtx in sorted(IS2_atl03_beams): #-- atmospheric profile for beam gtx from ATL09 dataset pfl = fileID[gtx].attrs['atmosphere_profile'] #-- complementary beam in pair cmp = associated_beam_pair[gtx] #-- extract and interpolate atmospheric parameters from ATL09 IS2_atl09_mds,IS2_atl09_attrs = read_HDF5_ATL09(ATL09_file, pfl, Segment_ID[gtx], ATTRIBUTES=True, VERBOSE=VERBOSE, COMM=comm) #-- segment fit across-track slopes Distributed_dH_across = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_dH_across.mask = np.ones((n_seg),dtype=np.bool) #-- segment fit across-track slope errors Distributed_dH_across_Error = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_dH_across_Error.mask = np.ones((n_seg),dtype=np.bool) #-- contribution of geolocation uncertainty to height error Distributed_sigma_geo = np.ma.zeros((n_seg),fill_value=fill_value) Distributed_sigma_geo.mask = np.ones((n_seg),dtype=np.bool) #-- iterate over valid ATL03 segments #-- in ATL03 1-based indexing: invalid == 0 #-- here in 0-based indexing: invalid == -1 segment_indices, = np.nonzero((Segment_Index_begin[gtx][:-1] >= 0) & (Segment_Index_begin[gtx][1:] >= 0)) #-- verify that complementary beam pair is in list of beams iteration_count = len(segment_indices) if (cmp in IS2_atl03_beams) else 0 #-- run for each geoseg (distributed over comm.size # of processes) for iteration in range(comm.rank, iteration_count, comm.size): #-- indice for iteration (can run through a subset of segments) j = segment_indices[iteration] #-- across track slopes for beam if ((~Segment_Height[gtx].mask[j]) & (~Segment_Height[cmp].mask[j])): #-- segment fit across-track slopes dY = (Segment_Y_atc[gtx].data[j] - Segment_Y_atc[cmp].data[j]) Distributed_dH_across.data[j] = (Segment_Land_Ice[gtx].data[j] - Segment_Land_Ice[cmp].data[j])/dY Distributed_dH_across.mask[j] = False #-- segment fit across-track slope errors Distributed_dH_across_Error.data[j] = np.sqrt( Segment_Land_Ice_Error[gtx].data[j]**2 + Segment_Land_Ice_Error[cmp].data[j]**2)/np.abs(dY) Distributed_dH_across_Error.mask[j] = False #-- geolocation uncertainty sigma_geo_across = fileID[gtx]['geolocation']['sigma_across'][j] sigma_geo_along = fileID[gtx]['geolocation']['sigma_along'][j] sigma_geo_h = fileID[gtx]['geolocation']['sigma_h'][j] #-- contribution of geolocation uncertainty to height errors Distributed_sigma_geo.data[j] = np.sqrt(sigma_geo_h**2 + (sigma_geo_along*Segment_dH_along[gtx].data[j])**2 + (sigma_geo_across*Distributed_dH_across.data[j])**2) Distributed_sigma_geo.mask[j] = False #-- segment fit across-track slopes Segment_dH_across[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_dH_across[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_dH_across.data, MPI.DOUBLE], \ recvbuf=[Segment_dH_across[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_dH_across.mask, MPI.BOOL], \ recvbuf=[Segment_dH_across[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_dH_across = None #-- segment fit across-track slope errors Segment_dH_across_Error[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_dH_across_Error[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_dH_across_Error.data, MPI.DOUBLE], \ recvbuf=[Segment_dH_across_Error[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_dH_across_Error.mask, MPI.BOOL], \ recvbuf=[Segment_dH_across_Error[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_dH_across_Error = None #-- contribution of geolocation uncertainty to height errors Segment_sigma_geo[gtx] = np.ma.zeros((n_seg),fill_value=fill_value) Segment_sigma_geo[gtx].mask = np.ones((n_seg),dtype=np.bool) comm.Allreduce(sendbuf=[Distributed_sigma_geo.data, MPI.DOUBLE], \ recvbuf=[Segment_sigma_geo[gtx].data, MPI.DOUBLE], op=MPI.SUM) comm.Allreduce(sendbuf=[Distributed_sigma_geo.mask, MPI.BOOL], \ recvbuf=[Segment_sigma_geo[gtx].mask, MPI.BOOL], op=MPI.LAND) Distributed_sigma_geo = None #-- wait for all distributed processes to finish for beam comm.Barrier() #-- set values for invalid segments to fill_value of each variable Segment_delta_time[gtx].data[Segment_delta_time[gtx].mask] = Segment_delta_time[gtx].fill_value Segment_Height[gtx].data[Segment_Height[gtx].mask] = Segment_Height[gtx].fill_value Segment_Land_Ice[gtx].data[Segment_Land_Ice[gtx].mask] = Segment_Land_Ice[gtx].fill_value Segment_dH_along[gtx].data[Segment_dH_along[gtx].mask] = Segment_dH_along[gtx].fill_value Segment_dH_across[gtx].data[Segment_dH_across[gtx].mask] = Segment_dH_across[gtx].fill_value Segment_Height_Error[gtx].data[Segment_Height_Error[gtx].mask] = Segment_Height_Error[gtx].fill_value Segment_Land_Ice_Error[gtx].data[Segment_Land_Ice_Error[gtx].mask] = Segment_Land_Ice_Error[gtx].fill_value Segment_dH_along_Error[gtx].data[Segment_dH_along_Error[gtx].mask] = Segment_dH_along_Error[gtx].fill_value Segment_dH_across_Error[gtx].data[Segment_dH_across_Error[gtx].mask] = Segment_dH_across_Error[gtx].fill_value Segment_Mean_Median[gtx].data[Segment_Mean_Median[gtx].mask] = Segment_Mean_Median[gtx].fill_value Segment_X_atc[gtx].data[Segment_X_atc[gtx].mask] = Segment_X_atc[gtx].fill_value Segment_X_spread[gtx].data[Segment_X_spread[gtx].mask] = Segment_X_spread[gtx].fill_value Segment_Y_atc[gtx].data[Segment_Y_atc[gtx].mask] = Segment_Y_atc[gtx].fill_value Segment_sigma_geo[gtx].data[Segment_sigma_geo[gtx].mask] = Segment_sigma_geo[gtx].fill_value Segment_Longitude[gtx].data[Segment_Longitude[gtx].mask] = Segment_Longitude[gtx].fill_value Segment_Latitude[gtx].data[Segment_Latitude[gtx].mask] = Segment_Latitude[gtx].fill_value Segment_N_Fit[gtx].data[Segment_N_Fit[gtx].mask] = Segment_N_Fit[gtx].fill_value Segment_Window[gtx].data[Segment_Window[gtx].mask] = Segment_Window[gtx].fill_value Segment_RDE[gtx].data[Segment_RDE[gtx].mask] = Segment_RDE[gtx].fill_value Segment_SNR[gtx].data[Segment_SNR[gtx].mask] = Segment_SNR[gtx].fill_value Segment_Summary[gtx].data[Segment_Summary[gtx].mask] = Segment_Summary[gtx].fill_value Segment_Iterations[gtx].data[Segment_Iterations[gtx].mask] = Segment_Iterations[gtx].fill_value Segment_Source[gtx].data[Segment_Source[gtx].mask] = Segment_Source[gtx].fill_value Segment_Pulses[gtx].data[Segment_Pulses[gtx].mask] = Segment_Pulses[gtx].fill_value FPB_mean_corr[gtx].data[FPB_mean_corr[gtx].mask] = FPB_mean_corr[gtx].fill_value FPB_mean_sigma[gtx].data[FPB_mean_sigma[gtx].mask] = FPB_mean_sigma[gtx].fill_value FPB_median_corr[gtx].data[FPB_median_corr[gtx].mask] = FPB_median_corr[gtx].fill_value FPB_median_sigma[gtx].data[FPB_median_sigma[gtx].mask] = FPB_median_sigma[gtx].fill_value FPB_n_corr[gtx].data[FPB_n_corr[gtx].mask] = FPB_n_corr[gtx].fill_value FPB_cal_corr[gtx].data[FPB_cal_corr[gtx].mask] = FPB_cal_corr[gtx].fill_value TPS_mean_corr[gtx].data[TPS_mean_corr[gtx].mask] = TPS_mean_corr[gtx].fill_value TPS_median_corr[gtx].data[TPS_median_corr[gtx].mask] = TPS_median_corr[gtx].fill_value #-- save tep and dead time information and statistics IS2_atl03_fit['ancillary_data'][gtx] = {} IS2_atl03_attrs['ancillary_data'][gtx] = {} #-- tep time of day IS2_atl03_fit['ancillary_data'][gtx]['tep_tod'] = np.array(tep[gtx]['tep_tod']) IS2_atl03_attrs['ancillary_data'][gtx]['tep_tod'] = {} IS2_atl03_attrs['ancillary_data'][gtx]['tep_tod']['units'] = "seconds since 2018-01-01" IS2_atl03_attrs['ancillary_data'][gtx]['tep_tod']['long_name'] = "TEP Time Of Day" IS2_atl03_attrs['ancillary_data'][gtx]['tep_tod']['standard_name'] = "time" IS2_atl03_attrs['ancillary_data'][gtx]['tep_tod']['source'] = tep[gtx]['pce'] IS2_atl03_attrs['ancillary_data'][gtx]['tep_tod']['description'] = ("The time of day " "at of the start of the data within the TEP histogram, in seconds since the " "ATLAS SDP GPS 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.") #-- tep window start IS2_atl03_fit['ancillary_data'][gtx]['tx_start'] = np.array(tep[gtx]['tx_start']) IS2_atl03_attrs['ancillary_data'][gtx]['tx_start'] = {} IS2_atl03_attrs['ancillary_data'][gtx]['tx_start']['units'] = "seconds" IS2_atl03_attrs['ancillary_data'][gtx]['tx_start']['long_name'] = "Start of the TEP Window" IS2_atl03_attrs['ancillary_data'][gtx]['tx_start']['contentType'] = "auxiliaryInformation" IS2_atl03_attrs['ancillary_data'][gtx]['tx_start']['source'] = tep[gtx]['pce'] IS2_atl03_attrs['ancillary_data'][gtx]['tx_start']['description'] = ("Starting time for the " "window centered around the primary TEP arrival for calculating the transmit pulse shape.") #-- tep window end IS2_atl03_fit['ancillary_data'][gtx]['tx_end'] = np.array(tep[gtx]['tx_end']) IS2_atl03_attrs['ancillary_data'][gtx]['tx_end'] = {} IS2_atl03_attrs['ancillary_data'][gtx]['tx_end']['units'] = "seconds" IS2_atl03_attrs['ancillary_data'][gtx]['tx_end']['long_name'] = "End of the TEP Window" IS2_atl03_attrs['ancillary_data'][gtx]['tx_end']['contentType'] = "auxiliaryInformation" IS2_atl03_attrs['ancillary_data'][gtx]['tx_end']['source'] = tep[gtx]['pce'] IS2_atl03_attrs['ancillary_data'][gtx]['tx_end']['description'] = ("Ending time for the " "window centered around the primary TEP arrival for calculating the transmit pulse shape.") #-- tep robust dispersion estimator IS2_atl03_fit['ancillary_data'][gtx]['tx_robust_sprd'] = np.array(tep[gtx]['tx_robust_sprd']) IS2_atl03_attrs['ancillary_data'][gtx]['tx_robust_sprd'] = {} IS2_atl03_attrs['ancillary_data'][gtx]['tx_robust_sprd']['units'] = "seconds" IS2_atl03_attrs['ancillary_data'][gtx]['tx_robust_sprd']['long_name'] = "Robust Spread" IS2_atl03_attrs['ancillary_data'][gtx]['tx_robust_sprd']['contentType'] = "auxiliaryInformation" IS2_atl03_attrs['ancillary_data'][gtx]['tx_robust_sprd']['source'] = tep[gtx]['pce'] IS2_atl03_attrs['ancillary_data'][gtx]['tx_robust_sprd']['description'] = ("Temporal width of " "the transmit pulse (sec), calculated from the RDE of the primary TEP waveform") #-- tep full width at half maximum IS2_atl03_fit['ancillary_data'][gtx]['sigma_tx'] = np.array(tep[gtx]['sigma_tx']) IS2_atl03_attrs['ancillary_data'][gtx]['sigma_tx'] = {} IS2_atl03_attrs['ancillary_data'][gtx]['sigma_tx']['units'] = "seconds" IS2_atl03_attrs['ancillary_data'][gtx]['sigma_tx']['long_name'] = "Duration of Transmit Pulse" IS2_atl03_attrs['ancillary_data'][gtx]['sigma_tx']['contentType'] = "auxiliaryInformation" IS2_atl03_attrs['ancillary_data'][gtx]['sigma_tx']['source'] = tep[gtx]['pce'] IS2_atl03_attrs['ancillary_data'][gtx]['sigma_tx']['description'] = ("Temporal duration of " "the transmit pulse (sec), calculated from the FWHM of the TEP waveform") #-- mean dead time IS2_atl03_fit['ancillary_data'][gtx]['t_dead'] = np.array(mean_dead_time[gtx]) IS2_atl03_attrs['ancillary_data'][gtx]['t_dead'] = {} IS2_atl03_attrs['ancillary_data'][gtx]['t_dead']['units'] = "seconds" IS2_atl03_attrs['ancillary_data'][gtx]['t_dead']['long_name'] = "Dead-time" IS2_atl03_attrs['ancillary_data'][gtx]['t_dead']['contentType'] = "auxiliaryInformation" IS2_atl03_attrs['ancillary_data'][gtx]['t_dead']['source'] = "CAL42" IS2_atl03_attrs['ancillary_data'][gtx]['t_dead']['description'] = ("Mean dead-time for " "channels in the detector (sec)") #-- copy beam variables IS2_atl03_fit[gtx] = dict(land_ice_segments={}) IS2_atl03_fill[gtx] = dict(land_ice_segments={}) IS2_atl03_attrs[gtx] = dict(land_ice_segments={}) #-- group attributes for beam IS2_atl03_attrs[gtx]['Description'] = fileID[gtx].attrs['Description'] IS2_atl03_attrs[gtx]['atlas_pce'] = fileID[gtx].attrs['atlas_pce'] IS2_atl03_attrs[gtx]['atlas_beam_type'] = fileID[gtx].attrs['atlas_beam_type'] IS2_atl03_attrs[gtx]['groundtrack_id'] = fileID[gtx].attrs['groundtrack_id'] IS2_atl03_attrs[gtx]['atmosphere_profile'] = fileID[gtx].attrs['atmosphere_profile'] IS2_atl03_attrs[gtx]['atlas_spot_number'] = fileID[gtx].attrs['atlas_spot_number'] IS2_atl03_attrs[gtx]['sc_orientation'] = fileID[gtx].attrs['sc_orientation'] #-- group attributes for land_ice_segments IS2_atl03_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_atl03_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_atl03_fit[gtx]['land_ice_segments']['delta_time'] = Segment_delta_time[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['delta_time'] = Segment_delta_time[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['delta_time'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['delta_time']['units'] = "seconds since 2018-01-01" IS2_atl03_attrs[gtx]['land_ice_segments']['delta_time']['long_name'] = "Elapsed GPS seconds" IS2_atl03_attrs[gtx]['land_ice_segments']['delta_time']['standard_name'] = "time" IS2_atl03_attrs[gtx]['land_ice_segments']['delta_time']['calendar'] = "standard" IS2_atl03_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_atl03_attrs[gtx]['land_ice_segments']['delta_time']['coordinates'] = \ "segment_id latitude longitude" #-- latitude IS2_atl03_fit[gtx]['land_ice_segments']['latitude'] = Segment_Latitude[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['latitude'] = Segment_Latitude[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['latitude'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['latitude']['units'] = "degrees_north" IS2_atl03_attrs[gtx]['land_ice_segments']['latitude']['contentType'] = "physicalMeasurement" IS2_atl03_attrs[gtx]['land_ice_segments']['latitude']['long_name'] = "Latitude" IS2_atl03_attrs[gtx]['land_ice_segments']['latitude']['standard_name'] = "latitude" IS2_atl03_attrs[gtx]['land_ice_segments']['latitude']['description'] = ("Latitude of " "segment center") IS2_atl03_attrs[gtx]['land_ice_segments']['latitude']['valid_min'] = -90.0 IS2_atl03_attrs[gtx]['land_ice_segments']['latitude']['valid_max'] = 90.0 IS2_atl03_attrs[gtx]['land_ice_segments']['latitude']['coordinates'] = \ "segment_id delta_time longitude" #-- longitude IS2_atl03_fit[gtx]['land_ice_segments']['longitude'] = Segment_Longitude[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['longitude'] = Segment_Longitude[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['longitude'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['longitude']['units'] = "degrees_east" IS2_atl03_attrs[gtx]['land_ice_segments']['longitude']['contentType'] = "physicalMeasurement" IS2_atl03_attrs[gtx]['land_ice_segments']['longitude']['long_name'] = "Longitude" IS2_atl03_attrs[gtx]['land_ice_segments']['longitude']['standard_name'] = "longitude" IS2_atl03_attrs[gtx]['land_ice_segments']['longitude']['description'] = ("Longitude of " "segment center") IS2_atl03_attrs[gtx]['land_ice_segments']['longitude']['valid_min'] = -180.0 IS2_atl03_attrs[gtx]['land_ice_segments']['longitude']['valid_max'] = 180.0 IS2_atl03_attrs[gtx]['land_ice_segments']['longitude']['coordinates'] = \ "segment_id delta_time latitude" #-- segment ID IS2_atl03_fit[gtx]['land_ice_segments']['segment_id'] = Segment_ID[gtx][1:] IS2_atl03_attrs[gtx]['land_ice_segments']['segment_id'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['segment_id']['units'] = "1" IS2_atl03_attrs[gtx]['land_ice_segments']['segment_id']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['segment_id']['long_name'] = "Along-track segment ID number" IS2_atl03_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_atl03_attrs[gtx]['land_ice_segments']['segment_id']['coordinates'] = \ "delta_time latitude longitude" #-- land ice height corrected for first photon bias and transmit-pulse shape IS2_atl03_fit[gtx]['land_ice_segments']['h_li'] = Segment_Land_Ice[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['h_li'] = Segment_Land_Ice[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['h_li'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['h_li']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['h_li']['contentType'] = "physicalMeasurement" IS2_atl03_attrs[gtx]['land_ice_segments']['h_li']['long_name'] = "Land Ice height" IS2_atl03_attrs[gtx]['land_ice_segments']['h_li']['description'] = ("Standard land-ice segment " "height determined by land ice algorithm, corrected for first-photon bias, representing the " "median-based height of the selected PEs") IS2_atl03_attrs[gtx]['land_ice_segments']['h_li']['coordinates'] = \ "segment_id delta_time latitude longitude" #-- land ice height errors (max of fit or first photon bias uncertainties) IS2_atl03_fit[gtx]['land_ice_segments']['h_li_sigma'] = Segment_Land_Ice_Error[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['h_li_sigma'] = Segment_Land_Ice_Error[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['h_li_sigma'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['h_li_sigma']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['h_li_sigma']['contentType'] = "qualityInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['h_li_sigma']['long_name'] = "Expected RMS segment misfit" IS2_atl03_attrs[gtx]['land_ice_segments']['h_li_sigma']['description'] = ("Propagated error due to " "sampling error and FPB correction from the land ice algorithm") IS2_atl03_attrs[gtx]['land_ice_segments']['h_li_sigma']['coordinates'] = \ "segment_id delta_time latitude longitude" #-- vertical geolocation error due to PPD and POD IS2_atl03_fit[gtx]['land_ice_segments']['sigma_geo_h'] = Segment_sigma_geo[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['sigma_geo_h'] = Segment_sigma_geo[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['sigma_geo_h'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['sigma_geo_h']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['sigma_geo_h']['contentType'] = "qualityInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['sigma_geo_h']['long_name'] = "Vertical Geolocation Error" IS2_atl03_attrs[gtx]['land_ice_segments']['sigma_geo_h']['description'] = ("Total vertical geolocation error " "due to PPD and POD, including the effects of horizontal geolocation error on the segment vertical error.") IS2_atl03_attrs[gtx]['land_ice_segments']['sigma_geo_h']['coordinates'] = \ "segment_id delta_time latitude longitude" #-- segment quality summary IS2_atl03_fit[gtx]['land_ice_segments']['atl06_quality_summary'] = Segment_Summary[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['atl06_quality_summary'] = Segment_Summary[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['atl06_quality_summary'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['atl06_quality_summary']['units'] = "1" IS2_atl03_attrs[gtx]['land_ice_segments']['atl06_quality_summary']['contentType'] = "qualityInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['atl06_quality_summary']['long_name'] = "ATL06 Quality Summary" IS2_atl03_attrs[gtx]['land_ice_segments']['atl06_quality_summary']['description'] = ("The ATL06_quality_summary " "parameter indicates the best-quality subset of all ATL06 data. A zero in this parameter implies that no " "data-quality tests have found a problem with the segment, a one implies that some potential problem has " "been found. Users who select only segments with zero values for this flag can be relatively certain of " "obtaining high-quality data, but will likely miss a significant fraction of usable data, particularly in " "cloudy, rough, or low-surface-reflectance conditions.") IS2_atl03_attrs[gtx]['land_ice_segments']['atl06_quality_summary']['flag_meanings'] = \ "best_quality potential_problem" IS2_atl03_attrs[gtx]['land_ice_segments']['longitude']['valid_min'] = 0 IS2_atl03_attrs[gtx]['land_ice_segments']['longitude']['valid_max'] = 1 IS2_atl03_attrs[gtx]['land_ice_segments']['atl06_quality_summary']['coordinates'] = \ "segment_id delta_time latitude longitude" #-- dem variables IS2_atl03_fit[gtx]['land_ice_segments']['dem'] = {} IS2_atl03_fill[gtx]['land_ice_segments']['dem'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['dem'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['dem']['Description'] = ("The dem group " "contains the reference digital elevation model and geoid heights.") IS2_atl03_attrs[gtx]['land_ice_segments']['dem']['data_rate'] = ("Data within this group " "are stored at the land_ice_segments segment rate.") #-- geoid height fv = fileID[gtx]['geophys_corr']['geoid'].attrs['_FillValue'] geoid = np.ma.array(fileID[gtx]['geophys_corr']['geoid'][:], fill_value=fv) geoid.mask = geoid.data == geoid.fill_value geoid_h = (geoid[1:] + geoid[0:-1])/2.0 geoid_h.data[geoid_h.mask] = geoid_h.fill_value IS2_atl03_fit[gtx]['land_ice_segments']['dem']['geoid_h'] = geoid_h IS2_atl03_fill[gtx]['land_ice_segments']['dem']['geoid_h'] = geoid_h.fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['dem']['geoid_h'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['dem']['geoid_h']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['dem']['geoid_h']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['dem']['geoid_h']['long_name'] = "Geoid Height" IS2_atl03_attrs[gtx]['land_ice_segments']['dem']['geoid_h']['description'] = ("Geoid height above " "WGS-84 reference ellipsoid (range -107 to 86m)") IS2_atl03_attrs[gtx]['land_ice_segments']['dem']['geoid_h']['source'] = "EGM2008" IS2_atl03_attrs[gtx]['land_ice_segments']['dem']['geoid_h']['valid_min'] = -107 IS2_atl03_attrs[gtx]['land_ice_segments']['dem']['geoid_h']['valid_max'] = 86 IS2_atl03_attrs[gtx]['land_ice_segments']['dem']['geoid_h']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- geophysical variables IS2_atl03_fit[gtx]['land_ice_segments']['geophysical'] = {} IS2_atl03_fill[gtx]['land_ice_segments']['geophysical'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical'] = {} IS2_atl03_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_atl03_attrs[gtx]['land_ice_segments']['geophysical']['data_rate'] = ("Data within this group " "are stored at the land_ice_segments segment rate.") #-- background rate bckgrd = (Segment_Background[gtx][1:] + Segment_Background[gtx][0:-1])/2.0 IS2_atl03_fit[gtx]['land_ice_segments']['geophysical']['bckgrd'] = np.copy(bckgrd) IS2_atl03_fill[gtx]['land_ice_segments']['geophysical']['bckgrd'] = None IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bckgrd'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bckgrd']['units'] = "counts / second" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bckgrd']['contentType'] = "modelResult" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bckgrd']['long_name'] = ("Background count " "rate based on the ATLAS 50-shot sum interpolated to the reference photon") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bckgrd']['description'] = ("The background " "count rate from the 50-shot altimetric histogram after removing the number of likely signal photons") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bckgrd']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- blowing snow PSC flag IS2_atl03_fit[gtx]['land_ice_segments']['geophysical']['bsnow_psc'] = \ IS2_atl09_mds[pfl]['high_rate']['bsnow_psc'][1:] IS2_atl03_fill[gtx]['land_ice_segments']['geophysical']['bsnow_psc'] = None IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_psc'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_psc']['units'] = "1" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_psc']['contentType'] = "modelResult" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_psc']['long_name'] = "Blowing snow PSC flag" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_psc']['description'] = ("Indicates the " "potential for polar stratospheric clouds to affect the blowing snow retrieval, where 0=none and 3=maximum. " "This flag is a function of month and hemisphere and is only applied poleward of 60 north and south") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_psc']['flag_meanings'] = ("none slight " "moderate maximum_bsnow_PSC_affected") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_psc']['flag_values'] = [0,1,2,3] IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_psc']['valid_min'] = 0 IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_psc']['valid_max'] = 3 IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_psc']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- blowing snow confidence IS2_atl03_fit[gtx]['land_ice_segments']['geophysical']['bsnow_conf'] = \ IS2_atl09_mds[pfl]['high_rate']['bsnow_con'][1:] IS2_atl03_fill[gtx]['land_ice_segments']['geophysical']['bsnow_conf'] = None IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_conf'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_conf']['units'] = "1" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_conf']['contentType'] = "modelResult" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_conf']['long_name'] = "Blowing snow confidence" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_conf']['description'] = ("Indicates the blowing snow " "confidence, where -3=surface not detected; 2=no surface wind;-1=no scattering layer found; 0=no top layer found; " "1=none-little; 2=weak; 3=moderate; 4=moderate-high; 5=high; 6=very high") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_conf']['flag_meanings'] = ("surface_not_detected " "no_surface_wind no_scattering_layer_found no_top_layer_found none_little weak moderate moderate_high high very_high") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_conf']['flag_values'] = [-3,-2,-1,0,1,2,3,4,5,6] IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_conf']['valid_min'] = -3 IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_conf']['valid_max'] = 6 IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_conf']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- blowing snow optical depth bsnow_od = np.ma.array(IS2_atl09_mds[pfl]['high_rate']['bsnow_od'][1:], fill_value=IS2_atl09_attrs[pfl]['high_rate']['bsnow_od']['_FillValue']) IS2_atl03_fit[gtx]['land_ice_segments']['geophysical']['bsnow_od'] = bsnow_od IS2_atl03_fill[gtx]['land_ice_segments']['geophysical']['bsnow_od'] = bsnow_od.fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_od'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_od']['units'] = "1" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_od']['contentType'] = "modelResult" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_od']['long_name'] = "Blowing snow OD" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_od']['description'] = "Blowing snow layer optical depth" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['bsnow_od']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- cloud flag ASR IS2_atl03_fit[gtx]['land_ice_segments']['geophysical']['cloud_flag_asr'] = \ IS2_atl09_mds[pfl]['high_rate']['cloud_flag_asr'][1:] IS2_atl03_fill[gtx]['land_ice_segments']['geophysical']['cloud_flag_asr'] = None IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_asr'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_asr']['units'] = "1" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_asr']['contentType'] = "modelResult" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_asr']['long_name'] = "Cloud Flag ASR" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_asr']['description'] = ("Indicates the Cloud " "flag probability from apparent surface reflectance, where 0=clear with high confidence; 1=clear with medium " "confidence; 2=clear with low confidence; 3=cloudy with low confidence; 4=cloudy with medium confidence; 5=cloudy " "with high confidence; 6=unknown") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_asr']['flag_meanings'] = ("clear_with_high_confidence " "clear_with_medium_confidence clear_with_low_confidence cloudy_with_low_confidence cloudy_with_medium_confidence " "cloudy_with_high_confidence unknown") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_asr']['flag_values'] = [0,1,2,3,4,5,6] IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_asr']['valid_min'] = 0 IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_asr']['valid_max'] = 6 IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_asr']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- cloud flag atm IS2_atl03_fit[gtx]['land_ice_segments']['geophysical']['cloud_flag_atm'] = \ IS2_atl09_mds[pfl]['high_rate']['cloud_flag_atm'][1:] IS2_atl03_fill[gtx]['land_ice_segments']['geophysical']['cloud_flag_atm'] = None IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_atm'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_atm']['units'] = "1" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_atm']['contentType'] = "modelResult" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_atm']['long_name'] = "Cloud Flag Atm" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_atm']['description'] = ("Number of layers found " "from the backscatter profile using the DDA layer finder") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_atm']['flag_values'] = [0,1,2,3,4,5,6,7,8,9,10] IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_atm']['valid_min'] = 0 IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_atm']['valid_max'] = 10 IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['cloud_flag_atm']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- multiple scattering warning flag msw_flag = np.ma.array(IS2_atl09_mds[pfl]['high_rate']['msw_flag'][1:], fill_value=IS2_atl09_attrs[pfl]['high_rate']['msw_flag']['_FillValue']) IS2_atl03_fit[gtx]['land_ice_segments']['geophysical']['msw_flag'] = msw_flag IS2_atl03_fill[gtx]['land_ice_segments']['geophysical']['msw_flag'] = msw_flag.fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['msw_flag'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['msw_flag']['units'] = "1" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['msw_flag']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['msw_flag']['long_name'] = "Multiple Scattering Warning Flag" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['msw_flag']['description'] = ("Combined flag indicating the " "risks of severe multiple scattering. The multiple scattering warning flag (ATL09 parameter msw_flag) has values from " "-1 to 5 where zero means no multiple scattering and 5 the greatest. If no layers were detected, then msw_flag = 0. " "If blowing snow is detected and its estimated optical depth is greater than or equal to 0.5, then msw_flag = 5. " "If the blowing snow optical depth is less than 0.5, then msw_flag = 4. If no blowing snow is detected but there are " "cloud or aerosol layers detected, the msw_flag assumes values of 1 to 3 based on the height of the bottom of the " "lowest layer: < 1 km, msw_flag = 3; 1-3 km, msw_flag = 2; > 3km, msw_flag = 1. A value of -1 indicates that the " "signal to noise of the data was too low to reliably ascertain the presence of cloud or blowing snow. We expect " "values of -1 to occur only during daylight.") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['msw_flag']['flag_meanings'] = ("cannot_determine " "no_layers layer_gt_3km layer_between_1_and_3_km layer_lt_1km blow_snow_od_lt_0.5 blow_snow_od_gt_0.5") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['msw_flag']['flag_values'] = [-1,0,1,2,3,4,5] IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['msw_flag']['valid_min'] = -1 IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['msw_flag']['valid_max'] = 5 IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['msw_flag']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- range bias correction fv = fileID[gtx]['geolocation']['range_bias_corr'].attrs['_FillValue'] range_bias_corr = np.ma.array(fileID[gtx]['geolocation']['range_bias_corr'][:], fill_value=fv) range_bias_corr.mask = range_bias_corr.data == range_bias_corr.fill_value segment_range_bias = (range_bias_corr[1:] + range_bias_corr[0:-1])/2.0 segment_range_bias.data[segment_range_bias.mask] = segment_range_bias.fill_value IS2_atl03_fit[gtx]['land_ice_segments']['geophysical']['range_bias_corr'] = segment_range_bias IS2_atl03_fill[gtx]['land_ice_segments']['geophysical']['range_bias_corr'] = segment_range_bias.fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['range_bias_corr'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['range_bias_corr']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['range_bias_corr']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['range_bias_corr']['long_name'] = "Range bias correction" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['range_bias_corr']['description'] = "The range_bias estimated from geolocation analysis" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['range_bias_corr']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- total neutral atmosphere delay correction fv = fileID[gtx]['geolocation']['neutat_delay_total'].attrs['_FillValue'] neutat_delay_total = np.ma.array(fileID[gtx]['geolocation']['neutat_delay_total'][:], fill_value=fv) neutat_delay_total.mask = neutat_delay_total.data == neutat_delay_total.fill_value segment_neutat_delay = (neutat_delay_total[1:] + neutat_delay_total[0:-1])/2.0 segment_neutat_delay.data[segment_neutat_delay.mask] = segment_neutat_delay.fill_value IS2_atl03_fit[gtx]['land_ice_segments']['geophysical']['neutat_delay_total'] = segment_neutat_delay IS2_atl03_fill[gtx]['land_ice_segments']['geophysical']['neutat_delay_total'] = segment_neutat_delay.fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['neutat_delay_total'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['neutat_delay_total']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['neutat_delay_total']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['neutat_delay_total']['long_name'] = "Total Neutral Atmospheric Delay" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['neutat_delay_total']['description'] = "Total neutral atmosphere delay correction (wet+dry)" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['neutat_delay_total']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- solar elevation fv = fileID[gtx]['geolocation']['solar_elevation'].attrs['_FillValue'] solar_elevation = np.ma.array(fileID[gtx]['geolocation']['solar_elevation'][:], fill_value=fv) solar_elevation.mask = solar_elevation.data == solar_elevation.fill_value segment_solar_elevation = (solar_elevation[1:] + solar_elevation[0:-1])/2.0 segment_solar_elevation.data[segment_solar_elevation.mask] = segment_solar_elevation.fill_value IS2_atl03_fit[gtx]['land_ice_segments']['geophysical']['solar_elevation'] = segment_solar_elevation IS2_atl03_fill[gtx]['land_ice_segments']['geophysical']['solar_elevation'] = segment_solar_elevation.fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['solar_elevation'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['solar_elevation']['units'] = "degrees" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['solar_elevation']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['solar_elevation']['long_name'] = "Solar elevation" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['solar_elevation']['description'] = ("Solar Angle above " "or below the plane tangent to the ellipsoid surface at the laser spot. Positive values mean the sun is above the " "horizon, while negative values mean it is below the horizon. The effect of atmospheric refraction is not included.") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['solar_elevation']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- solar azimuth fv = fileID[gtx]['geolocation']['solar_azimuth'].attrs['_FillValue'] solar_azimuth = np.ma.array(fileID[gtx]['geolocation']['solar_azimuth'][:], fill_value=fv) solar_azimuth.mask = solar_azimuth.data == solar_azimuth.fill_value segment_solar_azimuth = (solar_azimuth[1:] + solar_azimuth[0:-1])/2.0 segment_solar_azimuth.data[segment_solar_azimuth.mask] = segment_solar_azimuth.fill_value IS2_atl03_fit[gtx]['land_ice_segments']['geophysical']['solar_azimuth'] = segment_solar_azimuth IS2_atl03_fill[gtx]['land_ice_segments']['geophysical']['solar_azimuth'] = segment_solar_azimuth.fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['solar_azimuth'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['solar_azimuth']['units'] = "degrees_east" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['solar_azimuth']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['solar_azimuth']['long_name'] = "Solar azimuth" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['solar_azimuth']['description'] = ("The direction, " "eastwards from north, of the sun vector as seen by an observer at the laser ground spot.") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['solar_azimuth']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- geophysical correction values at segment reference photons #-- dynamic atmospheric correction fv = fileID[gtx]['geophys_corr']['dac'].attrs['_FillValue'] dac = np.ma.array(fileID[gtx]['geophys_corr']['dac'][:], fill_value=fv) dac.mask = dac.data == dac.fill_value segment_dac = (dac[1:] + dac[0:-1])/2.0 segment_dac.data[segment_dac.mask] = segment_dac.fill_value IS2_atl03_fit[gtx]['land_ice_segments']['geophysical']['dac'] = segment_dac IS2_atl03_fill[gtx]['land_ice_segments']['geophysical']['dac'] = segment_dac.fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['dac'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['dac']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['dac']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['dac']['long_name'] = "Dynamic Atmosphere Correction" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['dac']['description'] = ("Dynamic Atmospheric Correction " "(DAC) includes inverted barometer (IB) effect") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['dac']['source'] = 'Mog2D-G' IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['dac']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- solid earth tide fv = fileID[gtx]['geophys_corr']['tide_earth'].attrs['_FillValue'] tide_earth = np.ma.array(fileID[gtx]['geophys_corr']['tide_earth'][:], fill_value=fv) tide_earth.mask = tide_earth.data == tide_earth.mask segment_earth_tide = (tide_earth[1:] + tide_earth[0:-1])/2.0 segment_earth_tide.data[segment_earth_tide.mask] = segment_earth_tide.fill_value IS2_atl03_fit[gtx]['land_ice_segments']['geophysical']['tide_earth'] = segment_earth_tide IS2_atl03_fill[gtx]['land_ice_segments']['geophysical']['tide_earth'] = segment_earth_tide.fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_earth'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_earth']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_earth']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_earth']['long_name'] = "Earth Tide" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_earth']['description'] = "Solid Earth Tide" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_earth']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- load tide fv = fileID[gtx]['geophys_corr']['tide_load'].attrs['_FillValue'] tide_load = np.ma.array(fileID[gtx]['geophys_corr']['tide_load'][:], fill_value=fv) tide_load.mask = tide_load.data == tide_load.fill_value segment_load_tide = (tide_load[1:] + tide_load[0:-1])/2.0 segment_load_tide.data[segment_load_tide.mask] = segment_load_tide.fill_value IS2_atl03_fit[gtx]['land_ice_segments']['geophysical']['tide_load'] = segment_load_tide IS2_atl03_fill[gtx]['land_ice_segments']['geophysical']['tide_load'] = segment_load_tide.fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_load'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_load']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_load']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_load']['long_name'] = "Load Tide" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_load']['description'] = ("Load Tide - Local " "displacement due to Ocean Loading (-6 to 0 cm)") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_load']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- ocean tide fv = fileID[gtx]['geophys_corr']['tide_ocean'].attrs['_FillValue'] tide_ocean = np.ma.array(fileID[gtx]['geophys_corr']['tide_ocean'][:], fill_value=fv) tide_ocean.mask = tide_ocean.data == tide_ocean.fill_value segment_ocean_tide = (tide_ocean[1:] + tide_ocean[0:-1])/2.0 segment_ocean_tide.data[segment_ocean_tide.mask] = segment_ocean_tide.fill_value IS2_atl03_fit[gtx]['land_ice_segments']['geophysical']['tide_ocean'] = segment_ocean_tide IS2_atl03_fill[gtx]['land_ice_segments']['geophysical']['tide_ocean'] = segment_ocean_tide.fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_ocean'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_ocean']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_ocean']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_ocean']['long_name'] = "Ocean Tide" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_ocean']['description'] = ("Ocean Tides " "including diurnal and semi-diurnal (harmonic analysis), and longer period tides (dynamic and " "self-consistent equilibrium).") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_ocean']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- ocean pole tide fv = fileID[gtx]['geophys_corr']['tide_oc_pole'].attrs['_FillValue'] tide_oc_pole = np.ma.array(fileID[gtx]['geophys_corr']['tide_oc_pole'][:], mask=(fileID[gtx]['geophys_corr']['tide_oc_pole'][:] == fv), fill_value=fv) segment_oc_pole_tide = (tide_oc_pole[1:] + tide_oc_pole[0:-1])/2.0 segment_oc_pole_tide.data[segment_oc_pole_tide.mask] = segment_oc_pole_tide.fill_value IS2_atl03_fit[gtx]['land_ice_segments']['geophysical']['tide_oc_pole'] = segment_oc_pole_tide IS2_atl03_fill[gtx]['land_ice_segments']['geophysical']['tide_oc_pole'] = segment_oc_pole_tide.fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_oc_pole'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_oc_pole']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_oc_pole']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_oc_pole']['long_name'] = "Ocean Pole Tide" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_oc_pole']['description'] = ("Oceanic surface " "rotational deformation due to polar motion (-2 to 2 mm).") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_oc_pole']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- pole tide fv = fileID[gtx]['geophys_corr']['tide_pole'].attrs['_FillValue'] tide_pole = np.ma.array(fileID[gtx]['geophys_corr']['tide_pole'][:], fill_value=fv) tide_pole.mask = tide_pole.data == tide_pole.fill_value segment_pole_tide = (tide_pole[1:] + tide_pole[0:-1])/2.0 segment_pole_tide.data[segment_pole_tide.mask] = segment_pole_tide.fill_value IS2_atl03_fit[gtx]['land_ice_segments']['geophysical']['tide_pole'] = segment_pole_tide IS2_atl03_fill[gtx]['land_ice_segments']['geophysical']['tide_pole'] = segment_pole_tide.fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_pole'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_pole']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_pole']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_pole']['long_name'] = "Solid Earth Pole Tide" IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_pole']['description'] = ("Solid Earth Pole " "Tide - Rotational deformation due to polar motion (-1.5 to 1.5 cm).") IS2_atl03_attrs[gtx]['land_ice_segments']['geophysical']['tide_pole']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- bias correction variables IS2_atl03_fit[gtx]['land_ice_segments']['bias_correction'] = {} IS2_atl03_fill[gtx]['land_ice_segments']['bias_correction'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['Description'] = ("The bias_correction group " "contains information about the estimated first-photon bias, and the transmit-pulse-shape bias.") IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['data_rate'] = ("Data within this group " "are stored at the land_ice_segments segment rate.") #-- mean first photon bias IS2_atl03_fit[gtx]['land_ice_segments']['bias_correction']['fpb_mean_corr'] = FPB_mean_corr[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['bias_correction']['fpb_mean_corr'] = FPB_mean_corr[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_mean_corr'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_mean_corr']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_mean_corr']['contentType'] = "qualityInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_mean_corr']['long_name'] = "first photon bias mean correction" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_mean_corr']['description'] = ("Estimated first-photon-bias " "(fpb) correction to mean segment height") IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_mean_corr']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- mean first photon bias uncertainty IS2_atl03_fit[gtx]['land_ice_segments']['bias_correction']['fpb_mean_corr_sigma'] = FPB_mean_sigma[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['bias_correction']['fpb_mean_corr_sigma'] = FPB_mean_sigma[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_mean_corr_sigma'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_mean_corr_sigma']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_mean_corr_sigma']['contentType'] = "qualityInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_mean_corr_sigma']['long_name'] = ("first photon bias " "mean correction error") IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_mean_corr_sigma']['description'] = ("Estimated error in " "first-photon-bias (fpb) correction for mean segment heights") IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_mean_corr_sigma']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- median first photon bias IS2_atl03_fit[gtx]['land_ice_segments']['bias_correction']['fpb_med_corr'] = FPB_median_corr[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['bias_correction']['fpb_med_corr'] = FPB_median_corr[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_med_corr'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_med_corr']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_med_corr']['contentType'] = "qualityInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_med_corr']['long_name'] = "first photon bias median correction" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_med_corr']['description'] = ("Estimated first-photon-bias " "(fpb) correction giving the difference between the mean segment height and the corrected median height") IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_med_corr']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- median first photon bias correction IS2_atl03_fit[gtx]['land_ice_segments']['bias_correction']['fpb_med_corr_sigma'] = FPB_median_sigma[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['bias_correction']['fpb_med_corr_sigma'] = FPB_median_sigma[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_med_corr_sigma'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_med_corr_sigma']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_med_corr_sigma']['contentType'] = "qualityInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_med_corr_sigma']['long_name'] = ("first photon bias median " "correction error") IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_med_corr_sigma']['description'] = ("Estimated error in " "first-photon-bias (fpb) correction giving the difference between the mean segment height and the corrected median height") IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_med_corr_sigma']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- first photon bias corrected number of photons IS2_atl03_fit[gtx]['land_ice_segments']['bias_correction']['fpb_n_corr'] = FPB_n_corr[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['bias_correction']['fpb_n_corr'] = FPB_n_corr[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_n_corr'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_n_corr']['units'] = "1" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_n_corr']['contentType'] = "qualityInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_n_corr']['long_name'] = "corrected number of photons" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_n_corr']['description'] = ("Estimated photon count after " "first-photon-bias correction") IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_n_corr']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- CAL-19 first photon bias IS2_atl03_fit[gtx]['land_ice_segments']['bias_correction']['fpb_cal_corr'] = FPB_cal_corr[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['bias_correction']['fpb_cal_corr'] = FPB_cal_corr[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_cal_corr'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_cal_corr']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_cal_corr']['contentType'] = "qualityInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_cal_corr']['long_name'] = "first photon bias calibrated correction" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_cal_corr']['description'] = ("Estimated first-photon-bias " "(fpb) correction calculated using the ATL03 calibration products") IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['fpb_cal_corr']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- mean transmit pulse shape correction IS2_atl03_fit[gtx]['land_ice_segments']['bias_correction']['tx_mean_corr'] = TPS_mean_corr[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['bias_correction']['tx_mean_corr'] = TPS_mean_corr[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['tx_mean_corr'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['tx_mean_corr']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['tx_mean_corr']['contentType'] = "qualityInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['tx_mean_corr']['long_name'] = "tx shape mean correction" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['tx_mean_corr']['description'] = ("Estimate of the difference " "between the mean of the full-waveform transmit-pulse and the mean of a broadened, truncated waveform consistent " "with the received pulse") IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['tx_mean_corr']['source'] = tep[gtx]['pce'] IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['tx_mean_corr']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- median transmit pulse shape correction IS2_atl03_fit[gtx]['land_ice_segments']['bias_correction']['tx_med_corr'] = TPS_median_corr[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['bias_correction']['tx_med_corr'] = TPS_median_corr[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['tx_med_corr'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['tx_med_corr']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['tx_med_corr']['contentType'] = "qualityInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['tx_med_corr']['long_name'] = "tx shape median correction" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['tx_med_corr']['description'] = ("Estimate of the difference " "between the median of the full-waveform transmit-pulse and the median of a broadened, truncated waveform consistent " "with the received pulse") IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['tx_med_corr']['source'] = tep[gtx]['pce'] IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['tx_med_corr']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- difference between the mean and median of fit residuals IS2_atl03_fit[gtx]['land_ice_segments']['bias_correction']['med_r_fit'] = Segment_Mean_Median[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['bias_correction']['med_r_fit'] = Segment_Mean_Median[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['med_r_fit'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['med_r_fit']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['med_r_fit']['contentType'] = "qualityInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['med_r_fit']['long_name'] = "mean median residual" IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['med_r_fit']['description'] = ("Difference between " "uncorrected mean and median of linear fit residuals") IS2_atl03_attrs[gtx]['land_ice_segments']['bias_correction']['med_r_fit']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- fit statistics variables IS2_atl03_fit[gtx]['land_ice_segments']['fit_statistics'] = {} IS2_atl03_fill[gtx]['land_ice_segments']['fit_statistics'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['Description'] = ("The fit_statistics group " "contains a variety of parameters that might indicate the quality of the fitted segment data. Data in " "this group are sparse, with dimensions matching the land_ice_segments group.") IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['data_rate'] = ("Data within this group " "are stored at the land_ice_segments segment rate.") #-- segment fit heights IS2_atl03_fit[gtx]['land_ice_segments']['fit_statistics']['h_mean'] = Segment_Height[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['fit_statistics']['h_mean'] = Segment_Height[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['h_mean'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['h_mean']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['h_mean']['contentType'] = "modelResult" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['h_mean']['long_name'] = "Height Mean" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['h_mean']['description'] = ("Mean surface " "height, not corrected for first-photon bias or pulse truncation") IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['h_mean']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- segment fit along-track slopes IS2_atl03_fit[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dx'] = Segment_dH_along[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dx'] = Segment_dH_along[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dx'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dx']['units'] = "meters/meters" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dx']['contentType'] = "modelResult" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dx']['long_name'] = "Along Track Slope" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dx']['description'] = ("Along-track slope " "from along-track segment fit") IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dx']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- segment fit across-track slopes IS2_atl03_fit[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dy'] = Segment_dH_across[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dy'] = Segment_dH_across[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dy'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dy']['units'] = "meters/meters" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dy']['contentType'] = "modelResult" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dy']['long_name'] = "Across Track Slope" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dy']['description'] = ("Across track slope " "from segment fits to weak and strong beam; the same slope is reported for both laser beams in each pair") IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dy']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- segment fit height errors IS2_atl03_fit[gtx]['land_ice_segments']['fit_statistics']['sigma_h_mean'] = Segment_Height_Error[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['fit_statistics']['sigma_h_mean'] = Segment_Height_Error[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['sigma_h_mean'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['sigma_h_mean']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['sigma_h_mean']['contentType'] = "qualityInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['sigma_h_mean']['long_name'] = "Height Error" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['sigma_h_mean']['description'] = ("Propagated height " "error due to PE-height sampling error for height from the along-track fit, not including geolocation-induced error") IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['sigma_h_mean']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- segment fit across-track slope errors IS2_atl03_fit[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dx_sigma'] = Segment_dH_along_Error[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dx_sigma'] = Segment_dH_along_Error[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dx_sigma'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dx_sigma']['units'] = "meters/meters" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dx_sigma']['contentType'] = "qualityInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dx_sigma']['long_name'] = "Sigma of Along Track Slope" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dx_sigma']['description'] = ("Propagated error in " "the along-track segment slope") IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dx_sigma']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- segment fit along-track slope errors IS2_atl03_fit[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dy_sigma'] = Segment_dH_across_Error[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dy_sigma'] = Segment_dH_across_Error[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dy_sigma'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dy_sigma']['units'] = "meters/meters" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dy_sigma']['contentType'] = "qualityInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dy_sigma']['long_name'] = "Sigma of Across Track Slope" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dy_sigma']['description'] = ("Propagated error in " "the across-track segment slope calculated from segment fits to weak and strong beam") IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['dh_fit_dy_sigma']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- number of photons in fit IS2_atl03_fit[gtx]['land_ice_segments']['fit_statistics']['n_fit_photons'] = Segment_N_Fit[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['fit_statistics']['n_fit_photons'] = Segment_N_Fit[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_fit_photons'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_fit_photons']['units'] = "1" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_fit_photons']['contentType'] = "physicalMeasurement" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_fit_photons']['long_name'] = "Number of Photons in Fit" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_fit_photons']['description'] = ("Number of PEs used to " "determine mean surface height in the iterative surface fit") IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_fit_photons']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- size of the window used in the fit IS2_atl03_fit[gtx]['land_ice_segments']['fit_statistics']['w_surface_window_final'] = Segment_Window[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['fit_statistics']['w_surface_window_final'] = Segment_Window[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['w_surface_window_final'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['w_surface_window_final']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['w_surface_window_final']['contentType'] = "physicalMeasurement" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['w_surface_window_final']['long_name'] = "Surface Window Width" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['w_surface_window_final']['description'] = ("Width of the surface " "window, top to bottom") IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['w_surface_window_final']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- signal-to-noise ratio IS2_atl03_fit[gtx]['land_ice_segments']['fit_statistics']['snr'] = Segment_SNR[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['fit_statistics']['snr'] = Segment_SNR[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['snr'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['snr']['units'] = "1" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['snr']['contentType'] = "physicalMeasurement" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['snr']['long_name'] = "SNR" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['snr']['description'] = ("Signal-to-noise " "ratio in the final refined window") IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['snr']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- robust dispersion estimator IS2_atl03_fit[gtx]['land_ice_segments']['fit_statistics']['h_robust_sprd'] = Segment_RDE[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['fit_statistics']['h_robust_sprd'] = Segment_RDE[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['h_robust_sprd'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['h_robust_sprd']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['h_robust_sprd']['contentType'] = "qualityInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['h_robust_sprd']['long_name'] = "Robust Spread" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['h_robust_sprd']['description'] = ("RDE of misfit " "between PE heights and the along-track segment fit") IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['h_robust_sprd']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- number of iterations for fit IS2_atl03_fit[gtx]['land_ice_segments']['fit_statistics']['n_fit_iterations'] = Segment_Iterations[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['fit_statistics']['n_fit_iterations'] = Segment_Iterations[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_fit_iterations'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_fit_iterations']['units'] = "1" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_fit_iterations']['contentType'] = "modelResult" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_fit_iterations']['long_name'] = "Number of Iterations used in Fit" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_fit_iterations']['description'] = ("Number of Iterations when " "determining the mean surface height") IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_fit_iterations']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- signal source selection IS2_atl03_fit[gtx]['land_ice_segments']['fit_statistics']['signal_selection_source'] = Segment_Source[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['fit_statistics']['signal_selection_source'] = Segment_Source[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['signal_selection_source'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['signal_selection_source']['units'] = "1" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['signal_selection_source']['contentType'] = "qualityInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['signal_selection_source']['long_name'] = "Signal Selection Source" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['signal_selection_source']['description'] = ("Indicates the last " "algorithm attempted to select the signal for fitting. 1=Signal selection succeeded using ATL03 detected PE; 2=Signal " "selection failed using ATL03 detected PE but succeeded using all flagged ATL03 PE; 3=Signal selection failed using " "all flagged ATL03 PE, but succeeded using the backup algorithm; 4=All signal-finding strategies failed.") IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['signal_selection_source']['flag_values'] = [1,2,3,4] IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['signal_selection_source']['valid_min'] = 1 IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['signal_selection_source']['valid_max'] = 4 IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['signal_selection_source']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- number potential segment pulses IS2_atl03_fit[gtx]['land_ice_segments']['fit_statistics']['n_seg_pulses'] = Segment_Pulses[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['fit_statistics']['n_seg_pulses'] = Segment_Pulses[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_seg_pulses'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_seg_pulses']['units'] = "1" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_seg_pulses']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_seg_pulses']['long_name'] = "Number potential segment pulses" IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_seg_pulses']['description'] = ("The number of pulses " "potentially included in the segment") IS2_atl03_attrs[gtx]['land_ice_segments']['fit_statistics']['n_seg_pulses']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- ground track variables IS2_atl03_fit[gtx]['land_ice_segments']['ground_track'] = {} IS2_atl03_fill[gtx]['land_ice_segments']['ground_track'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['Description'] = ("The ground_track group " "contains parameters describing the GT and RGT for each land ice segment, as well as angular " "information about the beams.") IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['data_rate'] = ("Data within this group " "are stored at the land_ice_segments segment rate.") #-- along-track X coordinates of segment fit IS2_atl03_fit[gtx]['land_ice_segments']['ground_track']['x_atc'] = Segment_X_atc[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['ground_track']['x_atc'] = Segment_X_atc[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['x_atc'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['x_atc']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['x_atc']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['x_atc']['long_name'] = "X Along Track" IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['x_atc']['description'] = ("The along-track " "x-coordinate of the segment, measured parallel to the RGT, measured from the ascending node of the equatorial " "crossing of a given RGT.") IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['x_atc']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- along-track Y coordinates of segment fit IS2_atl03_fit[gtx]['land_ice_segments']['ground_track']['y_atc'] = Segment_Y_atc[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['ground_track']['y_atc'] = Segment_Y_atc[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['y_atc'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['y_atc']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['y_atc']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['y_atc']['long_name'] = "Y Along Track" IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['y_atc']['description'] = ("The along-track " "y-coordinate of the segment, relative to the RGT, measured along the perpendicular to the RGT, " "positive to the right of the RGT.") IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['y_atc']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- along-track X coordinate spread of points used in segment fit IS2_atl03_fit[gtx]['land_ice_segments']['ground_track']['x_spread'] = Segment_X_spread[gtx] IS2_atl03_fill[gtx]['land_ice_segments']['ground_track']['x_spread'] = Segment_X_spread[gtx].fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['x_spread'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['x_spread']['units'] = "meters" IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['x_spread']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['x_spread']['long_name'] = "X Along Track Spread" IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['x_spread']['description'] = ("The spread of " "along-track x-coordinates for points used in the segment fit. Coordinates measured parallel to the " "RGT, measured from the ascending node of the equatorial crossing of a given RGT.") IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['x_spread']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- elevation fv = fileID[gtx]['geolocation']['ref_elev'].attrs['_FillValue'] ref_elev = np.ma.array(fileID[gtx]['geolocation']['ref_elev'][:], fill_value=fv) ref_elev.mask = ref_elev.data == ref_elev.fill_value segment_ref_elev = (ref_elev[1:] + ref_elev[0:-1])/2.0 segment_ref_elev.data[segment_ref_elev.mask] = segment_ref_elev.fill_value IS2_atl03_fit[gtx]['land_ice_segments']['ground_track']['ref_elev'] = segment_ref_elev IS2_atl03_fill[gtx]['land_ice_segments']['ground_track']['ref_elev'] = segment_ref_elev.fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['ref_elev'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['ref_elev']['units'] = "radians" IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['ref_elev']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['ref_elev']['long_name'] = "Elevation" IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['ref_elev']['description'] = ("Elevation of the " "unit pointing vector for the reference photon in the local ENU frame in radians. The angle is measured " "from East-North plane and positive towards Up") IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['ref_elev']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- azimuth fv = fileID[gtx]['geolocation']['ref_azimuth'].attrs['_FillValue'] ref_azimuth = np.ma.array(fileID[gtx]['geolocation']['ref_azimuth'][:], fill_value=fv) ref_azimuth.mask = ref_azimuth.data == ref_azimuth.fill_value segment_ref_azimuth = (ref_azimuth[1:] + ref_azimuth[0:-1])/2.0 segment_ref_azimuth.data[segment_ref_azimuth.mask] = segment_ref_azimuth.fill_value IS2_atl03_fit[gtx]['land_ice_segments']['ground_track']['ref_azimuth'] = segment_ref_azimuth IS2_atl03_fill[gtx]['land_ice_segments']['ground_track']['ref_azimuth'] = segment_ref_azimuth.fill_value IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['ref_azimuth'] = {} IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['ref_azimuth']['units'] = "radians" IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['ref_azimuth']['contentType'] = "referenceInformation" IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['ref_azimuth']['long_name'] = "Azimuth" IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['ref_azimuth']['description'] = ("Azimuth of the " "unit pointing vector for the reference photon in the local ENU frame in radians. The angle is measured " "from North and positive towards East") IS2_atl03_attrs[gtx]['land_ice_segments']['ground_track']['ref_azimuth']['coordinates'] = \ "../segment_id ../delta_time ../latitude ../longitude" #-- parallel h5py I/O does not support compression filters at this time if (comm.rank == 0): #-- use default output file name and path if not output_file: args = (SUB,'ATL06',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(ATL03_dir,file_format.format(*args)) #-- write to HDF5 file HDF5_ATL03_write(IS2_atl03_fit, IS2_atl03_attrs, COMM=comm, VERBOSE=VERBOSE, INPUT=[ATL03_file,ATL09_file], FILL_VALUE=IS2_atl03_fill, CLOBBER=True, FILENAME=output_file) #-- change the permissions level to MODE os.chmod(output_file, MODE) #-- close the input ATL03 file fileID.close() #-- PURPOSE: read ICESat-2 ATL09 HDF5 data file for specific variables def read_HDF5_ATL09(FILENAME, pfl, S, ATTRIBUTES=True, VERBOSE=False, COMM=None): #-- Open the HDF5 file for reading fileID = h5py.File(FILENAME, 'r', driver='mpio', comm=COMM) print(FILENAME) if VERBOSE and (COMM.rank == 0) else None #-- allocate python dictionaries for ICESat-2 ATL09 variables and attributes IS2_atl09_mds = {} IS2_atl09_attrs = {} #-- read profile reported for the ATLAS strong beams within the file IS2_atl09_mds[pfl] = dict(high_rate={}) #-- extract segment_id for mapping ATL09 atmospheric parameters to ATL03 segment_id = fileID[pfl]['high_rate']['segment_id'][:] #-- Calibrated Attenuated Backscatter at 25 hz high_rate_keys = ['aclr_true','bsnow_con','bsnow_dens','bsnow_h', 'bsnow_h_dens','bsnow_od','bsnow_psc','cloud_flag_asr','cloud_flag_atm', 'cloud_fold_flag','column_od_asr','column_od_asr_qf','msw_flag', 'snow_ice','solar_azimuth','solar_elevation','surf_refl_true'] #-- number of output ATL03 segments n_seg = len(S) #-- parallel indices for filling variables ii = np.arange(COMM.rank,n_seg,COMM.size) #-- extract variables of interest and map to ATL03 segments for key in high_rate_keys: val = np.copy(fileID[pfl]['high_rate'][key][:]) fint = scipy.interpolate.interp1d(segment_id, val, kind='nearest', fill_value='extrapolate') IS2_atl09_mds[pfl]['high_rate'][key] = np.zeros((n_seg),dtype=val.dtype) IS2_atl09_mds[pfl]['high_rate'][key][ii] = fint(S[ii]).astype(val.dtype) #-- Getting attributes of included variables if ATTRIBUTES: #-- Getting attributes of IS2_atl09_mds profile variables IS2_atl09_attrs[pfl] = dict(high_rate={}) #-- Global Group Attributes for att_name,att_val in fileID[pfl].attrs.items(): IS2_atl09_attrs[pfl][att_name] = att_val #-- Variable Attributes for key in high_rate_keys: IS2_atl09_attrs[pfl]['high_rate'][key] = {} for att_name,att_val in fileID[pfl]['high_rate'][key].attrs.items(): IS2_atl09_attrs[pfl]['high_rate'][key][att_name] = att_val #-- Global File Attributes if ATTRIBUTES: for att_name,att_val in fileID.attrs.items(): IS2_atl09_attrs[att_name] = att_val #-- Closing the HDF5 file fileID.close() #-- Return the datasets and variables return (IS2_atl09_mds,IS2_atl09_attrs) #-- PURPOSE: outputting the reduced and corrected ICESat-2 data to HDF5 def HDF5_ATL03_write(IS2_atl03_data, IS2_atl03_attrs, COMM=None, INPUT=None, FILENAME='', FILL_VALUE=None, CLOBBER=True, VERBOSE=False): #-- setting HDF5 clobber attribute if CLOBBER: clobber = 'w' else: clobber = 'w-' #-- open output HDF5 file fileID = h5py.File(FILENAME, clobber)#, driver='mpio', comm=COMM) print(FILENAME) if VERBOSE and (COMM.rank == 0) else None #-- create HDF5 records h5 = {} # #-- ICESat-2 spacecraft orientation at time # fileID.create_group('orbit_info') # h5['orbit_info'] = {} # for k,v in IS2_atl03_data['orbit_info'].items(): # #-- Defining the HDF5 dataset variables # val = 'orbit_info/{0}'.format(k) # h5['orbit_info'][k] = fileID.create_dataset(val, np.shape(v), data=v, # dtype=v.dtype, compression='gzip') # #-- add HDF5 variable attributes # for att_name,att_val in IS2_atl03_attrs['orbit_info'][k].items(): # h5['orbit_info'][k].attrs[att_name] = att_val #-- information ancillary to the data product #-- 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) h5['ancillary_data'] = {} for k in ['atlas_sdp_gps_epoch','data_end_utc','data_start_utc','end_cycle', 'end_geoseg','end_gpssow','end_gpsweek','end_orbit','end_region', 'end_rgt','granule_end_utc','granule_start_utc','release','start_cycle', 'start_geoseg','start_gpssow','start_gpsweek','start_orbit','start_region', 'start_rgt','version']: #-- Defining the HDF5 dataset variables v = IS2_atl03_data['ancillary_data'][k] val = 'ancillary_data/{0}'.format(k) h5['ancillary_data'][k] = fileID.create_dataset(val, np.shape(v), data=v, dtype=v.dtype) #-- add HDF5 variable attributes for att_name,att_val in IS2_atl03_attrs['ancillary_data'][k].items(): h5['ancillary_data'][k].attrs[att_name] = att_val #-- land_ice_segments variable groups for each beam GROUPS=['fit_statistics','geophysical','ground_track','dem','bias_correction'] #-- write each output beam for gtx in ['gt1l','gt1r','gt2l','gt2r','gt3l','gt3r']: fileID.create_group(gtx) fileID['ancillary_data'].create_group(gtx) #-- add HDF5 group attributes for beam for att_name in ['Description','atlas_pce','atlas_beam_type', 'groundtrack_id','atmosphere_profile','atlas_spot_number', 'sc_orientation']: fileID[gtx].attrs[att_name] = IS2_atl03_attrs[gtx][att_name] #-- add transmit pulse shape and dead time parameters h5['ancillary_data'][gtx] = {} for k,v in IS2_atl03_data['ancillary_data'][gtx].items(): #-- attributes attrs = IS2_atl03_attrs['ancillary_data'][gtx][k] #-- Defining the HDF5 dataset variables val = 'ancillary_data/{0}/{1}'.format(gtx,k) h5['ancillary_data'][gtx][k] = fileID.create_dataset(val, np.shape(v), data=v, dtype=v.dtype) #-- add HDF5 variable attributes for att_name,att_val in attrs.items(): h5['ancillary_data'][gtx][k].attrs[att_name] = att_val #-- create land_ice_segments group fileID[gtx].create_group('land_ice_segments') h5[gtx] = dict(land_ice_segments={}) for att_name in ['Description','data_rate']: att_val = IS2_atl03_attrs[gtx]['land_ice_segments'][att_name] fileID[gtx]['land_ice_segments'].attrs[att_name] = att_val #-- segment_id v = IS2_atl03_data[gtx]['land_ice_segments']['segment_id'] attrs = IS2_atl03_attrs[gtx]['land_ice_segments']['segment_id'] # #-- parallel indices for filling variables # pind = np.arange(COMM.rank,len(v),COMM.size) #-- Defining the HDF5 dataset variables val = '{0}/{1}/{2}'.format(gtx,'land_ice_segments','segment_id') h5[gtx]['land_ice_segments']['segment_id'] = fileID.create_dataset(val, np.shape(v), data=v, dtype=v.dtype, compression='gzip') # with h5[gtx]['land_ice_segments']['segment_ID'].collective: # h5[gtx]['land_ice_segments']['segment_id'][pind] = v[pind] #-- add HDF5 variable attributes for att_name,att_val in attrs.items(): h5[gtx]['land_ice_segments']['segment_id'].attrs[att_name] = att_val #-- geolocation, time and height variables for k in ['latitude','longitude','delta_time','h_li','h_li_sigma', 'sigma_geo_h','atl06_quality_summary']: #-- values and attributes v = IS2_atl03_data[gtx]['land_ice_segments'][k] attrs = IS2_atl03_attrs[gtx]['land_ice_segments'][k] fillvalue = FILL_VALUE[gtx]['land_ice_segments'][k] #-- Defining the HDF5 dataset variables val = '{0}/{1}/{2}'.format(gtx,'land_ice_segments',k) h5[gtx]['land_ice_segments'][k] = fileID.create_dataset(val, np.shape(v), data=v, dtype=v.dtype, fillvalue=fillvalue, compression='gzip') # with h5[gtx]['land_ice_segments'][k].collective: # h5[gtx]['land_ice_segments'][k][pind] = v[pind] #-- attach dimensions for dim in ['segment_id']: h5[gtx]['land_ice_segments'][k].dims.create_scale( h5[gtx]['land_ice_segments'][dim], dim) h5[gtx]['land_ice_segments'][k].dims[0].attach_scale( h5[gtx]['land_ice_segments'][dim]) #-- add HDF5 variable attributes for att_name,att_val in attrs.items(): h5[gtx]['land_ice_segments'][k].attrs[att_name] = att_val #-- fit statistics, geophysical corrections, geolocation and dem for key in GROUPS: fileID[gtx]['land_ice_segments'].create_group(key) h5[gtx]['land_ice_segments'][key] = {} for att_name in ['Description','data_rate']: att_val=IS2_atl03_attrs[gtx]['land_ice_segments'][key][att_name] fileID[gtx]['land_ice_segments'][key].attrs[att_name] = att_val for k,v in IS2_atl03_data[gtx]['land_ice_segments'][key].items(): #-- attributes attrs = IS2_atl03_attrs[gtx]['land_ice_segments'][key][k] fillvalue = FILL_VALUE[gtx]['land_ice_segments'][key][k] #-- Defining the HDF5 dataset variables val = '{0}/{1}/{2}/{3}'.format(gtx,'land_ice_segments',key,k) if fillvalue: h5[gtx]['land_ice_segments'][key][k] = \ fileID.create_dataset(val, np.shape(v), data=v, dtype=v.dtype, fillvalue=fillvalue, compression='gzip') else: h5[gtx]['land_ice_segments'][key][k] = \ fileID.create_dataset(val, np.shape(v), data=v, dtype=v.dtype, compression='gzip') # with h5[gtx]['land_ice_segments'][key][k].collective: # h5[gtx]['land_ice_segments'][key][k][pind] = v[pind] #-- attach dimensions for dim in ['segment_id']: h5[gtx]['land_ice_segments'][key][k].dims.create_scale( h5[gtx]['land_ice_segments'][dim], dim) h5[gtx]['land_ice_segments'][key][k].dims[0].attach_scale( h5[gtx]['land_ice_segments'][dim]) #-- add HDF5 variable attributes for att_name,att_val in attrs.items(): h5[gtx]['land_ice_segments'][key][k].attrs[att_name] = att_val #-- HDF5 file title fileID.attrs['featureType'] = 'trajectory' fileID.attrs['title'] = 'ATLAS/ICESat-2 Land Ice Height' fileID.attrs['summary'] = ('Estimates of the ice-sheet mean surface height ' 'relative to the WGS-84 ellipsoid, and ancillary parameters needed to ' 'interpret and assess the quality of these height estimates.') fileID.attrs['description'] = ('Land ice surface heights for each beam, ' 'along and across-track slopes calculated for beam pairs. All ' 'parameters are calculated for the same along-track increments for ' 'each beam and repeat.') date_created = datetime.datetime.today() fileID.attrs['date_created'] = date_created.isoformat() project = 'ICESat-2 > Ice, Cloud, and land Elevation Satellite-2' fileID.attrs['project'] = project platform = 'ICESat-2 > Ice, Cloud, and land Elevation Satellite-2' fileID.attrs['project'] = platform #-- add attribute for elevation instrument and designated processing level instrument = 'ATLAS > Advanced Topographic Laser Altimeter System' fileID.attrs['instrument'] = instrument fileID.attrs['source'] = 'Spacecraft' fileID.attrs['references'] = 'http://nsidc.org/data/icesat2/data.html' fileID.attrs['processing_level'] = '4' #-- add attributes for input ATL03 and ATL09 files fileID.attrs['input_files'] = ','.join([os.path.basename(i) for i in INPUT]) #-- find geospatial and temporal ranges lnmn,lnmx,ltmn,ltmx,tmn,tmx = (np.inf,-np.inf,np.inf,-np.inf,np.inf,-np.inf) for gtx in ['gt1l','gt1r','gt2l','gt2r','gt3l','gt3r']: lon = IS2_atl03_data[gtx]['land_ice_segments']['longitude'] lat = IS2_atl03_data[gtx]['land_ice_segments']['latitude'] delta_time = IS2_atl03_data[gtx]['land_ice_segments']['delta_time'] #-- setting the geospatial and temporal ranges lnmn = lon.min() if (lon.min() < lnmn) else lnmn lnmx = lon.max() if (lon.max() > lnmx) else lnmx ltmn = lat.min() if (lat.min() < ltmn) else ltmn ltmx = lat.max() if (lat.max() > ltmx) else ltmx tmn = delta_time.min() if (delta_time.min() < tmn) else tmn tmx = delta_time.max() if (delta_time.max() > tmx) else tmx #-- add geospatial and temporal attributes fileID.attrs['geospatial_lat_min'] = ltmn fileID.attrs['geospatial_lat_max'] = ltmx fileID.attrs['geospatial_lon_min'] = lnmn fileID.attrs['geospatial_lon_max'] = lnmx fileID.attrs['geospatial_lat_units'] = "degrees_north" fileID.attrs['geospatial_lon_units'] = "degrees_east" fileID.attrs['geospatial_ellipsoid'] = "WGS84" fileID.attrs['date_type'] = 'UTC' fileID.attrs['time_type'] = 'CCSDS UTC-A' #-- convert start and end time from ATLAS SDP seconds into Julian days atlas_sdp_gps_epoch=IS2_atl03_data['ancillary_data']['atlas_sdp_gps_epoch'] gps_seconds = atlas_sdp_gps_epoch + np.array([tmn,tmx]) time_leaps = count_leap_seconds(gps_seconds) time_julian = 2444244.5 + (gps_seconds - time_leaps)/86400.0 #-- convert to calendar date with convert_julian.py YY,MM,DD,HH,MN,SS = convert_julian(time_julian,FORMAT='tuple') #-- add attributes with measurement date start, end and duration tcs = datetime.datetime(np.int(YY[0]), np.int(MM[0]), np.int(DD[0]), np.int(HH[0]), np.int(MN[0]), np.int(SS[0]), np.int(1e6*(SS[0] % 1))) fileID.attrs['time_coverage_start'] = tcs.isoformat() tce = datetime.datetime(np.int(YY[1]), np.int(MM[1]), np.int(DD[1]), np.int(HH[1]), np.int(MN[1]), np.int(SS[1]), np.int(1e6*(SS[1] % 1))) fileID.attrs['time_coverage_end'] = tce.isoformat() fileID.attrs['time_coverage_duration'] = '{0:0.0f}'.format(tmx-tmn) #-- Closing the HDF5 file fileID.close() #-- run main program if __name__ == '__main__': main()