def check_time(self): # Check if there is are matching identifier columns 't', infer format ... either int or date (m/d/Y) # in all config files if ('t' not in list(self.sensor)) or ('t' not in list(self.ground)): t_sensor = None util.CodeError( 'Column named "t" is required in SENSOR, GROUND, and VEG_LAYERS config files, ' 'identifying time stamp for each row', self.log) else: try: t_sensor = [util.str2date(d) for d in self.sensor['t']] t_ground = [util.str2date(d) for d in self.ground['t']] except TypeError: t_sensor = [int(d) for d in self.sensor['t']] t_ground = [int(d) for d in self.ground['t']] if t_sensor != t_ground: util.CodeError( 'Time identifier column (t) does not match in SENSOR and GROUND config files', self.log) """ need to implement this!! """ if self.vegetation_cover: if ('t' not in list(self.veg_layers)): util.CodeError( 'Column named "t" is required in SENSOR, GROUND, and VEG_LAYERS config files, ' 'identifying time stamp for each row', self.log) return np.array(t_sensor)
def single_simulation(self, simulation): # Ground layer -------------------------------------------------------------------------------------------- if simulation['surface_type'] == 1: self.ground = ground.Soil(simulation, self.log) elif simulation['surface_type'] == 2: self.ground = ground.Water(simulation, self.log) elif simulation['surface_type'] == 3: self.ground = ground.Ice(simulation, self.log) else: util.CodeError( 'Ground Surface type not recognized. Must be integer: 1, 2, or 3', self.log) print self.ground.epsilon # Vegetation (potentially multi-layered) # Organize output if self.vegetation_cover: # need to implement! pass else: sigma0_dB = np.full((2, 2), -9999.) sigma0 = self.ground.back_matrix[:2, :2] * 4.0 * np.pi * np.cos( simulation['theta'], dtype=np.float32) valid_mask = (sigma0 > 0) sigma0_dB[valid_mask] = util.pow2db(sigma0[valid_mask]) print "sigma0_dB= ", sigma0_dB
def check_columns(self, dat, config_type): """ Check to see if the required parameters are included in datafile, and if so, check if all values are valid """ dat_columns = dat.columns.values required_params = self._params[config_type].keys() params_dict = util.merge_dictionaries([ self._params[p] for p in self._params ]) # collapse nested parameters dictionary if config_type == 'ground': if 'surface_type' in dat.columns.values: unique_surfaces = [ self._surface_types[s] for s in np.unique(dat['surface_type']) ] for surf in unique_surfaces: required_params += self.get_additional_inputs( surf, dat_columns) else: util.CodeError( 'GROUND input column "surface_type" is missing in config file.', self.log) elif config_type == 'veg_scatterers': required_params += self.get_additional_inputs( config_type, dat_columns) column_mask = np.in1d(required_params, dat_columns) if np.sum(column_mask) == len(required_params): for param in required_params: print param, dat[param].dtype values = dat[param][~np.isnan(dat[param])] if np.sum((values < params_dict[param][0]) | (values > params_dict[param][1])) > 0: util.CodeError( 'Invalid value detected in column named "' + param + '" in ' + config_type + ' config datafile', self.log) else: if self.verbose: print config_type + ': all "' + param + '" values = valid' else: util.CodeError( config_type + ' config file is missing the following required parameters: ' + str(np.array(required_params)[~column_mask]), self.log)
def check_rows_complete(self): """ Simple check for no NaNs in all parameters """ for p in list(self.sensor): if np.sum(~pd.notnull(self.sensor[p])) > 0: util.CodeError( 'Missing data detected in SENSOR config file for column: ' + p, self.log) for p in list(self.ground): if np.sum(~pd.notnull(self.ground[p])) > 0: util.CodeError( 'Missing data detected in GROUND config file for column: ' + p, self.log) if self.vegetation_cover: for p in list(self.veg_layers): if np.sum(~pd.notnull(self.veg_layers[p])) > 0: util.CodeError( 'Missing data detected in VEG_LAYERS config file for column: ' + p, self.log) return True
def get_relevant_ground_params(self, dat): """ Check ground config data if required parameters for specified ground types are present """ if 'surface_type' in dat.columns.values: unique_surfaces = [ self._surface_types[s] for s in np.unique(dat['surface_type']) ] else: util.CodeError( 'GROUND parameter "surface_type" is missing from config file', self.log)
def estimate_gauss_IN_IS(self, apq, bpq, cpq, qx, qy, qz, qt, s, l): """ Subroutine that numerically computes the IN and IS terms for a Gaussian distribution of surface height and Gaussian correlation function Estimate uses truncated summation instead of numerical integration """ IN = np.zeros((2, 2, 2, 2), dtype=np.complex64) IS = np.zeros((2, 2, 2, 2), dtype=np.complex64) nmax = 100 prec = 1.0e-6 # Summation for IN and IS, using Gaussian correlation function sum_value = 0. term = 1. for n in xrange(1, nmax + 1): chk = np.abs(sum_value) xn = np.float(n) term = (term / xn) * (qz * s)**2 xint = np.exp(-(qt * l)**2 / (4. * xn)) / xn add = term * xint sum_value += add if np.abs(add) < (prec * chk): break else: if n == nmax: util.CodeError( 'Truncated Gaussian summation in Geometric Optics model is not making good ' 'progress ... please check parameters', self.log) l2 = l * l tn = np.pi * l2 * np.exp(-(qz * s)**2) ts = -tn / qz sumn = tn * sum_value sumsx = ts * qx * sum_value sumsy = ts * qy * sum_value # Multiply by polarization dependent coefficients and set up matrix # i1 = P, i2 = Q, i3 = M, i4 = N for i1 in xrange(2): for i2 in xrange(2): for i3 in xrange(2): for i4 in xrange(2): IN[i1, i2, i3, i4] = sumn * apq[i1, i2] * np.conj(apq[i3, i4]) IS[i1, i2, i3, i4] = sumsx * (bpq[i1, i2] * np.conj(apq[i3, i4]) + apq[i1, i2] * np.conj(bpq[i3, i4])) + \ sumsy * (cpq[i1, i2] * np.conj(apq[i3, i4]) + apq[i1, i2] * np.conj(cpq[i3, i4])) return IN, IS
def __init__(self, simulation_mode, vegetation_cover=False, input_dir=None, output_dir=None, overwrite_output=True): self.simulation_mode = simulation_mode self.vegetation_cover = vegetation_cover ########################################################################################################### # Configure input directory, check if necessary input files exist ########################################################################################################### if input_dir is None: # if no input dir is specified, will default to input dir found in mimics source directory self.input_dir = os.path.join(self._src_path, '../input') else: self.input_dir = input_dir if self.vegetation_cover: self._required_inputs += ['veg_layers.csv', 'veg_scatterers.csv'] required_inputs = [ os.path.join(self.input_dir, i) for i in self._required_inputs ] for req_in in required_inputs: if not os.path.exists(req_in): util.CodeError( 'Required input file [' + os.path.split(req_in)[-1] + '] does not exist in input ' 'directory. Check if selected input directory is correct and check contents' ) ########################################################################################################### # Set up output directory ########################################################################################################### if output_dir is None: # if no output dir is specified, will create one in source directory -- will overwrite any existing one self.output_dir = os.path.join(self._src_path, '../output') else: self.output_dir = output_dir if os.path.exists(self.output_dir): if overwrite_output: shutil.rmtree( self.output_dir) # Delete existing output directory os.makedirs(self.output_dir ) # Create a new, empty directory with same path else: util.CodeError( '"output" directory exists. Either set "overwrite_output" to True ' '-- or -- specify a custom new output directory with "output_dir"' ) else: os.makedirs(self.output_dir) ########################################################################################################### # Set up logging & configurations ########################################################################################################### # Create a logfile self.log = open(os.path.join(self.output_dir, 'logfile.txt'), 'a') # Load configurations if self.simulation_mode == 'snapshot': self.config = config.Snapshot(input_dir=self.input_dir, veg_cover=self.vegetation_cover, logfile=self.log) elif self.simulation_mode == 'timeseries': self.config = config.Timeseries(input_dir=self.input_dir, veg_cover=self.vegetation_cover, logfile=self.log)
def eps_soil(freq_GHz, temp, sand_frac, clay_frac, mv, rho_b=1.7, logfile=None): """ Relative Dielectric Constant of SOIL based on: Ulaby & Long "Microwave Radar and Radiometric Remote Sensing" 2014 (Section 4-8) Implemented from the following paper: M.C. Dobson, F.F. Ulaby, M.T. Hallikainen, M.A. El-Rayes, "Microwave dielectric behavior of wet soil - Part II: Dielectric mixing models," IEEE. Trans. Geosci. Remote Sens., vol. 23, no. 1, pp. 35-46, 1985. Method computes the real and imaginary parts of the relative dielectric constant of soil at a given temperature 0 < t < 40C, frequency, volumetric moisture content, soil bulk density, sand and clay fractions. eps_soil = eps_r - j*eps_i Parameters: freq_GHz: frequency, GHz temp: temperature, degrees C sand_frac: sand fraction, 0 - 1. clay_frac: clay fraction, 0 - 1. mv: volumetric water content of soil, 0 - 1. rho_b: bulk density, g/cm3 (typical value is 1.7 g/cm3) Returns: eps_r: real part of dielectric constant eps_i: imaginary part of dielectric constant """ # Non-frozen water in soil if temp > 40: util.CodeError( "Soil water temperature is too hot! Must be less than 40 C", logfile) elif temp > 0: freq_hz = freq_GHz * 1.0e9 # convert GHz to Hz alpha = 0.65 # eq: 4.68a, optimized coefficient beta1 = 1.27 - 0.519 * sand_frac - 0.152 * clay_frac # eq: 4.68b, optimized coefficient beta2 = 2.06 - 0.928 * sand_frac - 0.255 * clay_frac # eq: 4.68c, optimized coefficient # Effective conductivity, empirically derived ----------------------------------------------------------------- if freq_GHz > 1.3: sigma_s = -1.645 + 1.939 * rho_b - 2.256 * sand_frac + 1.594 * clay_frac # eq. 4.68d elif (freq_GHz >= 0.3) and (freq_GHz <= 1.3): sigma_s = 0.0467 + 0.22 * rho_b - 0.411 * sand_frac + 0.661 * clay_frac # eq. 4.70 else: util.CodeError( "Selected frequency is not supported in dielectric constant calculation.", logfile) # Dielectric constant of pure water ---------------------------------------------------------------------------- epsw_inf = 4.9 # eq: 4.15, high-frequency limit of free water dieletric constant # Static dielectric constant (at f = 0), dimensionless; eq. 4.18, Klein & Swift 1977 epsw_0 = 88.045 - 0.4147 * temp + 6.295e-4 * np.power( temp, 2) + 1.075e-5 * np.power(temp, 3) # Relaxation time consant (s) of pure water; eq. 4.16 tau_w = (1.1109e-10 - 3.824e-12 * temp + 6.938e-14 * np.power(temp, 2) - 5.096e-16 * np.power(temp, 3)) / \ (2 * np.pi) # dielectric constant of water, real & imaginary parts; single Debye model, section 4-1 + addded conductivity term epsw_r = epsw_inf + (epsw_0 - epsw_inf) / ( 1. + np.power(2. * np.pi * freq_hz * tau_w, 2)) # eq. 4.67a epsw_i = (2. * np.pi * freq_hz * tau_w * (epsw_0 - epsw_inf)) / \ (1. + np.power(2. * np.pi * freq_hz * tau_w, 2)) + (2.65 - rho_b) / (2.65 * mv) * \ (sigma_s / (2. * np.pi * util.CONSTANTS['eps0'] * freq_hz)) # eq. 4.67b # Dielectric constant of soil ---------------------------------------------------------------------------------- eps_r = np.power((1 + 0.66 * rho_b + np.power(mv, beta1) * np.power(epsw_r, alpha) - mv), (1 / alpha)) # eq. 4.66a eps_i = np.power(mv, beta2) * epsw_i # eq. 4.66b else: # frozen soil util.CodeError("Frozen soils are not currently supported by model", logfile) #eps_r = 3. + mv * (20. + (2./3.) * temp) # from Kyle's code, dielectric.f, ln 1203-04 #eps_i = -1. * (1. + temp / 50.) * 2. * mv / 0.15 return eps_r, eps_i
def backscatter(self): util.CodeError( 'Attempt to calculate backscatter of general Ground class -- MUST only use subclass', self.log)