def test_solar_irradiance(mode_mono): # Default context ctx = KernelDictContext() # We can instantiate the element s = SolarIrradianceSpectrum() # Unsupported solar spectrum keywords raise with pytest.raises(ValueError): SolarIrradianceSpectrum(dataset="doesnt_exist") # Produced kernel dict is valid assert KernelDict.from_elements(s, ctx=ctx).load() # A more detailed specification still produces a valid object s = SolarIrradianceSpectrum(scale=2.0) assert KernelDict.from_elements(s, ctx=ctx).load() # Element doesn't work out of the supported spectral range s = SolarIrradianceSpectrum(dataset="thuillier_2003") with pytest.raises(ValueError): ctx = KernelDictContext(spectral_ctx={"wavelength": 2400.0}) s.kernel_dict(ctx) # solid_2017_mean dataset can be used ctx = KernelDictContext() s = SolarIrradianceSpectrum(dataset="solid_2017_mean") assert KernelDict.from_elements(s, ctx=ctx).load()
def test_centralpatch_compute_scale(modes_all_double): ctx = KernelDictContext() cs = CentralPatchSurface(width=10 * ureg.m, central_patch=LambertianSurface(width=1 * ureg.m)) scale = cs._compute_scale_parameter(ctx=ctx) assert np.allclose(scale, 3.33333333) ctx = ctx.evolve(override_canopy_width=2 * ureg.m) scale = cs._compute_scale_parameter(ctx=ctx) assert np.allclose(scale, 1.6666666666667)
def test_molecular_atmosphere_switches(mode_mono): # Absorption can be deactivated atmosphere = MolecularAtmosphere(has_absorption=False) ctx = KernelDictContext() assert np.all(atmosphere.eval_radprops(ctx.spectral_ctx).sigma_a == 0.0) # Scattering can be deactivated atmosphere = MolecularAtmosphere(has_scattering=False) ctx = KernelDictContext() assert np.all(atmosphere.eval_radprops(ctx.spectral_ctx).sigma_s == 0.0) # At least one must be active with pytest.raises(ValueError): MolecularAtmosphere(has_absorption=False, has_scattering=False)
def test_multi_radiancemeter_external_medium(mode_mono): # create a series of multiradiancemeter measures and a kernel dict context # which places some of them inside and outside the atmospheric volume # assert that the external medium is set correctly in the cameras' kernel dicts d1 = MultiRadiancemeterMeasure() ctx1 = KernelDictContext(atmosphere_medium_id="test_atmosphere") ctx2 = KernelDictContext() kd1 = d1.kernel_dict(ctx=ctx1) kd2 = d1.kernel_dict(ctx=ctx2) assert kd1["measure"]["medium"] == {"type": "ref", "id": "test_atmosphere"} assert "medium" not in kd2["measure"].keys()
def test_perspective_external_medium(mode_mono): # create a series of perspective camera measures and a kernel dict context # which places some of them inside and outside the atmospheric volume # assert that the external medium is set correctly in the cameras' kernel dicts s1 = PerspectiveCameraMeasure() ctx1 = KernelDictContext(atmosphere_medium_id="test_atmosphere") ctx2 = KernelDictContext() kd1 = s1.kernel_dict(ctx=ctx1) kd2 = s1.kernel_dict(ctx=ctx2) assert kd1["measure"]["medium"] == {"type": "ref", "id": "test_atmosphere"} assert "medium" not in kd2["measure"].keys()
def test_uniform(modes_all): from mitsuba.core.xml import load_dict # Instantiate with value only s = UniformSpectrum(value=1.0) assert s.quantity is PhysicalQuantity.DIMENSIONLESS assert s.value == 1.0 * ureg.dimensionless # Instantiate with value and quantity s = UniformSpectrum(value=1.0, quantity=PhysicalQuantity.COLLISION_COEFFICIENT) assert s.value == 1.0 * ureg.m**-1 UniformSpectrum(value=1.0, quantity="collision_coefficient") assert s.quantity == PhysicalQuantity.COLLISION_COEFFICIENT assert s.value == 1.0 * ureg.m**-1 # Instantiate with unsupported quantity with pytest.raises(ValueError): UniformSpectrum(value=1.0, quantity="speed") # Instantiate with all arguments s = UniformSpectrum(quantity="collision_coefficient", value=1.0) assert s.value == ureg.Quantity(1.0, "m^-1") # Raise if units and quantity are inconsistent with pytest.raises(pinttr.exceptions.UnitsError): UniformSpectrum(quantity="collision_coefficient", value=ureg.Quantity(1.0, "")) # Instantiate from factory using dict s = spectrum_factory.convert({ "type": "uniform", "quantity": "radiance", "value": 1.0, "value_units": "W/km^2/sr/nm", }) # Produced kernel dict is valid ctx = KernelDictContext() assert load_dict(onedict_value(s.kernel_dict(ctx))) is not None # Unit scaling is properly applied with ucc.override({"radiance": "W/m^2/sr/nm"}): s = UniformSpectrum(quantity="radiance", value=1.0) with uck.override({"radiance": "kW/m^2/sr/nm"}): ctx = KernelDictContext() d = s.kernel_dict(ctx) assert np.allclose(d["spectrum"]["value"], 1e-3)
def test_multi_radiancemeter_noargs(mode_mono): # Instantiation succeeds s = MultiRadiancemeterMeasure() # The kernel dict can be instantiated ctx = KernelDictContext() assert KernelDict.from_elements(s, ctx=ctx).load()
def test_directional(mode_mono): from mitsuba.core.xml import load_dict # We need a default spectral config ctx = KernelDictContext() # Constructor d = DirectionalIllumination() assert load_dict(onedict_value(d.kernel_dict(ctx))) is not None # Check if a more detailed spec is valid d = DirectionalIllumination(irradiance={"type": "uniform", "value": 1.0}) assert load_dict(onedict_value(d.kernel_dict(ctx))) is not None # Check if solar irradiance spectrum can be used d = DirectionalIllumination(irradiance={"type": "solar_irradiance"}) assert load_dict(onedict_value(d.kernel_dict(ctx))) is not None # Check if specification from a float works d = DirectionalIllumination(irradiance=1.0) assert load_dict(onedict_value(d.kernel_dict(ctx))) is not None # Check if specification from a constant works d = DirectionalIllumination(irradiance=ureg.Quantity(1.0, "W/m^2/nm")) assert load_dict(onedict_value(d.kernel_dict(ctx))) is not None with pytest.raises(pinttr.exceptions.UnitsError): # Wrong units DirectionalIllumination(irradiance=ureg.Quantity(1.0, "W/m^2/sr/nm"))
def test_blend_array(modes_all): """ Test instantiation and kernel dict generation with an array of weights. """ # Constructing using 1D arrays for weights succeeds phase = BlendPhaseFunction( components=[ { "type": "isotropic" }, { "type": "isotropic" }, { "type": "isotropic" }, ], weights=[np.ones((10, )), np.ones((10, )), np.ones((10, ))], bbox=[[0, 0, 0], [1, 1, 1]], ) # Weights are normalised assert np.allclose(1 / 3, phase.weights) # Kernel dict generation succeeds ctx = KernelDictContext() assert phase.kernel_dict(ctx).load()
def test_ramiatm_experiment_surface_adjustment(mode_mono): """Create a Rami4ATM experiment and assert the central patch surface is created with the correct parameters, according to the canopy and atmosphere.""" from mitsuba.core import ScalarTransform4f ctx = KernelDictContext() s = 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=0, ), surface=CentralPatchSurface(central_patch=LambertianSurface(), background_surface=LambertianSurface()), ) expected_trafo = ScalarTransform4f.scale( 1400) * ScalarTransform4f.translate((-0.499642857, -0.499642857, 0.0)) kernel_dict = s.kernel_dict(ctx=ctx) assert np.allclose(kernel_dict["bsdf_surface"]["weight"]["to_uv"].matrix, expected_trafo.matrix)
def test_leaf_cloud_kernel_dict(mode_mono): """Partial unit testing for :meth:`LeafCloud.kernel_dict`.""" ctx = KernelDictContext() cloud_id = "my_cloud" cloud = LeafCloud( id=cloud_id, leaf_positions=[[0, 0, 0], [1, 1, 1]], leaf_orientations=[[1, 0, 0], [0, 1, 0]], leaf_radii=[0.1, 0.1], leaf_reflectance=0.5, leaf_transmittance=0.5, ) kernel_dict = cloud.kernel_dict(ctx=ctx) # The BSDF is bilambertian with the parameters we initially set assert kernel_dict[f"bsdf_{cloud_id}"] == { "type": "bilambertian", "reflectance": {"type": "uniform", "value": 0.5}, "transmittance": {"type": "uniform", "value": 0.5}, } # Leaves are disks for shape_key in [f"{cloud_id}_leaf_0", f"{cloud_id}_leaf_1"]: assert kernel_dict[shape_key]["type"] == "disk" # Kernel dict is valid assert KernelDict(kernel_dict).load()
def test_kernel_dict_duplicate_id(): from eradiate.contexts import KernelDictContext from eradiate.scenes.illumination import illumination_factory # Upon trying to add a scene element, whose ID is already present # in the KernelDict, a Warning must be issued. with pytest.warns(Warning): kd = KernelDict({ "testmeasure": { "type": "directional", "id": "testmeasure", "irradiance": { "type": "interpolated", "wavelengths": [400, 500], "values": [1, 1], }, } }) kd.add( illumination_factory.convert({ "type": "directional", "id": "testmeasure", "irradiance": { "type": "interpolated", "wavelengths": [400, 500], "values": [2, 2], }, }), ctx=KernelDictContext(), )
def test_onedim_experiment_inconsistent_multiradiancemeter(mode_mono): # A MultiRadiancemeter measure must have all origins inside the atmosphere or none. # A mix of both will raise an error. ctx = KernelDictContext() # Construct with typical parameters test_absorption_data_set = eradiate.path_resolver.resolve( "tests/spectra/absorption/us76_u86_4-spectra-4000_25711.nc") exp = OneDimExperiment( surface={"type": "rpv"}, atmosphere={ "type": "heterogeneous", "molecular_atmosphere": { "construct": "ussa1976", "absorption_data_sets": dict(us76_u86_4=test_absorption_data_set), }, }, illumination={ "type": "directional", "zenith": 45.0 }, measures=[ { "type": "multi_radiancemeter", "origins": [[1, 0, 0], [1000000, 0, 0]], "directions": [[0, 0, -1], [0, 0, -1]], "id": "multi_radiancemeter", }, ], ) with pytest.raises(ValueError): exp.kernel_dict(ctx=ctx)
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()
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()
def test_discrete_canopy_padded(mode_mono, tempfile_leaves, tempfile_spheres): """Unit tests for :meth:`.DiscreteCanopy.padded`""" ctx = KernelDictContext() canopy = DiscreteCanopy.leaf_cloud_from_files( id="canopy", size=[100, 100, 30], leaf_cloud_dicts=[ { "instance_filename": tempfile_spheres, "leaf_cloud_filename": tempfile_leaves, "sub_id": "leaf_cloud", } ], ) # The padded canopy object is valid and instantiable padded_canopy = canopy.padded_copy(2) assert padded_canopy assert KernelDict.from_elements(padded_canopy, ctx=ctx).load() # Padded canopy has (2*padding + 1) ** 2 times more instances than original assert len(padded_canopy.instances(ctx)) == len(canopy.instances(ctx)) * 25 # Padded canopy has 2*padding + 1 times larger horizontal size than original assert np.allclose(padded_canopy.size[:2], 5 * canopy.size[:2]) # Padded canopy has same vertical size as original assert padded_canopy.size[2] == canopy.size[2]
def test_rpv(mode_mono): ctx = KernelDictContext() # Default constructor surface = RPVSurface() # Check if produced scene can be instantiated kernel_dict = KernelDict.from_elements(surface, ctx=ctx) assert kernel_dict.load() is not None # Construct from floats surface = RPVSurface(width=ureg.Quantity(1000.0, "km"), rho_0=0.3, k=1.4, g=-0.23) assert np.allclose(surface.width, ureg.Quantity(1e6, ureg.m)) # Construct from mixed spectrum types surface = RPVSurface( width=ureg.Quantity(1000.0, "km"), rho_0=0.3, k={ "type": "uniform", "value": 0.3 }, g={ "type": "interpolated", "wavelengths": [300.0, 800.0], "values": [-0.23, 0.23], }, ) # Check if produced scene can be instantiated assert KernelDict.from_elements(surface, ctx=ctx).load() is not None
def test_discrete_canopy_homogeneous(mode_mono): ctx = KernelDictContext() # The generate_homogeneous() constructor returns a valid canopy object canopy = DiscreteCanopy.homogeneous( n_leaves=1, leaf_radius=0.1, l_horizontal=10, l_vertical=3 ) assert KernelDict.from_elements(canopy, ctx=ctx).load()
def test_homogeneous_phase_function(mode_mono, phase_id, ref): """Supports all available phase function types.""" r = HomogeneousAtmosphere(phase={"type": phase_id}) # The resulting object produces a valid kernel dictionary ctx = KernelDictContext(ref=ref) kernel_dict = KernelDict.from_elements(r, ctx=ctx) assert kernel_dict.load() is not None
def test_distant_flux_direction(modes_all, direction, frame): d = DistantFluxMeasure(direction=direction) ctx = KernelDictContext() to_world = onedict_value(d.kernel_dict(ctx))["to_world"] # The reference frame is rotated as expected assert ek.allclose(to_world.transform_vector([1, 0, 0]), frame[0]) assert ek.allclose(to_world.transform_vector([0, 1, 0]), frame[1]) assert ek.allclose(to_world.transform_vector([0, 0, 1]), frame[2])
def test_isotropic(modes_all): ctx = KernelDictContext() # Default constructor phase = IsotropicPhaseFunction() # Check if produced kernel dict can be instantiated kernel_dict = KernelDict.from_elements(phase, ctx=ctx) assert kernel_dict.load() is not None
def test_multi_radiancemeter_args(mode_mono): # Instantiation succeeds s = MultiRadiancemeterMeasure( origins=[[0, 0, 0]] * 3, directions=[[1, 0, 0], [0, 1, 0], [0, 0, 1]] ) # The kernel dict can be instantiated ctx = KernelDictContext() assert KernelDict.from_elements(s, ctx=ctx).load()
def test_molecular_atmosphere_default(mode_mono, tmpdir, ussa76_approx_test_absorption_data_set): """Default MolecularAtmosphere constructor produces a valid kernel dictionary.""" spectral_ctx = SpectralContext.new(wavelength=550.0) ctx = KernelDictContext(spectral_ctx=spectral_ctx) atmosphere = MolecularAtmosphere(absorption_data_sets=dict( us76_u86_4=ussa76_approx_test_absorption_data_set)) assert KernelDict.from_elements(atmosphere, ctx=ctx).load() is not None
def test_leaf_cloud_from_file(mode_mono, tempfile_leaves): """Unit testing for :meth:`LeafCloud.from_file`.""" ctx = KernelDictContext() # A LeafCloud instance can be loaded from a file on the hard drive cloud = LeafCloud.from_file(tempfile_leaves) assert len(cloud.leaf_positions) == 5 assert np.allclose(cloud.leaf_radii, 0.1 * ureg.m) # Produced kernel dict is valid assert KernelDict.from_elements(cloud, ctx=ctx).load()
def test_homogeneous_width(mode_mono): """ Automatically sets width to ten times the scattering mean free path. """ r = HomogeneousAtmosphere() ctx = KernelDictContext() wavelength = ctx.spectral_ctx.wavelength assert np.isclose( r.kernel_width(ctx), 10.0 / compute_sigma_s_air(wavelength=wavelength) )
def test_heterogeneous_scale(mode_mono, path_to_ussa76_approx_data): ctx = KernelDictContext() d = HeterogeneousAtmosphere( molecular_atmosphere=MolecularAtmosphere.ussa1976( absorption_data_sets={"us76_u86_4": path_to_ussa76_approx_data}, ), particle_layers=[ParticleLayer() for _ in range(2)], scale=2.0, ).kernel_dict(ctx) assert d["medium_atmosphere"]["scale"] == 2.0 assert d.load()
def test_tabulated_basic(modes_all, dataset): # The phase function can be constructed phase = TabulatedPhaseFunction(data=dataset.phase) # Its kernel dict can be generated ctx = KernelDictContext() kernel_dict = phase.kernel_dict(ctx) # The kernel dict can be loaded assert kernel_dict.load()
def test_ramiatm_experiment_real_life(mode_mono): ctx = KernelDictContext() # Construct with typical parameters test_absorption_data_set = eradiate.path_resolver.resolve( "tests/spectra/absorption/us76_u86_4-spectra-4000_25711.nc") # Construct with typical parameters exp = Rami4ATMExperiment( surface={"type": "rpv"}, atmosphere={ "type": "heterogeneous", "molecular_atmosphere": { "construct": "ussa1976", "absorption_data_sets": dict(us76_u86_4=test_absorption_data_set), }, }, canopy={ "type": "discrete_canopy", "construct": "homogeneous", "lai": 3.0, "leaf_radius": 0.1 * ureg.m, "l_horizontal": 10.0 * ureg.m, "l_vertical": 2.0 * ureg.m, }, illumination={ "type": "directional", "zenith": 45.0 }, measures=[ { "type": "distant", "construct": "from_viewing_angles", "zeniths": np.arange(-60, 61, 5), "azimuths": 0.0, "id": "distant", }, { "type": "radiancemeter", "origin": [1, 0, 0], "id": "radiancemeter" }, ], ) assert exp.kernel_dict(ctx=ctx).load() is not None # -- Distant measures get no external medium assert "medium" not in exp.kernel_dict(ctx=ctx)["distant"] # -- Radiancemeter inside the atmosphere must have a medium assigned assert exp.kernel_dict(ctx=ctx)["radiancemeter"]["medium"] == { "type": "ref", "id": "medium_atmosphere", }
def test_kernel_width(mode_mono): obj = MyBlackSurface() # Size value is appropriately used as kernel object size ctx = KernelDictContext() obj.width = 1.0 * ureg.m assert obj.kernel_width(ctx) == 1.0 * ureg.m # Auto size yields 100 km kernel width obj.width = AUTO assert obj.kernel_width(ctx) == 100.0 * ureg.km # Override constrains auto size obj.width = AUTO ctx = KernelDictContext(override_scene_width=100.0 * ureg.m) assert obj.kernel_width(ctx) == 100.0 * ureg.m # Override with set value raises a warning obj.width = 1.0 * ureg.m assert obj.kernel_width(ctx) == 100.0 * ureg.m
def test_particle_layer_eval_ckd_absorbing_only(mode_ckd, tmpdir, absorbing_only, bins): """eval methods return expected values for an absorbing-only layer.""" layer = ParticleLayer(dataset=absorbing_only) spectral_config = CKDMeasureSpectralConfig(bin_set="10nm", bins=bins) spectral_ctx = spectral_config.spectral_ctxs()[0] assert np.all(layer.eval_sigma_s(spectral_ctx).magnitude == 0.0) assert np.all(layer.eval_sigma_a(spectral_ctx).magnitude > 0.0) assert np.all(layer.eval_albedo(spectral_ctx).magnitude == 0.0) ctx = KernelDictContext(spectral_ctx=spectral_ctx) assert layer.eval_width(ctx).magnitude > 0.0