def test_ckd_basic(modes_all_ckd): """ CKD correctness check ===================== We check for correctness when using CKD modes with atmosphere-free scenes. This test is designed to check that the CKD infrastructure (preprocessing, spectral loop, postprocessing) works. """ # Configure experiment, run and postprocess results exp = OneDimExperiment( atmosphere=None, surface=LambertianSurface(reflectance=1.0), measures=[ MultiDistantMeasure.from_viewing_angles( zeniths=np.arange(-60, 61, 5) * ureg.deg, azimuths=0.0 * ureg.deg, spectral_cfg={ "bin_set": "10nm", "bins": [ "550", "560", "570", "510", ], # We specify bins in arbitrary order on purpose }, ) ], ) exp.run() # Reflectance is uniform, equal to 1 assert np.allclose(exp.results["measure"].data_vars["brf"], 1.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))
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"}
def test_add_viewing_angles(mode_mono, measure_type, expected_zenith, expected_azimuth): # Initialise test data if measure_type == "multi_distant-nohplane": measure = MultiDistantMeasure.from_viewing_angles( zeniths=[-60, -45, 0, 45, 60], azimuths=0.0, auto_hplane=False, spp=1, ) elif measure_type == "multi_distant-hplane": measure = MultiDistantMeasure.from_viewing_angles( zeniths=[-60, -45, 0, 45, 60], azimuths=0.0, auto_hplane=True, spp=1, ) elif measure_type == "hemispherical_distant": measure = HemisphericalDistantMeasure( film_resolution=(2, 2), spp=1, ) else: assert False exp = OneDimExperiment(atmosphere=None, measures=measure) exp.process() measure = exp.measures[0] # Apply basic post-processing values = Gather(var="radiance").transform(measure.results) step = AddViewingAngles(measure=measure) result = step.transform(values) # Produced dataset has viewing angle coordinates assert "vza" in result.coords assert "vaa" in result.coords # Viewing angles are set to appropriate values assert np.allclose(expected_zenith, result.coords["vza"].values.squeeze()) assert np.allclose(expected_azimuth, result.coords["vaa"].values.squeeze())
def test_spectral_loop(mode_mono, cls, wavelengths, irradiance): """ Spectral loop ============= This test case checks if the spectral loop implementation and post-processing behaves as intended. Rationale --------- The scene consists of a square Lambertian surface with reflectance :math:`\\rho = 1` illuminated by a directional emitter positioned at the zenith. The reflected radiance is computed in monochromatic mode for a single wavelength and for multiple wavelength. The experiment is repeated: * for all solver applications supporting this type of scene; * for several spectral configurations (single wavenlength, multiple ascending, multiple descending); * for several irradiance spectra (uniform, solar). Expected behaviour ------------------ All BRF values are equal to 1. """ if cls == "onedim": cls_exp = OneDimExperiment kwargs = {"atmosphere": None} elif cls == "rami": cls_exp = RamiExperiment kwargs = {"canopy": None} else: raise ValueError if irradiance == "uniform": illumination = DirectionalIllumination(irradiance=1.0) elif irradiance == "solar": illumination = DirectionalIllumination( irradiance=SolarIrradianceSpectrum()) exp = cls_exp( surface=LambertianSurface(reflectance=1.0), illumination=illumination, measures=[ MultiDistantMeasure( spp=1, spectral_cfg={"wavelengths": wavelengths}, ) ], **kwargs, ) exp.run() assert np.allclose(np.squeeze(exp.results["measure"].brf.values), 1.0)
def test_rami4atm_experiment_construct_normalize_measures(mode_mono, padding): # When canopy is not None, measure target matches canopy unit cell exp = Rami4ATMExperiment( atmosphere=None, canopy=DiscreteCanopy.homogeneous( lai=3.0, leaf_radius=0.1 * ureg.m, l_horizontal=10.0 * ureg.m, l_vertical=2.0 * ureg.m, padding=padding, ), measures=MultiDistantMeasure(), ) target = exp.measures[0].target canopy = exp.canopy assert np.isclose(target.xmin, -0.5 * canopy.size[0]) assert np.isclose(target.xmax, 0.5 * canopy.size[0]) assert np.isclose(target.ymin, -0.5 * canopy.size[1]) assert np.isclose(target.ymax, 0.5 * canopy.size[1]) assert np.isclose(target.z, canopy.size[2]) # The measure target does not depend on the atmosphere exp = Rami4ATMExperiment( atmosphere=HomogeneousAtmosphere(width=ureg.Quantity(42.0, "km")), canopy=DiscreteCanopy.homogeneous( lai=3.0, leaf_radius=0.1 * ureg.m, l_horizontal=10.0 * ureg.m, l_vertical=2.0 * ureg.m, padding=padding, ), measures=MultiDistantMeasure(), ) target = exp.measures[0].target canopy = exp.canopy assert np.isclose(target.xmin, -0.5 * canopy.size[0]) assert np.isclose(target.xmax, 0.5 * canopy.size[0]) assert np.isclose(target.ymin, -0.5 * canopy.size[1]) assert np.isclose(target.ymax, 0.5 * canopy.size[1]) assert np.isclose(target.z, canopy.size[2])
def test_onedim_experiment_construct_measures(modes_all): """ A variety of measure specifications are acceptable """ # Init with a single measure (not wrapped in a sequence) assert OneDimExperiment(measures=MultiDistantMeasure()) # Init from a dict-based measure spec # -- Correctly wrapped in a sequence assert OneDimExperiment(measures=[{"type": "distant"}]) # -- Not wrapped in a sequence assert OneDimExperiment(measures={"type": "distant"})
def test_rami_experiment_construct_normalize_measures(mode_mono, padding): """ When canopy is not None, measure target matches canopy unit cell """ exp = RamiExperiment( canopy=DiscreteCanopy.homogeneous( lai=3.0, leaf_radius=0.1 * ureg.m, l_horizontal=10.0 * ureg.m, l_vertical=2.0 * ureg.m, padding=padding, ), measures=MultiDistantMeasure(), ) target = exp.measures[0].target canopy = exp.canopy assert np.isclose(target.xmin, -0.5 * canopy.size[0]) assert np.isclose(target.xmax, 0.5 * canopy.size[0]) assert np.isclose(target.ymin, -0.5 * canopy.size[1]) assert np.isclose(target.ymax, 0.5 * canopy.size[1])
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)