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_interpolated_kernel_dict(modes_all_mono): from mitsuba.core.xml import load_dict ctx = KernelDictContext(spectral_ctx=SpectralContext.new(wavelength=550.0)) spectrum = InterpolatedSpectrum( id="spectrum", quantity="irradiance", wavelengths=[500.0, 600.0], values=[0.0, 1.0], ) # Produced kernel dict is valid assert load_dict(spectrum.kernel_dict(ctx)["spectrum"]) is not None # Unit scaling is properly applied with ucc.override({"radiance": "W/m^2/sr/nm"}): s = InterpolatedSpectrum( quantity="radiance", wavelengths=[500.0, 600.0], values=[0.0, 1.0] ) with uck.override({"radiance": "kW/m^2/sr/nm"}): d = s.kernel_dict(ctx) assert np.allclose(d["spectrum"]["value"], 5e-4)
def test_1050(reflectance, artefact_dir): r""" Results consistency between `mono_double` and `ckd_double` modes ================================================================ This test checks the results consistency between mono_double and ckd_double modes in the [1049.5, 1050.5] nm wavelength bin. Rationale --------- * Sensor: Distant measure covering a plane (11 angular points, 10000 sample per pixel) and targeting (0, 0, 0). * Illumination: Directional illumination with a zenith angle :math:`\theta = 50.0°`. * Surface: a square surface with a Lambertian BRDF with reflectance parameter in :math:`\rho \in [0.0, 0.5, 1.0]`. * Atmosphere: a molecular atmosphere derived from the AFGL (1986) thermophysical properties `us_standard` model, with both scattering and absorption enabled. * Integrator: volumetric path tracer. Expected behaviour ------------------ The [1049.5, 1050.5] nm - integrated BRF results obtained with the `mono_double` and `ckd_double` modes should agree within 5 %. The `mono_double` BRF results are spectrally averaged according to the following formula: .. math:: \hat{q} = \frac{ \int_{ \lambda_{\mathrm{min}} }^{ \lambda_{\mathrm{max}} } q(\lambda) \, \mathrm{d} \lambda }{ \int_{ \lambda_{\mathrm{min}} }^{ \lambda_{\mathrm{max}} } \mathrm{d} \lambda } where * :math:`q` is some spectral quantity (e.g. the BRF) * :math:`\lambda` denotes the wavelength * :math:`[\lambda_{\mathrm{min}},\, \lambda_{\mathrm{max}}]` is the wavelength bin Results ------- Note: :math:`\mathrm{RAD}(x, y) = \left| \dfrac{x - y}{x} \right|` .. image:: generated/plots/test_onedim_ckd_vs_mono_1050_0.0.png :width: 95% .. image:: generated/plots/test_onedim_ckd_vs_mono_1050_0.5.png :width: 95% .. image:: generated/plots/test_onedim_ckd_vs_mono_1050_1.0.png :width: 95% """ # Settings zeniths = np.linspace(-75, 75, 11) spp = 1e4 wavelength_min = 1049.5 * ureg.nm wavelength_max = 1050.5 * ureg.nm wavenumbers = np.linspace(1 / wavelength_max, 1 / wavelength_min, 1001) wavelengths = 1 / wavenumbers # mono mode setting bin_set = "1nm" # ckd mode setting bins = "1050" # ckd mode setting # Determine artefact filename outdir = os.path.join(artefact_dir, "plots") os.makedirs(outdir, exist_ok=True) fname_plot = os.path.join( outdir, f"test_onedim_ckd_vs_mono_{bins}_{reflectance}.png") # Skip if data is missing, but still create an artefact for dataset_id in [ "H2O-spectra-9400_9500", "H2O-spectra-9500_9600", "CO2-spectra-9400_9500", "CO2-spectra-9500_9600", "N2O-spectra-9400_9500", "N2O-spectra-9500_9600", "CO-spectra-9400_9500", "CO-spectra-9500_9600", "CH4-spectra-9400_9500", "CH4-spectra-9500_9600", "O2-spectra-9400_9500", "O2-spectra-9500_9600", ]: test_tools.skipif_data_not_found( "absorption_spectrum", dataset_id, action=lambda: test_tools.missing_artefact(fname_plot), ) # Monochromatic experiment mono_exp = init_mono_experiment( wavelengths=wavelengths, spp=spp, reflectance=reflectance, zeniths=zeniths, ) with uck.override( length="km"): # this is a temporary workaround the 'batman' issue mono_exp.run() mono_results = mono_exp.results["measure"] wavelength_bin_width = (wavelengths.max() - wavelengths.min()).m_as( mono_results.w.attrs["units"]) mono_results_averaged = mono_results.integrate( coord="w") / wavelength_bin_width # CKD experiment ckd_exp = init_ckd_experiment( bin_set=bin_set, bins=bins, spp=spp, reflectance=reflectance, zeniths=zeniths, ) ckd_exp.run() ckd_results = ckd_exp.results["measure"] # Make figure make_figure( ckd_results=ckd_results, mono_results_averaged=mono_results_averaged, fname_plot=fname_plot, wavelength_bin="[1049.5, 1050.5] nm", reflectance=reflectance, ) # Assert averaged mono results are consistent with ckd results within 5 % mono_brf = mono_results_averaged.brf.squeeze().values ckd_brf = ckd_results.brf.squeeze().values assert np.allclose(mono_brf, ckd_brf, rtol=0.05)
def test_target_origin(modes_all): from mitsuba.core import Point3f # TargetPoint: basic constructor with ucc.override({"length": "km"}): t = TargetPoint([0, 0, 0]) assert t.xyz.units == ureg.km with pytest.raises(ValueError): TargetPoint(0) # TargetPoint: check kernel item with ucc.override({"length": "km"}), uck.override({"length": "m"}): t = TargetPoint([1, 2, 0]) assert ek.allclose(t.kernel_item(), [1000, 2000, 0]) # TargetRectangle: basic constructor with ucc.override({"length": "km"}): t = TargetRectangle(0, 1, 0, 1) assert t.xmin == 0.0 * ureg.km assert t.xmax == 1.0 * ureg.km assert t.ymin == 0.0 * ureg.km assert t.ymax == 1.0 * ureg.km with ucc.override({"length": "m"}): t = TargetRectangle(0, 1, 0, 1) assert t.xmin == 0.0 * ureg.m assert t.xmax == 1.0 * ureg.m assert t.ymin == 0.0 * ureg.m assert t.ymax == 1.0 * ureg.m with pytest.raises(ValueError): TargetRectangle(0, 1, "a", 1) with pytest.raises(ValueError): TargetRectangle(0, 1, 1, -1) # TargetRectangle: check kernel item t = TargetRectangle(-1, 1, -1, 1) with uck.override({"length": "mm"}): # Tricky: we can't compare transforms directly kernel_item = t.kernel_item()["to_world"] assert ek.allclose(kernel_item.transform_point(Point3f(-1, -1, 0)), [-1000, -1000, 0]) assert ek.allclose(kernel_item.transform_point(Point3f(1, 1, 0)), [1000, 1000, 0]) assert ek.allclose(kernel_item.transform_point(Point3f(1, 1, 42)), [1000, 1000, 42]) # Factory: basic test with ucc.override({"length": "m"}): t = Target.new("point", xyz=[1, 1, 0]) assert isinstance(t, TargetPoint) assert np.allclose(t.xyz, ureg.Quantity([1, 1, 0], ureg.m)) t = Target.new("rectangle", 0, 1, 0, 1) assert isinstance(t, TargetRectangle) # Converter: basic test with ucc.override({"length": "m"}): t = Target.convert({"type": "point", "xyz": [1, 1, 0]}) assert isinstance(t, TargetPoint) assert np.allclose(t.xyz, ureg.Quantity([1, 1, 0], ureg.m)) t = Target.convert([1, 1, 0]) assert isinstance(t, TargetPoint) assert np.allclose(t.xyz, ureg.Quantity([1, 1, 0], ureg.m)) with pytest.raises(ValueError): Target.convert({"xyz": [1, 1, 0]})