Exemplo n.º 1
0
    def eval_dataset(self, spectral_ctx: SpectralContext) -> xr.Dataset:
        """
        Return a dataset that holds the radiative properties of the corresponding
        atmospheric profile. This method dispatches evaluation to specialised
        methods depending on the active mode.

        Parameters
        ----------
        spectral_ctx : :class:`.SpectralContext`
            A spectral context data structure containing relevant spectral
            parameters (*e.g.* wavelength in monochromatic mode).

        Returns
        -------
        Dataset
            Radiative properties dataset.
        """
        if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
            return self.eval_dataset_mono(spectral_ctx.wavelength).squeeze()

        elif eradiate.mode().has_flags(ModeFlags.ANY_CKD):
            return self.eval_dataset_ckd(
                spectral_ctx.bindex,
                bin_set_id=spectral_ctx.bin_set.id).squeeze()

        else:
            raise UnsupportedModeError(supported=("monochromatic", "ckd"))
Exemplo n.º 2
0
    def eval(self, spectral_ctx: SpectralContext) -> np.ndarray:
        r"""
        Evaluate phase function based on a spectral context. This method
        dispatches evaluation to specialised methods depending on the active
        mode.

        Parameters
        ----------
        spectral_ctx : :class:`.SpectralContext`
            A spectral context data structure containing relevant spectral
            parameters (*e.g.* wavelength in monochromatic mode, bin and
            quadrature point index in CKD mode).

        Returns
        -------
        ndarray
            Evaluated phase function as a 1D array.

        Notes
        -----
        The phase function is represented by an array of values mapped to
        regularly spaced scattering angle cosine values
        (:math:`\mu \in [-1, 1]`).
        """
        if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
            return self.eval_mono(spectral_ctx.wavelength).squeeze()

        elif eradiate.mode().has_flags(ModeFlags.ANY_CKD):
            return self.eval_ckd(spectral_ctx.bindex).squeeze()

        else:
            raise UnsupportedModeError(supported=("monochromatic", "ckd"))
Exemplo n.º 3
0
def test_heterogeneous_single(modes_all_single, components, bin_set,
                              path_to_ussa76_approx_data):
    """
    Unit tests for a HeterogeneousAtmosphere with a single component.
    """
    # Construct succeeds
    if components == "molecular":
        if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
            component = MolecularAtmosphere.ussa1976(absorption_data_sets={
                "us76_u86_4":
                path_to_ussa76_approx_data
            }, )
        elif eradiate.mode().has_flags(ModeFlags.ANY_CKD):
            component = MolecularAtmosphere.afgl_1986()
        else:
            pytest.skip(f"unsupported mode '{eradiate.mode().id}'")

        atmosphere = HeterogeneousAtmosphere(molecular_atmosphere=component)
    else:
        component = ParticleLayer()
        atmosphere = HeterogeneousAtmosphere(particle_layers=[component])

    # Produced kernel dict can be loaded
    ctx = KernelDictContext(spectral_ctx=CKDSpectralContext(bin_set=bin_set))
    assert atmosphere.kernel_dict(ctx).load()
Exemplo n.º 4
0
def test_add_srf(modes_all_single):
    if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
        spectral_cfg = {"wavelengths": [550.0]}
    elif eradiate.mode().has_flags(ModeFlags.ANY_CKD):
        spectral_cfg = {"bins": ["550"]}
    else:
        pytest.skip(f"Please add test for '{eradiate.mode().id}' mode")

    exp = OneDimExperiment(
        atmosphere=None,
        measures=MultiDistantMeasure.from_viewing_angles(
            zeniths=[-60, -45, 0, 45, 60],
            azimuths=0.0,
            spp=1,
            spectral_cfg=spectral_cfg,
        ),
    )
    exp.process()
    measure = exp.measures[0]

    # Apply basic post-processing
    values = Pipeline([
        Gather(var="radiance"),
        AggregateCKDQuad(var="radiance", measure=measure)
    ]).transform(measure.results)

    step = AddSpectralResponseFunction(measure=measure)
    result = step.transform(values)

    # The spectral response function is added to the dataset as a data variable
    assert "srf" in result.data_vars
    # Its only dimension is wavelength
    assert set(result.srf.dims) == {"srf_w"}
Exemplo n.º 5
0
        def eval_illumination_spectrum(
            field_name: str, k_units: pint.Unit
        ) -> pint.Quantity:
            # Local helper function to help with illumination spectrum evaluation

            spectrum: Spectrum = getattr(illumination, field_name)

            if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
                # Very important: sort spectral coordinate
                wavelengths = np.sort(measure.spectral_cfg.wavelengths)
                assert np.allclose(wavelengths, wavelengths_dataset)
                return spectrum.eval_mono(wavelengths).m_as(k_units)

            elif eradiate.mode().has_flags(ModeFlags.ANY_CKD):
                # Collect bins and wavelengths, evaluate spectrum
                bins = measure.spectral_cfg.bins
                wavelengths = [bin.wcenter.m_as(ureg.nm) for bin in bins] * ureg.nm

                # Note: This line assumes that eval_ckd() returns a constant
                # value over the entire bin
                result = spectrum.eval_ckd(*(bin.bindexes[0] for bin in bins)).m_as(
                    k_units
                )

                # Reorder data by ascending wavelengths
                indices = wavelengths.argsort()
                wavelengths = wavelengths[indices]
                assert np.allclose(wavelengths, wavelengths_dataset)
                result = result[indices]

                return result

            else:
                raise UnsupportedModeError(supported=("monochromatic", "ckd"))
Exemplo n.º 6
0
def test_add_illumination(modes_all_single, illumination_type, expected_dims):
    # Initialise test data
    if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
        spectral_cfg = {"wavelengths": [540.0, 550.0, 560.0]}
    elif eradiate.mode().has_flags(ModeFlags.ANY_CKD):
        spectral_cfg = {"bins": ["540", "550", "560"]}
    else:
        pytest.skip(f"Please add test for '{eradiate.mode().id}' mode")

    exp = OneDimExperiment(
        atmosphere=None,
        illumination={"type": illumination_type},
        measures=MultiDistantMeasure(spectral_cfg=spectral_cfg),
    )
    exp.process()
    measure = exp.measures[0]

    # Apply basic post-processing
    values = Pipeline(steps=[
        ("gather", Gather(var="radiance")),
        ("aggregate_ckd_quad",
         AggregateCKDQuad(var="radiance", measure=measure)),
    ]).transform(measure.results)

    step = AddIllumination(illumination=exp.illumination, measure=measure)
    result = step.transform(values)
    # Irradiance is here and is indexed in Sun angles and spectral coordinate
    irradiance = result.data_vars["irradiance"]
    assert set(irradiance.dims) == {"w"}.union(set(expected_dims))
Exemplo n.º 7
0
def test_heterogeneous_multi(modes_all_single, bin_set,
                             path_to_ussa76_approx_data):
    """
    Unit tests for a HeterogeneousAtmosphere with multiple (2+) components.
    """
    # Construct succeeds
    if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
        molecular_atmosphere = MolecularAtmosphere.ussa1976(
            absorption_data_sets={"us76_u86_4": path_to_ussa76_approx_data}, )
    elif eradiate.mode().has_flags(ModeFlags.ANY_CKD):
        molecular_atmosphere = MolecularAtmosphere.afgl_1986()
    else:
        pytest.skip(f"unsupported mode '{eradiate.mode().id}'")

    atmosphere = HeterogeneousAtmosphere(
        molecular_atmosphere=molecular_atmosphere,
        particle_layers=[ParticleLayer() for _ in range(2)],
    )

    # Radiative property metadata are correct
    ctx = KernelDictContext(spectral_ctx=CKDSpectralContext(bin_set=bin_set))

    # Kernel dict production succeeds
    assert atmosphere.kernel_phase(ctx)
    assert atmosphere.kernel_media(ctx)
    assert atmosphere.kernel_shapes(ctx)

    # Produced kernel dict can be loaded
    kernel_dict = atmosphere.kernel_dict(ctx)
    assert kernel_dict.load()
Exemplo n.º 8
0
def test_interpolated_eval(modes_all):
    if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
        spectral_ctx = SpectralContext.new(wavelength=550.0)
        expected = 0.5

    elif eradiate.mode().has_flags(ModeFlags.ANY_CKD):
        bin = BinSet.from_db("10nm").select_bins("550")[0]
        spectral_ctx = SpectralContext.new(bindex=bin.bindexes[0])
        expected = 0.5

    else:
        assert False

    # Spectrum without quantity performs linear interpolation and yields units
    # consistent with values
    spectrum = InterpolatedSpectrum(wavelengths=[500.0, 600.0], values=[0.0, 1.0])
    assert spectrum.eval(spectral_ctx) == expected
    spectrum.values *= ureg("W/m^2/nm")
    assert spectrum.eval(spectral_ctx) == expected * ureg("W/m^2/nm")

    # Spectrum with quantity performs linear interpolation and yields units
    # consistent with quantity
    spectrum = InterpolatedSpectrum(
        quantity="irradiance", wavelengths=[500.0, 600.0], values=[0.0, 1.0]
    )
    # Interpolation returns quantity
    assert spectrum.eval(spectral_ctx) == expected * ucc.get("irradiance")
Exemplo n.º 9
0
    def eval_sigma_s(self, spectral_ctx: SpectralContext) -> pint.Quantity:
        """
        Evaluate scattering coefficient spectrum based on a spectral context.
        This method dispatches evaluation to specialised methods depending on
        the active mode.

        Parameters
        ----------
        spectral_ctx : :class:`.SpectralContext`
            A spectral context data structure containing relevant spectral
            parameters (*e.g.* wavelength in monochromatic mode, bin and
            quadrature point index in CKD mode).

        Returns
        -------
        quantity
            Evaluated spectrum as an array with length equal to the number of
            layers.
        """

        if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
            return self.eval_sigma_s_mono(spectral_ctx.wavelength).squeeze()

        elif eradiate.mode().has_flags(ModeFlags.ANY_CKD):
            return self.eval_sigma_s_ckd(spectral_ctx.bindex).squeeze()

        else:
            raise UnsupportedModeError(supported=("monochromatic", "ckd"))
Exemplo n.º 10
0
    def new(**kwargs) -> SpectralContext:
        """
        Create a new instance of one of the :class:`SpectralContext` child
        classes. *The instantiated class is defined based on the currently active
        mode.* Keyword arguments are passed to the instantiated class's
        constructor.

        Parameters
        ----------
        **kwargs
            Keyword arguments depending on the currently active mode (see below
            for a list of actual keyword arguments).

        wavelength : quantity or float, default: 550 nm
            **Monochromatic modes** [:class:`.MonoSpectralContext`].
            Wavelength. Unit-enabled field (default: ucc[wavelength]).

        bindex : :class:`.Bindex`, default: test value (1st quadrature point for the "550" bin of the "10nm" bin set)
            **CKD modes** [:class:`.CKDSpectralContext`].
            CKD bindex.

        See Also
        --------
        :func:`eradiate.mode`, :func:`eradiate.set_mode`
        """

        if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
            return MonoSpectralContext(**kwargs)

        elif eradiate.mode().has_flags(ModeFlags.ANY_CKD):
            return CKDSpectralContext(**kwargs)

        else:
            raise UnsupportedModeError(supported=("monochromatic", "ckd"))
Exemplo n.º 11
0
def test_onedim_experiment_run_detailed(modes_all):
    """
    Test for correctness of the result dataset generated by OneDimExperiment.
    """
    if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
        spectral_cfg = {"wavelengths": 550.0 * ureg.nm}
    elif eradiate.mode().has_flags(ModeFlags.ANY_CKD):
        spectral_cfg = {"bin_set": "10nm", "bins": "550"}
    else:
        pytest.skip(f"Please add test for '{eradiate.mode().id}' mode")

    # Create simple scene
    exp = OneDimExperiment(measures=[
        {
            "type": "hemispherical_distant",
            "id": "toa_hsphere",
            "film_resolution": (32, 32),
            "spp": 1000,
            "spectral_cfg": spectral_cfg,
        },
    ])

    # Run RT simulation
    exp.run()

    # Check result dataset structure
    results = exp.results["toa_hsphere"]

    # Post-processing creates expected variables ...
    expected = {"irradiance", "brf", "brdf", "radiance", "spp", "srf"}
    if eradiate.mode().has_flags(ModeFlags.ANY_CKD):
        expected |= {"irradiance_srf", "brf_srf", "brdf_srf", "radiance_srf"}
    assert set(results.data_vars) == expected

    # ... dimensions
    assert set(
        results["radiance"].dims) == {"sza", "saa", "x_index", "y_index", "w"}
    assert set(results["irradiance"].dims) == {"sza", "saa", "w"}

    # ... and other coordinates
    expected_coords = {
        "sza", "saa", "vza", "vaa", "x", "y", "x_index", "y_index", "w"
    }
    if eradiate.mode().has_flags(ModeFlags.ANY_CKD):
        expected_coords |= {"bin", "bin_wmin", "bin_wmax"}
    assert set(results["radiance"].coords) == expected_coords

    expected_coords = {"sza", "saa", "w"}
    if eradiate.mode().has_flags(ModeFlags.ANY_CKD):
        expected_coords |= {"bin", "bin_wmin", "bin_wmax"}
    assert set(results["irradiance"].coords) == expected_coords

    # We just check that we record something as expected
    assert np.all(results["radiance"].data > 0.0)
Exemplo n.º 12
0
def test_mode_flags():
    # Check flags for mono mode
    eradiate.set_mode("mono")
    assert eradiate.mode().has_flags(ModeFlags.ERT_MONO)
    assert not eradiate.mode().has_flags(ModeFlags.MTS_DOUBLE)

    # Check flags for mono_double mode
    eradiate.set_mode("mono_double")
    assert eradiate.mode().has_flags(ModeFlags.ERT_MONO)
    assert eradiate.mode().has_flags(ModeFlags.MTS_DOUBLE)

    # Check if conversion of string to flags works as intended
    eradiate.set_mode("mono_double")
    assert eradiate.mode().has_flags("any_double")
Exemplo n.º 13
0
def test_spectral_context_new(modes_all):
    """
    Unit tests for :meth:`SpectralContext.new`.
    """

    # Empty call to new() should yield the appropriate SpectralContext instance
    if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
        assert isinstance(SpectralContext.new(), MonoSpectralContext)
    elif eradiate.mode().has_flags(ModeFlags.ANY_CKD):
        assert isinstance(SpectralContext.new(), CKDSpectralContext)
    else:
        # All modes must have a context: this test fails if the mode has no
        # associated spectral context
        assert False
Exemplo n.º 14
0
def init_experiment(bottom, top, sigma_a, sigma_s, phase, r, w, spp):
    assert eradiate.mode().id == "mono_double"

    return eradiate.experiments.OneDimExperiment(
        measures=[
            eradiate.scenes.measure.MultiDistantMeasure.from_viewing_angles(
                spectral_cfg=eradiate.scenes.measure.MeasureSpectralConfig.new(
                    wavelengths=w, ),
                zeniths=np.linspace(-75, 75, 11) * ureg.deg,
                azimuths=0.0 * ureg.deg,
                spp=spp,
            )
        ],
        illumination=eradiate.scenes.illumination.DirectionalIllumination(
            zenith=30 * ureg.deg,
            azimuth=0.0 * ureg.deg,
            irradiance=eradiate.scenes.spectra.SolarIrradianceSpectrum(
                dataset="blackbody_sun"),
        ),
        atmosphere=eradiate.scenes.atmosphere.HomogeneousAtmosphere(
            bottom=bottom,
            top=top,
            sigma_a=sigma_a,
            sigma_s=sigma_s,
            phase=phase,
        ),
        surface=eradiate.scenes.surface.LambertianSurface(reflectance=r),
    )
Exemplo n.º 15
0
def test_onedim_experiment_run_basic(modes_all):
    """
    OneDimExperiment runs successfully in all modes.
    """
    if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
        spectral_cfg = MeasureSpectralConfig.new(wavelengths=550.0 * ureg.nm)
    elif eradiate.mode().has_flags(ModeFlags.ANY_CKD):
        spectral_cfg = MeasureSpectralConfig.new(bin_set="10nm", bins="550")
    else:
        pytest.skip(f"Please add test for '{eradiate.mode().id}' mode")

    exp = OneDimExperiment()
    exp.measures[0].spectral_cfg = spectral_cfg

    exp.run()
    assert isinstance(exp.results, dict)
Exemplo n.º 16
0
 def _split_spp_validator(self, attribute, value):
     if (eradiate.mode().has_flags(ModeFlags.ANY_SINGLE) and self.spp > 1e5
             and self.split_spp is None):
         warnings.warn(
             "In single-precision modes, setting a sample count ('spp') to "
             "values greater than 100,000 may result in floating point "
             "precision issues: using the measure's 'split_spp' parameter is "
             "recommended.")
Exemplo n.º 17
0
def _spectral_dims():
    if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
        return (
            (
                "w",
                {
                    "standard_name": "wavelength",
                    "long_name": "wavelength",
                    "units": ucc.get("wavelength"),
                },
            ),
        )
    elif eradiate.mode().has_flags(ModeFlags.ANY_CKD):
        return (
            ("bin", {"standard_name": "ckd_bin", "long_name": "CKD bin"}),
            ("index", {"standard_name": "ckd_index", "long_name": "CKD index"}),
        )
    else:
        raise UnsupportedModeError
Exemplo n.º 18
0
 def kernel_dict(self, ctx: KernelDictContext) -> KernelDict:
     if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
         # TODO: This is a workaround until the hg plugin accepts spectra for
         #  its g parameter
         g = float(onedict_value(self.g.kernel_dict(ctx=ctx))["value"])
         return KernelDict({self.id: {
             "type": "hg",
             "g": g,
         }})
     else:
         raise UnsupportedModeError(supported="monochromatic")
Exemplo n.º 19
0
def test_solar_irradiance_eval(modes_all):
    # Irradiance is correctly interpolated in mono mode
    s = SolarIrradianceSpectrum(dataset="thuillier_2003")

    if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
        spectral_ctx = SpectralContext.new(wavelength=550.0)
        # Reference value computed manually
        assert np.allclose(s.eval(spectral_ctx),
                           ureg.Quantity(1.87938, "W/m^2/nm"))

    elif eradiate.mode().has_flags(ModeFlags.ANY_CKD):
        bin_set = BinSet.from_db("10nm")
        bin = bin_set.select_bins("550")[0]
        bindex = bin.bindexes[0]
        spectral_ctx = SpectralContext.new(bindex=bindex)
        # Reference value computed manually
        assert np.allclose(s.eval(spectral_ctx),
                           ureg.Quantity(1.871527, "W/m^2/nm"))

    else:
        assert False
Exemplo n.º 20
0
def test_air_scattering_coefficient(modes_all_mono_ckd):
    ctx = KernelDictContext()

    # We can instantiate the class
    s = AirScatteringCoefficientSpectrum()

    # The spectrum evaluates correctly (reference values computed manually)
    if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
        expected = ureg.Quantity(0.0114934, "km^-1")

    elif eradiate.mode().has_flags(ModeFlags.ANY_CKD):
        expected = ureg.Quantity(0.0114968, "km^-1")

    else:
        assert False

    value = s.eval(ctx.spectral_ctx)
    assert np.allclose(value, expected)

    # The associated kernel dict is correctly formed and can be loaded
    from mitsuba.core.xml import load_dict

    assert load_dict(onedict_value(s.kernel_dict(ctx=ctx))) is not None
Exemplo n.º 21
0
    def eval_sigma_s(self, spectral_ctx: SpectralContext) -> pint.Quantity:
        """
        Evaluate scattering coefficient given a spectral context.

        Parameters
        ----------
        spectral_ctx : :class:`.SpectralContext`
            A spectral context data structure containing relevant spectral
            parameters (*e.g.* wavelength in monochromatic mode, bin and
            quadrature point index in CKD mode).

        Returns
        -------
        quantity
            Particle layer scattering coefficient.
        """
        if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
            return self.eval_sigma_s_mono(spectral_ctx.wavelength).squeeze()

        elif eradiate.mode().has_flags(ModeFlags.ANY_CKD):
            return self.eval_sigma_s_ckd(spectral_ctx.bindex).squeeze()

        else:
            raise UnsupportedModeError(supported=("monochromatic", "ckd"))
Exemplo n.º 22
0
    def kernel_dict(self, ctx: KernelDictContext) -> KernelDict:
        if eradiate.mode().has_flags(ModeFlags.ANY_MONO | ModeFlags.ANY_CKD):
            return KernelDict({
                "spectrum": {
                    "type":
                    "uniform",
                    "value":
                    float(
                        self.eval(ctx.spectral_ctx).m_as(uck.get(
                            self.quantity))),
                }
            })

        else:
            raise UnsupportedModeError(supported=("monochromatic", "ckd"))
Exemplo n.º 23
0
    def new(**kwargs) -> MeasureSpectralConfig:
        """
        Create a new instance of one of the :class:`.SpectralContext` child
        classes. *The instantiated class is defined based on the currently active
        mode.* Keyword arguments are passed to the instantiated class's
        constructor.

        Parameters
        ----------
        **kwargs : dict, optional
            Keyword arguments depending on the currently active mode (see below
            for a list of actual keyword arguments).

        wavelengths : quantity, default: [550] nm
            **Monochromatic modes** [:class:`.MonoMeasureSpectralConfig`].
            List of wavelengths (automatically converted to a Numpy array).
            *Unit-enabled field (default: ucc[wavelength]).*

        bin_set : :class:`.BinSet` or str, default: "10nm"
            **CKD modes** [:class:`.CKDMeasureSpectralConfig`].
            CKD bin set definition. If a string is passed, the data
            repository is queried for the corresponding identifier using
            :meth:`.BinSet.from_db`.

        bins : list of (str or tuple or dict or callable)
            **CKD modes** [:class:`.CKDSpectralContext`].
            List of CKD bins on which to perform the spectral loop. If unset,
            all the bins defined by the selected bin set will be covered.

        See Also
        --------
        :func:`eradiate.mode`, :func:`eradiate.set_mode`
        """
        mode = eradiate.mode()

        if mode is None:
            raise ModeError(
                "instantiating MeasureSpectralConfig requires a mode to be selected"
            )

        if mode.has_flags(ModeFlags.ANY_MONO):
            return MonoMeasureSpectralConfig(**kwargs)

        elif mode.has_flags(ModeFlags.ANY_CKD):
            return CKDMeasureSpectralConfig(**kwargs)

        else:
            raise UnsupportedModeError(supported=("monochromatic", "ckd"))
Exemplo n.º 24
0
def test_apply_spectral_response_function_transform(mode_id, spectral_cfg):
    """
    Unit tests for ApplySpectralResponseFunction.transform().
    """
    # Prepare basic data
    eradiate.set_mode(mode_id)

    exp = OneDimExperiment(
        atmosphere=None,
        measures=MultiDistantMeasure.from_viewing_angles(
            id="measure",
            zeniths=[-60, -45, 0, 45, 60],
            azimuths=0.0,
            spp=1,
            spectral_cfg=spectral_cfg,
        ),
    )
    measure = exp.measures[0]
    exp.process(measure)

    # Apply first steps of post-processing
    pipeline = exp.pipeline(measure)
    values = pipeline.transform(measure.results, stop_after="add_viewing_angles")

    # Apply tested pipeline step
    step = ApplySpectralResponseFunction(measure=measure, vars=["radiance"])

    if eradiate.mode().has_flags(ModeFlags.ANY_MONO):
        # In mono modes, the dataset produced by previous steps is missing
        # bin data required for the step to run successfully
        with pytest.raises(ValueError):
            step.transform(values)
        return

    # In binned modes, computation goes through
    result = step.transform(values)

    # The step adds a SRF-weighted variable
    assert "radiance_srf" in result.data_vars
    assert np.all(result.radiance_srf > 0.0)
Exemplo n.º 25
0
def test_mode_mono(mode_mono):
    mode = eradiate.mode()
    assert mode.kernel_variant == "scalar_mono"
Exemplo n.º 26
0
    def pipeline(
        self, *measures: t.Union[Measure, int]
    ) -> t.Union[Pipeline, t.Tuple[Pipeline, ...]]:
        result = []

        # Convert integer values to measure entries
        measures = [
            self.measures[measure] if isinstance(measure, int) else measure
            for measure in measures
        ]

        for measure in measures:
            pipeline = pipelines.Pipeline()

            # Gather
            pipeline.add(
                "gather",
                pipelines.Gather(sensor_dims=measure.sensor_dims,
                                 var=measure.var),
            )

            # Aggregate
            pipeline.add("aggregate_sample_count",
                         pipelines.AggregateSampleCount())
            pipeline.add(
                "aggregate_ckd_quad",
                pipelines.AggregateCKDQuad(measure=measure,
                                           var=measure.var[0]),
            )

            if isinstance(measure, (DistantFluxMeasure, )):
                pipeline.add(
                    "aggregate_radiosity",
                    pipelines.AggregateRadiosity(
                        sector_radiosity_var=measure.var[0],
                        radiosity_var="radiosity",
                    ),
                )

            # Assemble
            pipeline.add(
                "add_illumination",
                pipelines.AddIllumination(
                    illumination=self.illumination,
                    measure=measure,
                    irradiance_var="irradiance",
                ),
            )

            if measure.is_distant():
                pipeline.add("add_viewing_angles",
                             pipelines.AddViewingAngles(measure=measure))

            pipeline.add(
                "add_srf",
                pipelines.AddSpectralResponseFunction(measure=measure))

            # Compute
            if isinstance(measure,
                          (MultiDistantMeasure, HemisphericalDistantMeasure)):
                pipeline.add(
                    "compute_reflectance",
                    pipelines.ComputeReflectance(
                        radiance_var="radiance",
                        irradiance_var="irradiance",
                        brdf_var="brdf",
                        brf_var="brf",
                    ),
                )

                if eradiate.mode().has_flags(ModeFlags.ANY_CKD):
                    pipeline.add(
                        "apply_srf",
                        pipelines.ApplySpectralResponseFunction(
                            measure=measure,
                            vars=["radiance", "irradiance"],
                        ),
                    )

                    pipeline.add(
                        "compute_reflectance_srf",
                        pipelines.ComputeReflectance(
                            radiance_var="radiance_srf",
                            irradiance_var="irradiance_srf",
                            brdf_var="brdf_srf",
                            brf_var="brf_srf",
                        ),
                    )

            elif isinstance(measure, (DistantFluxMeasure, )):
                pipeline.add(
                    "compute_albedo",
                    pipelines.ComputeAlbedo(
                        radiosity_var="radiosity",
                        irradiance_var="irradiance",
                        albedo_var="albedo",
                    ),
                )

                if eradiate.mode().has_flags(ModeFlags.ANY_CKD):
                    pipeline.add(
                        "apply_srf",
                        pipelines.ApplySpectralResponseFunction(
                            measure=measure,
                            vars=["radiosity", "irradiance"],
                        ),
                    )

                    pipeline.add(
                        "compute_albedo_srf",
                        pipelines.ComputeAlbedo(
                            radiosity_var="radiosity_srf",
                            irradiance_var="irradiance_srf",
                            albedo_var="albedo_srf",
                        ),
                    )

            result.append(pipeline)

        return result[0] if len(result) == 1 else tuple(result)
Exemplo n.º 27
0
 def __attrs_pre_init__(self):
     if not eradiate.mode().has_flags(ModeFlags.ANY_MONO):
         raise UnsupportedModeError(supported="monochromatic")
Exemplo n.º 28
0
    def transform(self, x: t.Any) -> t.Any:
        # If not in CKD mode, this step is a no-op
        if not eradiate.mode().has_flags(ModeFlags.ANY_CKD):
            return x

        # Otherwise, compute quadrature spectrum-indexed variables and turn spp
        # into a per-bin average

        # Deduplicate bin list preserving order
        bins = list(OrderedDict.fromkeys(x.bin.to_index()))
        n_bins = len(bins)

        # Collect quadrature data
        quad = self.measure.spectral_cfg.bin_set.quad

        # Collect wavelengths associated with each bin
        wavelength_units = ucc.get("wavelength")
        wavelengths = []
        bin_wmins = []
        bin_wmaxs = []

        for bin in self.measure.spectral_cfg.bin_set.select_bins(("ids", {
                "ids": bins
        })):
            wavelengths.append(bin.wcenter.m_as(wavelength_units))
            bin_wmins.append(bin.wmin.m_as(wavelength_units))
            bin_wmaxs.append(bin.wmax.m_as(wavelength_units))

        result = x
        var = self.var

        # Get dimensions of current variable
        img = x.data_vars[var]
        dims = OrderedDict((y, len(img.coords[y])) for y in img.dims)

        if "bin" not in dims:
            raise ValueError(f"variable '{var}' is missing dimension 'bin'")

        if "index" not in dims:
            raise ValueError(f"variable '{var}' is missing dimension 'index'")

        # Init storage
        del dims["bin"]
        del dims["index"]

        aggregated = xr.DataArray(
            np.zeros([n_bins] + list(dims.values())),
            coords={
                "bin": img.bin,
                **{dim: img.coords[dim]
                   for dim in dims}
            },
        )

        # For each bin and each pixel, compute quadrature and store the result
        for i_bin, bin in enumerate(bins):
            values_at_nodes = img.sel(bin=bin).values

            # Rationale: Avoid using xarray's indexing in this loop for
            # performance reasons (wrong data indexing method will result in
            # 10x+ speed reduction)
            for indexes in itertools.product(
                    *[list(range(n)) for n in dims.values()]):
                aggregated.values[(i_bin, *indexes)] = quad.integrate(
                    values_at_nodes[(slice(None), *indexes)],
                    interval=np.array([0.0, 1.0]),
                )

        result = result.assign({var: aggregated})
        result[var].attrs = x[var].attrs

        # Average the 'spp' variable over the 'index' dimension
        with xr.set_options(keep_attrs=True):
            result["spp"] = x.spp.mean(dim="index")

        # Add spectral coordinates
        result = result.assign_coords({
            "w": (
                "bin",
                wavelengths,
                {
                    "standard_name": "wavelength",
                    "long_name": "wavelength",
                    "units": symbol(wavelength_units),
                },
            ),
            "bin_wmin": (
                "bin",
                bin_wmins,
                {
                    "standard_name": "bin_wmin",
                    "long_name": "spectral bin lower bound",
                    "units": symbol(wavelength_units),
                },
            ),
            "bin_wmax": (
                "bin",
                bin_wmaxs,
                {
                    "standard_name": "bin_wmax",
                    "long_name": "spectral bin upper bound",
                    "units": symbol(wavelength_units),
                },
            ),
        })

        # Swap the 'bin' and 'w' dimensions
        result = result.swap_dims({"bin": "w"})

        # Remove the 'index' dimension
        result = result.drop_dims("index")

        return result
Exemplo n.º 29
0
 def __init__(self, supported=None, unsupported=None, msg=None):
     super(UnsupportedModeError, self).__init__(msg)
     self.mode = eradiate.mode().id if eradiate.mode() is not None else None
     self.supported = list(always_iterable(supported))
     self.unsupported = list(always_iterable(unsupported))
Exemplo n.º 30
0
    def transform(self, x: t.Dict) -> xr.Dataset:
        # Basic preparation
        prefix = self.prefix

        sensor_dims = []
        sensor_dim_metadata = {}

        for y in self.sensor_dims:
            if isinstance(y, str):
                sensor_dims.append(y)
                sensor_dim_metadata[y] = {}
            else:
                sensor_dims.append(y[0])
                sensor_dim_metadata[y[0]] = y[1]

        spectral_dims = []
        spectral_dim_metadata = {}

        for y in _spectral_dims():
            if isinstance(y, str):
                spectral_dims.append(y)
                spectral_dim_metadata[y] = {}
            else:
                spectral_dims.append(y[0])
                spectral_dim_metadata[y[0]] = y[1]

        sensor_datasets = []

        regex = re.compile(
            r"\_".join(
                [prefix]
                + [rf"{sensor_dim}(?P<{sensor_dim}>\d*)" for sensor_dim in sensor_dims]
            )
        )

        # Loop on spectral indexes
        for spectral_index in x.keys():
            # Loop on sensors
            for sensor_id, sensor_data in x[spectral_index]["values"].items():
                # Collect data
                ds = sensor_data.copy(deep=False)
                spp = x[spectral_index]["spp"][sensor_id]

                # Set spectral coordinates
                spectral_coords = {
                    spectral_dim: [spectral_coord]
                    for spectral_dim, spectral_coord in zip(
                        spectral_dims, always_iterable(spectral_index)
                    )
                }

                # Detect sensor coordinates
                match = regex.match(sensor_id)

                if match is None:
                    raise RuntimeError(
                        "could not detect requested sensor dimensions in "
                        f"sensor ID '{sensor_id}' using regex '{regex.pattern}'; "
                        "this could be due to incorrect values or order of "
                        "'sensor_dims'"
                    )

                sensor_coords = {
                    f"{sensor_dim}_index": [int(match.group(sensor_dim))]
                    for sensor_dim in sensor_dims
                }

                # Add spp dimension even though sample count split did not
                # produce any extra sensor
                if "spp" not in sensor_dims:
                    sensor_coords["spp_index"] = [0]

                # Add spectral and sensor dimensions to img array
                all_coords = {**spectral_coords, **sensor_coords}
                ds["img"] = ds.img.expand_dims(dim=all_coords)

                # Package spp in a data array
                all_dims = list(all_coords.keys())
                ds["spp"] = (all_dims, np.reshape(spp, [1 for _ in all_dims]))

                sensor_datasets.append(ds)

        # Combine all the data
        with xr.set_options(keep_attrs=True):
            result = xr.merge(sensor_datasets)

        # Drop "channel" dimension when using a mono variant
        if eradiate.mode().has_flags(ModeFlags.MTS_MONO):
            result = result.squeeze("channel", drop=True)

        # Apply metadata to new dimensions
        for sensor_dim in sensor_dims:
            result[f"{sensor_dim}_index"].attrs = sensor_dim_metadata[sensor_dim]

        if "spp" not in sensor_dims:
            result["spp_index"].attrs = {
                "standard_name": "spp_index",
                "long_name": "SPP index",
            }

        for spectral_dim in spectral_dims:
            result[spectral_dim].attrs = spectral_dim_metadata[spectral_dim]

        # Apply metadata to data variables
        if isinstance(self.var, str):
            var = self.var
            var_metadata = {}
        else:
            var = self.var[0]
            var_metadata = self.var[1]

        result = result.rename({"img": var})
        result[var].attrs.update(var_metadata)

        result["spp"].attrs = {
            "standard_name": "sample_count",
            "long_name": "sample count",
        }

        return result