Esempio n. 1
0
def process_coherent_layers(snowpack, emmodel_list, sensor):

    effective_permittivity = [em.effective_permittivity() for em in emmodel_list]
    phase = [sensor.wavenumber * np.sqrt(eps_eff).real * lay.thickness for lay, eps_eff in zip(snowpack.layers, effective_permittivity)]

    coherent_layers = np.array(phase) < 3 * np.pi / 4

    if not np.any(coherent_layers):
        return snowpack, emmodel_list

    snowpack = snowpack.copy()

    if coherent_layers[-1]:
        raise SMRTError("The last layer is coherent, this is not supported")

    print("process_coherent_layers (in dev, use for testing only) # coherent layers:", np.sum(coherent_layers))

    for l in np.flatnonzero(coherent_layers[:-1])[::-1]:  # reverse the processing to safely delete the snowpack layer and interface
        print("coherent layer:", l)
        if coherent_layers[l - 1]:
            raise SMRTError("Two sucessive layers are coherent, this is not yet supported")
        # create a coherent interface
        coherent_interface = CoherentFlat(snowpack.interfaces[l:l + 2], snowpack.layers[l], effective_permittivity[l])
        # set the next interface to coherent
        snowpack.interfaces[l + 1] = coherent_interface
        # delete the layer to be deleted
        snowpack.delete(l)  # delete layer and interface l
        emmodel_list.pop(l)

    return snowpack, emmodel_list
Esempio n. 2
0
    def sel(self, **kwargs):

        if 'mode' in kwargs:
            if self.mtype == "dense5":

                if self.values.shape[0] == 3 and kwargs[
                        'auto_reduce_npol'] and kwargs['mode'] == 0:
                    ## 3pol->2pol
                    return smrt_matrix(self.values[0:2, 0:2,
                                                   kwargs['mode'], :, :],
                                       mtype='dense4')
                else:
                    return smrt_matrix(self.values[:, :, kwargs['mode'], :, :],
                                       mtype='dense4')

            elif self.mtype == "diagonal5":
                if self.values.shape[0] == 3 and kwargs[
                        'auto_reduce_npol'] and kwargs['mode'] == 0:
                    ## 3pol->2pol
                    return smrt_matrix(self.values[0:2, kwargs['mode'], :],
                                       mtype='diagonal4')
                else:
                    return smrt_matrix(self.values[:, kwargs['mode'], :],
                                       mtype='diagonal4')

            elif self.mtype == "dense4":
                raise SMRTError("Dense4 matrix can not be selected by mode")

            elif self.mtype == "diagonal4":
                raise SMRTError("Diagonal4 matrix can not be selected by mode")
            else:
                raise NotImplementedError
        else:
            raise SMRTError("Currently only selection by mode is implemented")
Esempio n. 3
0
def common_amsr(sensor_name, frequency_dict, channel=None, frequency=None, polarization=None, theta=55):

    if isinstance(channel, Sequence) and not isinstance(channel, str):
        if frequency is not None:
            raise SMRTError("Either channel or frequency should be given. Mixing both arguments is not understood.")
        return SensorList([common_amsr(sensor_name, frequency_dict, channel=c, frequency=None,
                                         polarization=polarization, theta=theta) for c in channel])

    if channel is not None:

        if len(channel) == 3:
            polarization = channel[2]
        else:
            polarization = ['H', 'V']

        fch = channel[0:2]

        try:
            frequency = frequency_dict[fch]
        except KeyError:
            raise SMRTError("%s channel frequency not recognized. Expected one of: %s" % (sensor_name, ", ".join(frequency_dict.keys())))

    if frequency is None:
        frequency = sorted(set(frequency_dict.values()))
        polarization = ['H', 'V']

    sensor = Sensor(frequency, None, theta, None, None, polarization, channel)

    return sensor
Esempio n. 4
0
    def sel(self, **kwargs):

        if 'mode' in kwargs:
            mode = kwargs['mode']
            # 3pol->2pol
            if self.values.shape[0] == 3 and kwargs[
                    'auto_reduce_npol'] and mode == 0:
                pola = slice(0, 2)
            else:
                pola = slice(None)
            if self.mtype == "dense5":
                return smrt_matrix(self.values[pola, pola, mode, :, :],
                                   mtype='dense4')
            elif self.mtype == "diagonal5":
                return smrt_matrix(self.values[pola, mode, :],
                                   mtype='diagonal4')

            elif self.mtype == "dense4":
                raise SMRTError("Dense4 matrix can not be selected by mode")

            elif self.mtype == "diagonal4":
                raise SMRTError("Diagonal4 matrix can not be selected by mode")
            else:
                raise NotImplementedError
        else:
            raise SMRTError("Currently only selection by mode is implemented")
Esempio n. 5
0
def import_class(scope, modulename, classname=None):
    """Import the modulename and return either the class named "classname" or the first class defined in the module if classname is None.

    :param scope: scope where to search for the module.
    :param modulename: name of the module to load.
    :param classname: name of the class to read from the module.
"""

    if (".." in modulename) or (modulename[0] == '.'):
        raise SMRTError("modulename error. Relative import is not allowed")

    modulename = scope + "." + modulename

    # add user_directories
    for pkg in user_plugin_package:
        #print(pkg + "." + modulename)
        res = do_import_class(pkg + "." + modulename, classname)
        if res is not None:
            return res

    # the last case, search in the smrt package
    res = do_import_class("smrt." + modulename, classname)
    if res is None:
        if classname is None:
            msg = "Unable to find the module '%s'." % modulename
        else:
            msg = "Unable to find the module '%s' to import the class '%s'." % (
                modulename, classname)
        raise SMRTError(msg)

    return res
Esempio n. 6
0
def make_soil(substrate_model, permittivity_model, temperature, moisture=None,
              sand=None, clay=None, drymatter=None, **kwargs):

    """ Construct a soil instance based on a given surface electromagnetic model, a permittivity model and parameters 

    :param substrate_model: name of substrate model, can be a class or a string. e.g. fresnel, wegmuller...
    :param permittivity_model: permittivity_model to use. Can be a name ("hut_epss" or "dobson85"), a function of frequency and temperature or a complex value.
    :param moisture: soil moisture in m:sup:`3` m:sup:`-3` to compute the permittivity. This parameter is used depending on the permittivity_model.
    :param sand: soil relative sand content. This parameter is used or not depending on the permittivity_model.
    :param clay: soil relative clay content. This parameter is used or not depending on the permittivity_model.
    :param drymatter: soil content in dry matter in kg m:sup:`-3`. This parameter is used or not depending on the permittivity_model.

    :param **kwargs: geometrical parameters depending on the substrate_model. Refer to the document of each model to see the list of required and optional parameters.
    Usually, it is roughness_rms, corr_length, ...

    **Usage example:**

    ::
        TOTEST: bottom = substrate.make('Flat', permittivity_model=complex('6-0.5j'))
        TOTEST:  bottom = substrate.make('Wegmuller', permittivity_model='soil', roughness_rms=0.25, moisture=0.25)

    """

    # process the permittivity_model argument
    if isinstance(permittivity_model, str):
        if sand is None or clay is None or drymatter is None:
            raise SMRTError("The parameters sand, clay and drymatter must be set")

        if permittivity_model == "hut_epss":
            # return soil_dielectric_constant_hut after setting the parameters
            permittivity_model = partial(soil_dielectric_constant_hut, SM=moisture, sand=sand, clay=clay, dm_rho=drymatter)

        elif permittivity_model == "dobson85":
            # return soil_dielectric_constant_dobson after setting the parameters
            permittivity_model = partial(soil_dielectric_constant_dobson, SM=moisture, S=sand, C=clay)
        else:
            raise SMRTError("The permittivity model '%s' is not recongized" % permittivity_model)
    else:
        if isinstance(permittivity_model, Number):  # a constant value
            # create a function with 2 args that always return the same value
            permittivity_model = lambda frequency, temperature, cst=permittivity_model: cst
        elif not callable(permittivity_model):
            raise SMRTError("The permittivity_model argument is not of the accepted types."
                              "It must be a string with an implemented permittivity model name,"
                              " a number or a function with two arguments.")
        # check that other parameters are
        if moisture is not None or sand is not None or clay is not None or drymatter is not None:
            raise Warning("Setting moisture, clay, sand or drymatter when permittivity_model is a number or function is useless")


    # process the substrate_model argument
    if not isinstance(substrate_model, type):
        substrate_model = get_substrate_model(substrate_model)

    # create the instance
    return substrate_model(temperature, permittivity_model, **kwargs)
Esempio n. 7
0
def make_snowpack(thickness, microstructure_model, density,
                  interface=None,
                  surface=None,
                  substrate=None,
                  atmosphere=None,
                  **kwargs):
    """
    build a multi-layered snowpack. Each parameter can be an array, list or a constant value.

    :param thickness: thicknesses of the layers in meter (from top to bottom). The last layer thickness can be "numpy.inf"
    for a semi-infinite layer.
    :param microstructure_model: microstructure_model to use (e.g. sticky_hard_spheres or independent_sphere or exponential).
    :param surface: type of surface interface, flat/fresnel is the default.  If surface and interface are both set,
    the interface must be a constant refering to all the "internal" interfaces.
    :param interface: type of interface, flat/fresnel is the default. It is usually a string for the interfaces
    without parameters (e.g. Flat or Transparent) or is created with :py:func:`~smrt.core.interface.make_interface` in more complex cases.
    Interface can be a constant or a list. In the latter case, its length must be the same as the number of layers,
    and interface[0] refers to the surface interface.
    :param density: densities of the layers.
    :param substrate: set the substrate of the snowpack. Another way to add a substrate is to use the + operator
    (e.g. snowpack + substrate).
    :param **kwargs: All the other parameters (temperature, microstructure parameters, emmodel, etc.) are given as optional arguments
    (e.g. temperature=[270, 250]).
    They are passed for each layer to the function :py:func:`~smrt.inputs.make_medium.make_snow_layer`.
    Thus, the documentation of this function is the reference. It describes precisely the available parameters.
    The microstructure parameter(s) depend on the microstructure_model used and is documented in each microstructure_model module.

    e.g.::

        sp = make_snowpack([1, 10], "exponential", density=[200,300], temperature=[240, 250], corr_length=[0.2e-3, 0.3e-3])

"""

    sp = Snowpack(substrate=substrate, atmosphere=atmosphere)

    if not isinstance(thickness, collections.abc.Iterable):
        raise SMRTError("The thickness argument must be iterable, that is, a list of numbers, numpy array or pandas Series or DataFrame.")

    lib.check_argument_size(density, len(thickness), "density")
    lib.check_argument_size(kwargs, len(thickness))

    if surface is not None and lib.is_sequence(interface):
        raise SMRTError("Setting both 'surface' and 'interface' arguments is ambiguous when inteface is a list or any sequence.")

    for i, dz in enumerate(thickness):
        layer = make_snow_layer(dz, lib.get(microstructure_model, i, "microstructure_model"),
                                density=lib.get(density, i, "density"),
                                **lib.get(kwargs, i))

        # add the interface
        linterface = lib.get(interface, i, "interface") if (i > 0) or (surface is None) else surface
        sp.append(layer, interface=make_interface(linterface))

    return sp
Esempio n. 8
0
    def check_validity(self, ks, kl, eps_r):

        # check validity
        if ks > 3:
            raise SMRTError(
                "Warning, roughness_rms is too high for the given wavelength. Limit is ks < 3. Here ks=%g"
                % ks)

        if ks * kl > np.sqrt(eps_r):
            raise SMRTError(
                "Warning, roughness_rms or correlation_length are too high for the given wavelength."
                " Limit is ks * kl < sqrt(eps_r). Here ks*kl=%g and sqrt(eps_r)=%g"
                % (ks * kl, np.sqrt(eps_r)))
Esempio n. 9
0
    def __init__(self, mat, mtype=None):

        if mat is 0:
            self.values = np.float64(0.)  # 0, but can be used as a numpy thing
            self.mtype = "0"
        else:
            self.values = mat

            if mtype is None:
                if isinstance(mat, list) and len(mat) in [2, 3]:
                    # diagonal matrix
                    if len(mat[0].shape) == 2:
                        mtype = "diagonal5"
                    else:
                        mtype = "diagonal4"
                elif len(mat.shape) == 5:
                    mtype = "dense5"
                elif len(mat.shape) == 4:
                    mtype = "dense4"
                elif len(mat.shape) == 3:
                    mtype = "diagonal5"
                elif len(mat.shape) == 2:
                    mtype = "diagonal4"
                else:
                    raise SMRTError("Unsupported matrix size")
            self.mtype = mtype
Esempio n. 10
0
    def check_validity(self, ks, kl, eps_r):

        # check validity
        if ks > 3:
            raise SMRTError(
                "Warning, roughness_rms is too high for the given wavelength. Limit is ks < 3. Here ks=%g"
                % ks)
Esempio n. 11
0
def amsre(channel=None, frequency=None, polarization=None, theta=55):
    """ Configuration for AMSR-E sensor.

    This function can be used to simulate all 12 AMSR-E channels i.e. frequencies of 6.925, 10.65, 18.7, 23.8, 36.5 and 89 GHz
    at both polarizations H and V. Alternatively single channels can be specified with 3-character identifiers. 18 and 19 GHz can
    be used interchangably to represent 18.7 GHz, similarly either 36 and 37 can be used to represent the 36.5 GHz channel.

    :param channel: single channel identifier
    :type channel: 3-character string

    :returns: :py:class:`Sensor` instance

    **Usage example:**

    ::

        from smrt import sensor
        radiometer = sensor.amsre()  # Simulates all channels
        radiometer = sensor.amsre('36V')  # Simulates 36.5 GHz channel only
        radiometer = sensor.amsre('06H')  # 6.925 GHz channel

    """

    amsre_frequency_dict = {
        '06': 6.925e9,
        '10': 10.65e9,
        '18': 18.7e9,
        '23': 23.8e9,
        '36': 36.5e9,
        '89': 89e9
    }

    if channel is not None:

        if len(channel) == 3:
            polarization = channel[2]
        else:
            polarization = ['H', 'V']

        fch = channel[0:2]

        if fch == "19":  # optional
            fch = "18"  # optional
        if fch == "37":  # optional
            fch = "36"  # optional

        try:
            frequency = amsre_frequency_dict[fch]
        except KeyError:
            raise SMRTError(
                "AMSR-E channel frequency not recognized. Expected one of: 06, 10, 18 or 19, 23, 36 or 37, 89"
            )

    if frequency is None:
        frequency = sorted(amsre_frequency_dict.values())
        polarization = ['H', 'V']

    sensor = Sensor(frequency, None, theta, None, None, polarization)

    return sensor
Esempio n. 12
0
def decompose_channel(channel, lengths):

    if isinstance(channel,
                  Sequence) and not isinstance(channel, six.string_types):

        data = [decompose_channel(ch) for ch in channel]
        frequency, theta, polarization, polarization_inc = tuple(
            map(list, zip(*data)))  # transpose

    else:
        if len(channel) != sum(lengths):
            raise SMRTError("the channel has an incorrect length")
        if lengths[0] > 0:
            frequency = channel[0:lengths[0]]
        else:
            frequency = None

        if lengths[1] > 0:
            polarization = channel[lengths[0]]
            if lengths[1] == 2:
                polarization_inc = channel[lengths[0] + 1]
        else:
            polarization_inc = polarization = None

        if lengths[2] > 0:
            theta = float(channel[-lengths[2]:])
        else:
            theta = None

    return frequency, theta, polarization, polarization_inc
Esempio n. 13
0
def water_parameters(ice_type, **kwargs):
    """Make a semi-infinite water layer.

    :param ice_type: ice_type is used to determine if a saline or fresh water layer is added
    Optional arguments are 'water_temperature', 'water_salinity' and 'water_depth' of the water layer.
    """

    # prepare default
    if ice_type in ['firstyear', 'multiyear']:
        water_temperature = FREEZING_POINT - 1.8
        water_salinity = 0.032  # = 0.032kg/kg = 32PSU; somewhat arbitrary value, fresher than average ocean salinity, reflecting lower salinities in polar regions
    elif ice_type == 'fresh':
        water_temperature = FREEZING_POINT
        water_salinity = 0.
    else:
        raise SMRTError(
            "'medium' must be set to one of the following: True (default), 'ocean', 'fresh'. Additional optional arguments for function make_ice_column are 'water_temperature', 'water_salinity' and 'water_depth'."
        )

    water_depth = 10.  # arbitrary value of 10m thickness for the water layer, microwave absorption in water is usually high, so this represents an infinitely thick water layer

    # override the following variable if set
    WaterParameter = collections.namedtuple(
        "WaterParameter",
        ('water_temperature', 'water_salinity', 'water_permittivity_model'))

    wp = WaterParameter(water_temperature=kwargs.get('water_temperature',
                                                     water_temperature),
                        water_salinity=kwargs.get('water_salinity',
                                                  water_salinity),
                        water_permittivity_model=seawater_permittivity_klein76)
    return wp
Esempio n. 14
0
def soil_dielectric_constant_monpetit2008(frequency, temperature):
    """Soil dielectric constant formulation based on the formulation Montpetit et al. 2018. The formulation is only valid for below-frrezing point temperature.

Reference: Montpetit, B., Royer, A., Roy, A., & Langlois, A. (2018). In-situ passive microwave emission model parameterization of sub-arctic frozen organic soils. Remote Sensing of Environment, 205, 112–118. https://doi.org/10.1016/j.rse.2017.10.033

"""
    # from functools import partial
    # from smrt.inputs.make_soil import soil_dielectric_constant_dobson
    if temperature > 273.15:
        raise SMRTError(
            "soil_dielectric_constant_monpetit is not implemented for above freezing temperatures."
        )
        # moisture=0.2
        # sand=0.4
        # clay=0.3
        # return partial(soil_dielectric_constant_dobson, SM=moisture, S=sand, C=clay)
    # else:

    p = scipy.interpolate.interp1d(
        [10.65e9, 19e9, 37e9],
        [complex(3.18, 0.0061),
         complex(3.42, 0.0051),
         complex(4.47, 0.33)],
        fill_value="extrapolate")
    return p(frequency)
Esempio n. 15
0
def do_import_class(modulename, classname):

    # check the module
    #spec = importlib.util.find_spec(modulename)
    #if spec is None:
    #    return None

    # import the module

    try:
        module = importlib.import_module(modulename)
    except ModuleNotFoundError:
        return None

    if classname is None:  # search for the first class defined in the module
        for name, obj in inspect.getmembers(module, inspect.isclass):
            if obj.__module__ == modulename:  # the second condition check if the class was defined in this module
                classname = name
                break

    if classname is None:
        raise SMRTError("Unable to find a class in the module '%s'" %
                        modulename)

    # get the class
    return getattr(module, classname)
Esempio n. 16
0
def smap(mode, theta=40):
    """Configuration for the passive (mode=P) and active (mode=A) sensor on smap

        This function returns either a passive sensor at 1.4 GHz (L-band) sensor or an active sensor at 1.26 GHz. The incidence angle is 40°.

    """

    if mode == 'P':
        return passive(
            1.4e9,
            theta=theta,
            channel_map={pola: dict(polarization=pola)
                         for pola in 'HV'},
            name='smap')
    elif mode == 'A':
        return active(1.26e9,
                      theta=theta,
                      theta_inc=theta,
                      channel_map={
                          channel: dict(polarization=channel[1],
                                        polarization_inc=channel[0])
                          for channel in ['HH', 'VV', 'HV']
                      },
                      name='smap')
    else:
        raise SMRTError('mode must by A (active) or P (passive')
Esempio n. 17
0
def common_amsr(sensor_name,
                frequency_dict,
                channel=None,
                frequency=None,
                polarization=None,
                theta=55,
                name=None):

    if frequency is None:
        # take default values
        frequency = sorted(set(frequency_dict.values()))
    else:
        # recreate the frequency dict
        frequency_dict = {
            "%02i" % (freq * 1e9): freq
            for freq in np.atleast_1d(frequency)
        }

    if polarization is None:
        polarization = ['H', 'V']

    # create the channel map
    channel_map = {
        freq + pola: dict(frequency=frequency_dict[freq],
                          polarization=pola,
                          theta=theta)
        for freq in frequency_dict for pola in polarization
    }

    if channel is not None:
        if isinstance(channel, str):
            channel = [channel]

        # add H and V to channel's name if not present
        new_channel = []
        for ch in channel:
            if ch[-1] not in 'HV':
                new_channel += [ch + 'H', ch + 'V']
            else:
                new_channel += [ch]

        # take into account 18=19 and 36=37
        for ch in new_channel:
            if '18' in ch:
                channel_map[ch] = channel_map.pop('19' + ch[-1])
            if '36' in ch:
                channel_map[ch] = channel_map.pop('37' + ch[-1])

        try:
            channel_map = filter_channel_map(channel_map, new_channel)
        except KeyError:
            raise SMRTError("%s channel not recognized. Expected one of: %s" %
                            (sensor_name, ", ".join(frequency_dict.keys())))

    sensor = passive(channel_map=channel_map,
                     **extract_configuration(channel_map),
                     name=name)

    return sensor
Esempio n. 18
0
def compute_thickness_from_z(z):
    """Compute the thickness of layers given the elevation z. Whatever the sign of z, the order *MUST* be from the topmost layer to the
    lowermost.

Several situation are accepted and interpretated as follows:
- z is positive and decreasing. The first value is the height of the surface about the ground (z=0) and z represents the top elevation
of each layer. This is typical of the seasonal snowpack.
- z is negative and decreasing. The first value is the elevation of the bottom of the first layer with respect to the surface (z=0).
This is typical of a snowpack on ice-sheet.
- z is positive and increasing. The first value is the depth of the bottom of the first layer with respect to the surface.
This is typical of a snowpack on ice-sheet.
- other case, when z is not monoton or is increasing with negative value raises an error.

Because z indicate the top or the bottom of a layer depending whether z=0 is the ground or the surface,
the value 0 can never be in z. This raises an error.

"""
    order = (np.diff(z) < 0)
    if np.any(z == 0):
        raise SMRTError("z must not include 0")
    positive = z >= 0

    if np.all(order):
        # descending z
        if np.all(positive):
            # z >0, this is typically a seasonal snowpack. z= height above ground
            z = -np.append(z.values, 0)
        else:
            # z < 0, this is typically a permanent, deep snowpack, without ground reference. z is the depth from the surface.
            z = -np.insert(z.values, 0, 0)

    elif np.any(order):
        # ascending z
        if np.all(positive):
            # ascending z and z > 0, this is typically a permanent, deep snowpack, without ground reference.
            # z is the depth from the surface.
            z = np.insert(z.values, 0, 0)
        else:
            # this is unusual
            raise SMRTError("z is ascending and has negative values, which an ambiguous situation")

    else:
        raise SMRTError("The z argument is not sorted")

    return np.diff(z)
Esempio n. 19
0
def concat_results(result_list, coord):
    """Concatenate several results from :py:meth:`smrt.core.model.Model.run` (of type :py:class:`Result`) into a single result
    (of type :py:class:`Result`). This extends the number of dimension in the xarray hold by the instance. The new dimension
    is specified with coord

    :param result_list: list of results returned by :py:meth:`smrt.core.model.Model.run` or other functions.
    :param coord: a tuple (dimension_name, dimension_values) for the new dimension. Dimension_values must be a sequence or
    array with the same length as result_list.

    :returns: :py:class:`Result` instance

    """

    if isinstance(coord, tuple):
        dim_name, dim_value = coord

        index = pd.Index(dim_value, name=dim_name)
    elif isinstance(coord, pd.Index):
        index = coord
        if index.name is None:
            index.name = 'snowpack_index'  # hope this will not conflict with an existing column
    else:
        raise SMRTError('unknown type for the coord argument')

    ResultClass = type(result_list[0])
    if not all([type(result) == ResultClass for result in result_list]):
        raise SMRTError("The results are not all of the same type")

    # channel_map ?
    if any((res.channel_map != result_list[0].channel_map for res in result_list)):
        assert isinstance(coord, tuple)
        # different channel maps, it means we have different sensors. Merge de sensor maps.
        channel_map = {ch: dict(**r.channel_map[ch], dim_name=dv) for r, dv in zip(result_list, dim_value) for ch in r.channel_map}
    else:
        # all the channel maps are the same
        channel_map = result_list[0].channel_map

    data = xr.concat([result.data for result in result_list], index)
    other_data = {v: xr.concat([result.other_data[v] for result in result_list], index) for v in result_list[0].other_data}

    return ResultClass(data,
                       channel_map=channel_map,
                       other_data=other_data)
Esempio n. 20
0
 def Tb_as_dataframe(self, index_by_channel=False, **kwargs):
     """Return brightness temperature. Any parameter can be added to slice the results (e.g. frequency=37e9 or polarization='V').
      See xarray slicing with sel method (to document)"""
     tb = self.data.sel(**kwargs).to_dataframe(name='Tb')
     if not index_by_channel:
         return tb
     else:
         if 'channel' not in tb.index.names:
             raise SMRTError("No channel information is given in the result. Unable to index the result by channel.")
         return tb.set_index(np.array([str(channel) + str(pola) for channel, pola in tb.index]))
Esempio n. 21
0
    def sel_data(self, channel=None, return_backscatter=False, **kwargs):
        # this function allows selection as xarray.DataArray.sel and in addition by channel if a channel_map is defined.

        # ffilter the variables of channel_map[channel] that are effectively in self.data.dims
        # and apply them to the selector sel in addition to kwargs

        if channel is not None:
            kwargs.update({
                k: v
                for k, v in self.channel_map[channel].items()
                if k in self.data.dims
            })

        if return_backscatter:
            # get theta
            theta = kwargs.pop('theta', None)
            theta_inc = kwargs.pop('theta_inc', None)

            if theta is not None and theta_inc is not None:
                if not np.all(theta_inc == theta):
                    raise SMRTError(
                        'theta and theta_inc must be the same when returning backscatter'
                    )

            if theta is None:
                theta = theta_inc

            if theta is None:
                theta = self.data.theta_inc

            def select_theta(x, theta, **kwargs):
                # select by theta and deal with cases where theta is in the coords or not
                if 'theta' in x.coords:
                    return x.sel(theta=theta, theta_inc=theta, **kwargs)
                else:
                    return x.sel(theta_inc=theta, **kwargs)

            if lib.is_sequence(theta):
                # now select all the theta if it is a sequence
                x = xr.concat([
                    select_theta(self.data, t, drop=True, **kwargs)
                    for t in theta
                ], pd.Index(theta, 'theta_inc'))
            else:
                x = select_theta(self.data, theta, drop=True, **kwargs)

        else:
            x = self.data.sel(drop=True, **kwargs)

        if return_backscatter:
            x = (4 * np.pi * np.cos(np.deg2rad(theta))) * x
            return dB(x) if return_backscatter == "dB" else x
        else:
            return x
Esempio n. 22
0
def get(x, i, name=None):
    # function to take the i-eme value in an array or dict of array. Can deal with scalar as well. In this case, it repeats the value.

    if isinstance(x, str):
        return x
    elif isinstance(x, pd.DataFrame) or isinstance(x, pd.Series):
        if i >= len(x.values):
            raise SMRTError(
                "The array '%s' is too short compared to the thickness array" %
                name)
        return x.values[i]
    if isinstance(x, Sequence) or isinstance(x, np.ndarray):
        if i >= len(x):
            raise SMRTError(
                "The array '%s' is too short compared to the thickness array."
                % name)
        return x[i]
    elif isinstance(x, dict):
        return {k: get(x[k], i, k) for k in x}
    else:
        return x
Esempio n. 23
0
def register_package(pkg):
    global user_plugin_package

    # check that the package can be imported. It must have an __init__.py
    try:
        module = importlib.import_module(pkg)
    except ImportError as e:
        raise SMRTError(
            "The package must be in the the sys.path list and must contain a __init__.py file (even empty). The import error is %s"
            % str(e))

    user_plugin_package.insert(0, pkg)
Esempio n. 24
0
def maxwell_garnett(frac_volume,
                    e0,
                    eps,
                    depol_xyz=None,
                    inclusion_shape=None,
                    length_ratio=None):
    """ Calculates effective permittivity using Maxwell-Garnett equation.

    :param frac_volume: Fractional volume of snow
    :param e0: Permittivity of background (no default, must be provided)
    :param eps: Permittivity of scattering material (no default, must be provided)
    :param depol_xyz: [Optional] Depolarization factors, spherical isotropy is default. It is not taken into account here.
    :param length_ratio: Length_ratio. Used to estimate depolarization factors when they are not given.
    :param inclusion_shape: Assumption for shape(s) of brine inclusions. Can be a string for single shape, or a list/tuple/dict of strings for mixture of shapes. So far, we have the following shapes: "spheres" and "random_needles" (i.e. randomly-oriented elongated ellipsoidal inclusions). 
            If the argument is a dict, the keys are the shapes and the values are the mixing ratio. If it is a list, the mixing_ratio argument is required.

    :returns: random orientation effective permittivity

    **Usage example:**

    ::

        # If used by electromagnetic model module:
        from .commonfunc import maxwell_garnett
        effective_permittivity = maxwell_garnett(frac_volume=0.2,
                                                 e0=1,
                                                 eps=3.185,
                                                 depol_xyz=[0.3, 0.3, 0.4])

        # If accessed from elsewhere, use absolute import
        from smrt.emmodel.commonfunc import maxwell_garnett

    """

    assert np.all(frac_volume <= 1)

    if inclusion_shape is not None and inclusion_shape != "spheres":
        raise SMRTError("inclusion_shape must be set to 'spheres'")

    if depol_xyz is None:
        depol_xyz = depolarization_factors(length_ratio)

    # Calculate x, y, z components of effective permittivity from Maxwell-Garnett theory
    effective_permittivity_xyz = e0 * (1 + frac_volume * (eps - e0) /
                                       (e0 + (1. - frac_volume) * depol_xyz *
                                        (eps - e0)))

    # Assume random orientation i.e. 1/3 of each polarizability component provides equal shares
    # to the macroscopic polarization density
    # See pg 68 Sihvola: Electromagnetic mixing formulas and applications

    return np.mean(effective_permittivity_xyz, dtype=np.complex128)
Esempio n. 25
0
    def return_as_dataframe(self, name, channel_axis=None, **kwargs):
        def xr_to_dataframe(x, name):
            # workaround for when the resulting array has no dims anymore
            if x.dims:
                return x.to_dataframe(name=name)
            else:
                return pd.DataFrame([float(x)], columns=[name])

        if channel_axis in ["column", "index"]:
            if not self.channel_map:
                raise SMRTError(
                    "No channel information is given in the result. Unable to index the result by channel."
                )

            # concat the dataframe obtained for each channel
            x = pd.concat([
                xr_to_dataframe(self.sel_data(channel=ch, **kwargs), name=ch)
                for ch in self.channel_map
            ],
                          axis=1,
                          join='inner')

            if channel_axis == "index":
                droplevel = not x.index.name and len(x.index) == 1 and x.index[
                    0] == 0  # this is our added index, remove it
                x = x.stack()
                if isinstance(x, pd.Series):
                    x = pd.DataFrame(x, columns=[name])

                x.index.set_names('channel', level=-1)
                if droplevel:
                    x = x.droplevel(0)

            return x
        elif channel_axis:
            raise SMRTError(
                'channel_axis argument must be "column" or "index"')
        else:
            return xr_to_dataframe(self.sel_data(**kwargs), name=name)
Esempio n. 26
0
    def __init__(self, intensity, coords=None):
        """Construct results array with the given intensity array (numpy array or xarray) and dimensions if numpy array is given

"""
        if isinstance(intensity, xr.DataArray):
            self.data = intensity
        else:
            self.data = xr.DataArray(intensity, coords)

        if hasattr(self, "mode"):
            self.data.attrs['mode'] = self.mode
        else:
            raise SMRTError("Result base class is abstract, uses a subclass instead. The subclass must define the 'mode' attribute")
Esempio n. 27
0
    def W_n(self, n, k):

        if self.autocorrelation_function == "gaussian":

           # gaussian C(r) = exp ( -(r/l)**2 )
            l = self.corr_length
            return (l**2/(2*n)) * np.exp(-(k*l)**2/(4*n))
        elif self.autocorrelation_function == "exponential":
            # exponential C(r) = exp( -r/l )
            l = self.corr_length
            return (l/n)**2 * (1 + (k*l/n)**2)**(-1.5)
        else:
            raise SMRTError("The autocorrelation function must be expoential or gaussian")
Esempio n. 28
0
def import_class(modulename, classname=None, root=None):
    """import the modulename and return either the class name classname or the first class defined in the module
"""

    if root is not None:
        if "." in modulename:
            raise SMRTError(
                "modulename error. Composed module name is not allowed when root argument is used"
            )
        modulename = root + "." + modulename

    # remove attempt of relative import
    if (".." in modulename) or (modulename[0] == '.'):
        raise SMRTError("modulename error. Relative import is not allowed")

    # import the module
    try:
        module = importlib.import_module(modulename)
    except ImportError as e:
        # TODO: try to import all the modules. Do we want this ??
        raise SMRTError(
            "Unable to find the module '%s' to import the class '%s'. The error is \"%s\""
            % (modulename, classname, str(e)))

    if classname is None:  # search for the first class defined in the module
        for name, obj in inspect.getmembers(module):
            if inspect.isclass(
                    obj
            ) and obj.__module__ == modulename:  # the second condition check if the class was defined in this module
                classname = name
                break

    if classname is None:
        raise SMRTError("Unable to find a class in the module '%s'" %
                        modulename)

    # get the class
    return getattr(module, classname)
Esempio n. 29
0
def make_snowpack(thickness,
                  microstructure_model,
                  density,
                  interface=None,
                  substrate=None,
                  **kwargs):
    """
    build a multi-layered snowpack. Each parameter can be an array, list or a constant value.

    :param thickness: thicknesses of the layers in meter (from top to bottom). The last layer thickness can be "numpy.inf" for a semi-infinite layer.
    :param microstructure_model: microstructure_model to use (e.g. sticky_hard_spheres or independent_sphere or exponential).
    :param interface: type of interface, flat/fresnel is the default.
    :param density: densities of the layers.
    :param substrate: set the substrate of the snowpack. Another way to add a substrate is to use the + operator (e.g. snowpack + substrate).
    All the other parameters (temperature, microstructure parameters, emmodel, etc, etc) are given as optional arguments (e.g. temperature=[270, 250]).
    They are passed for each layer to the function :py:func:`~smrt.inputs.make_medium.make_snow_layer`. Thus, the documentation of this function is the reference. It describes precisely the available parameters.
    The microstructure parameter(s) depend on the microstructure_model used and is documented in each microstructure_model module.

    e.g.::

        sp = make_snowpack([1, 10], "exponential", density=[200,300], temperature=[240, 250], corr_length=[0.2e-3, 0.3e-3])

"""

    sp = Snowpack(substrate=substrate)

    if not isinstance(thickness, collections.Iterable):
        raise SMRTError(
            "The thickness argument must be iterable, that is, a list of numbers, numpy array or pandas Series or DataFrame."
        )

    lib.check_argument_size(density, len(thickness), "density")
    lib.check_argument_size(kwargs, len(thickness))

    for i, dz in enumerate(thickness):
        layer = make_snow_layer(dz,
                                lib.get(microstructure_model, i,
                                        "microstructure_model"),
                                density=lib.get(density, i, "density"),
                                **lib.get(kwargs, i))

        # add the interface
        sp.append(layer,
                  interface=make_interface(lib.get(interface, i, "interface")))

    return sp
Esempio n. 30
0
def make_generic_stack(thickness,
                       temperature=273,
                       ks=0,
                       ka=0,
                       effective_permittivity=1,
                       interface=None,
                       substrate=None,
                       atmosphere=None):
    """
    build a multi-layered medium with prescribed scattering and absorption coefficients and effective permittivity. Must be used with presribed_kskaeps emmodel.

    :param thickness: thicknesses of the layers in meter (from top to bottom). The last layer thickness can be "numpy.inf" for a semi-infinite layer.
    :param temperature: temperature of layers in K
    :param ks: scattering coefficient of layers in m^-1
    :param ka: absorption coefficient of layers in m^-1
    :param interface: type of interface, flat/fresnel is the default

"""
    # TODO: Add an example
    #    e.g.::
    #
    #        sp = make_snowpack([1, 10], "exponential", density=[200,300], temperature=[240, 250], corr_length=[0.2e-3, 0.3e-3])
    #
    #"""

    sp = Snowpack(substrate=substrate, atmosphere=atmosphere)

    if not isinstance(thickness, collections.abc.Iterable):
        raise SMRTError(
            "The thickness argument must be iterable, that is, a list of numbers, numpy array or pandas Series or DataFrame."
        )

    for i, dz in enumerate(thickness):
        layer = make_generic_layer(dz,
                                   ks=lib.get(ks, i, "ks"),
                                   ka=lib.get(ka, i, "ka"),
                                   effective_permittivity=lib.get(
                                       effective_permittivity, i,
                                       "effective_permittivity"),
                                   temperature=lib.get(temperature, i,
                                                       "temperature"))

        sp.append(layer, lib.get(interface, i))

    return sp