예제 #1
0
    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)
예제 #2
0
    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
예제 #3
0
    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)
예제 #4
0
    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
예제 #5
0
    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)
예제 #6
0
    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
예제 #7
0
    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)
예제 #8
0
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
예제 #9
0
 def backscatter(self):
     util.CodeError(
         'Attempt to calculate backscatter of general Ground class -- MUST only use subclass',
         self.log)