예제 #1
0
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"))
예제 #2
0
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
예제 #3
0
def test_compute_sigma_s_air_multidim():
    """
    Supports multiple wavelength and number density values.
    """
    w = ureg.Quantity(np.linspace(280, 2400), "nm")
    n = ureg.Quantity(np.array([_LOSCHMIDT.m_as("m^-3")] * 8), "m^-3")
    result = compute_sigma_s_air(wavelength=w, number_density=n)
    assert result.shape == (len(w), len(n))
예제 #4
0
def test_array_rad_props_profile_eval_dataset(mode_mono):
    """
    Returns a data set.
    """
    p = ArrayRadProfile(
        levels=ureg.Quantity(np.linspace(0, 100, 12), "km"),
        albedo_values=ureg.Quantity(np.linspace(0.0, 1.0, 11),
                                    ureg.dimensionless),
        sigma_t_values=ureg.Quantity(np.linspace(0.0, 1e-5, 11), "m^-1"),
    )
    spectral_ctx = SpectralContext.new()
    assert isinstance(p.eval_dataset(spectral_ctx), xr.Dataset)
예제 #5
0
def test_spherical_to_cartesian():
    r = 2.0
    theta = np.deg2rad(30)
    phi = np.deg2rad(0)
    d = spherical_to_cartesian(r, theta, phi)
    assert np.allclose(d, [1, 0, np.sqrt(3)])

    r = ureg.Quantity(2.0, "km")
    theta = np.deg2rad(60)
    phi = np.deg2rad(30)
    d = spherical_to_cartesian(r, theta, phi)
    assert np.allclose(d,
                       ureg.Quantity(
                           [3.0 / 2.0, np.sqrt(3) / 2.0, 1.0], "km"))
예제 #6
0
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)
예제 #7
0
def test_homogeneous_sigma_s(mode_mono):
    """
    Assigns custom 'sigma_s' value.
    """
    spectral_ctx = SpectralContext.new()
    r = HomogeneousAtmosphere(sigma_s=1e-5)
    assert r.eval_sigma_s(spectral_ctx) == ureg.Quantity(1e-5, ureg.m ** -1)
예제 #8
0
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)
예제 #9
0
def test_on_quantity():
    v = on_quantity(attr.validators.instance_of(float))

    # This should succeed
    v(None, None, 1.)
    v(None, None, ureg.Quantity(1., "km"))

    # This should fail
    @attr.s
    class Attribute:  # Tiny class to pass an appropriate attribute argument
        name = attr.ib()

    attribute = Attribute(name="attribute")

    with pytest.raises(TypeError):
        v(None, attribute, "1.")
    with pytest.raises(TypeError):
        v(None, attribute, ureg.Quantity("1.", "km"))
예제 #10
0
def test_array_rad_props_profile_invalid_values(mode_mono):
    """
    Mismatching shapes in albedo_values and sigma_t_values arrays raise.
    """
    with pytest.raises(ValueError):
        ArrayRadProfile(
            levels=ureg.Quantity(np.linspace(0, 100, 12), "km"),
            albedo_values=np.linspace(0.0, 1.0, 11),
            sigma_t_values=np.linspace(0.0, 1e-5, 10),
        )
예제 #11
0
def test_sigma_s_air_wavelength_dependence():
    """
    Test that the Rayleigh scattering coefficient scales with the 4th power
    of wavelength.
    """

    wavelength = ureg.Quantity(np.linspace(240.0, 2400.0), "nm")
    sigma_s = compute_sigma_s_air(wavelength)
    prod = sigma_s.magnitude * np.power(wavelength, 4)
    assert np.allclose(prod, prod[0], rtol=0.2)
예제 #12
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
예제 #13
0
def test_ramiatm_experiment_kernel_dict(mode_mono, padding):
    from mitsuba.core import ScalarTransform4f

    ctx = KernelDictContext()

    # Surface width is appropriately inherited from canopy, when no atmosphere is present
    s = 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=[
            {
                "type": "distant",
                "id": "distant_measure"
            },
            {
                "type": "radiancemeter",
                "origin": [1, 0, 0],
                "id": "radiancemeter"
            },
        ],
    )
    kernel_scene = s.kernel_dict(ctx)
    assert np.allclose(
        kernel_scene["surface"]["to_world"].transform_point([1, -1, 0]),
        [5 * (2 * padding + 1), -5 * (2 * padding + 1), 0],
    )

    # -- Measures get no external medium assigned
    assert "medium" not in kernel_scene["distant_measure"]
    assert "medium" not in kernel_scene["radiancemeter"]

    # Surface width is appropriately inherited from atmosphere
    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=padding,
        ),
    )
    kernel_dict = s.kernel_dict(ctx)
    assert np.allclose(
        kernel_dict["surface"]["to_world"].matrix,
        ScalarTransform4f.scale([21000, 21000, 1]).matrix,
    )
예제 #14
0
def test_particle_layer_construct_attrs():
    """Assigns parameters to expected values."""
    bottom = ureg.Quantity(1.2, "km")
    top = ureg.Quantity(1.8, "km")
    tau_550 = ureg.Quantity(0.3, "dimensionless")
    layer = ParticleLayer(
        bottom=bottom,
        top=top,
        distribution=UniformParticleDistribution(),
        tau_550=tau_550,
        n_layers=9,
        dataset="tests/radprops/rtmom_aeronet_desert.nc",
    )
    assert layer.bottom == bottom
    assert layer.top == top
    assert isinstance(layer.distribution, UniformParticleDistribution)
    assert layer.tau_550 == tau_550
    assert layer.n_layers == 9
    assert layer.dataset == path_resolver.resolve(
        "tests/radprops/rtmom_aeronet_desert.nc"
    )
예제 #15
0
def test_converter(mode_mono):
    # Dicts are correctly processed
    s = spectrum_factory.converter("radiance")({
        "type": "uniform",
        "value": 1.0
    })
    assert s == UniformSpectrum(quantity="radiance", value=1.0)
    s = spectrum_factory.converter("irradiance")({
        "type": "uniform",
        "value": 1.0
    })
    assert s == UniformSpectrum(quantity="irradiance", value=1.0)

    # Floats and quantities are correctly processed
    s = spectrum_factory.converter("radiance")(1.0)
    assert s == UniformSpectrum(quantity="radiance", value=1.0)
    s = spectrum_factory.converter("radiance")(ureg.Quantity(
        1e6, "W/km^2/sr/nm"))
    assert s == UniformSpectrum(quantity="radiance", value=1.0)
    with pytest.raises(pinttr.exceptions.UnitsError):
        spectrum_factory.converter("irradiance")(ureg.Quantity(
            1, "W/m^2/sr/nm"))
예제 #16
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
예제 #17
0
def test_us76_approx_rad_profile_levels(mode_mono,
                                        us76_approx_test_absorption_data_set):
    """
    Collision coefficients' shape match altitude levels shape.
    """
    p = US76ApproxRadProfile(
        thermoprops=dict(levels=ureg.Quantity(np.linspace(0, 120, 121), "km")),
        absorption_data_set=us76_approx_test_absorption_data_set,
    )

    spectral_ctx = SpectralContext.new()
    for field in ["sigma_a", "sigma_s", "sigma_t", "albedo"]:
        x = getattr(p, f"eval_{field}")(spectral_ctx)
        assert x.shape == (120, )
예제 #18
0
def test_onedim_experiment_kernel_dict(modes_all):
    """
    Test non-trivial kernel dict generation behaviour.
    """
    from mitsuba.core import ScalarTransform4f

    ctx = KernelDictContext()

    # Surface width is appropriately inherited from atmosphere
    exp = OneDimExperiment(atmosphere=HomogeneousAtmosphere(
        width=ureg.Quantity(42.0, "km")))
    kernel_dict = exp.kernel_dict(ctx)
    assert np.allclose(
        kernel_dict["surface"]["to_world"].matrix,
        ScalarTransform4f.scale([21000, 21000, 1]).matrix,
    )

    # Setting atmosphere to None
    exp = OneDimExperiment(
        atmosphere=None,
        surface={
            "type": "lambertian",
            "width": 100.0,
            "width_units": "m"
        },
        measures=[
            {
                "type": "distant",
                "id": "distant_measure"
            },
            {
                "type": "radiancemeter",
                "origin": [1, 0, 0],
                "id": "radiancemeter"
            },
        ],
    )
    # -- Surface width is not overridden
    kernel_dict = exp.kernel_dict(ctx)
    assert np.allclose(
        kernel_dict["surface"]["to_world"].matrix,
        ScalarTransform4f.scale([50, 50, 1]).matrix,
    )
    # -- Atmosphere is not in kernel dictionary
    assert "atmosphere" not in kernel_dict

    # -- Measures get no external medium assigned
    assert "medium" not in kernel_dict["distant_measure"]
    assert "medium" not in kernel_dict["radiancemeter"]
예제 #19
0
def test_cos_angle_to_direction():
    # Old-style call
    assert np.allclose(cos_angle_to_direction(1.0, 0.0), [0, 0, 1])
    assert np.allclose(cos_angle_to_direction(0.0, 0.0), [1, 0, 0])
    assert np.allclose(cos_angle_to_direction(0.5, 0.0),
                       [np.sqrt(3) / 2, 0, 0.5])
    assert np.allclose(
        cos_angle_to_direction(-1.0, ureg.Quantity(135.0, "deg")), [0, 0, -1])

    # Vectorised call
    assert np.allclose(
        cos_angle_to_direction([1.0, 0.0, 0.5, -1.0],
                               [0, 0, 0.0, 0.75 * np.pi]),
        ([0, 0, 1], [1, 0, 0], [np.sqrt(3) / 2, 0, 0.5], [0, 0, -1]),
    )
예제 #20
0
def test_array_rad_props_profile(mode_mono):
    """
    Assigns attributes.
    """
    levels = ureg.Quantity(np.linspace(0, 100, 12), "km")
    albedo_values = ureg.Quantity(np.linspace(0.0, 1.0, 11),
                                  ureg.dimensionless)
    sigma_t_values = ureg.Quantity(np.linspace(0.0, 1e-5, 11), "m^-1")
    p = ArrayRadProfile(
        levels=levels,
        albedo_values=albedo_values,
        sigma_t_values=sigma_t_values,
    )

    spectral_ctx = SpectralContext.new()
    assert isinstance(p.levels, ureg.Quantity)
    assert isinstance(p.eval_albedo(spectral_ctx=spectral_ctx), ureg.Quantity)
    assert isinstance(p.eval_sigma_t(spectral_ctx=spectral_ctx), ureg.Quantity)
    assert isinstance(p.eval_sigma_a(spectral_ctx=spectral_ctx), ureg.Quantity)
    assert isinstance(p.eval_sigma_s(spectral_ctx=spectral_ctx), ureg.Quantity)
    assert np.allclose(p.levels, levels)
    assert np.allclose(p.eval_albedo(spectral_ctx=spectral_ctx), albedo_values)
    assert np.allclose(p.eval_sigma_t(spectral_ctx=spectral_ctx),
                       sigma_t_values)
예제 #21
0
def test_sigma_s_air():
    """
    Test computation of Rayleigh scattering coefficient for air with
    default values.
    """

    ref_cross_section = ureg.Quantity(4.513e-27, "cm**2")
    ref_sigmas = ref_cross_section * _LOSCHMIDT
    expected = ref_sigmas

    # Compare to reference value computed from scattering cross section in
    # Bates (1984) Planetary and Space Science, Volume 32, No. 6.
    assert np.allclose(compute_sigma_s_air(number_density=_LOSCHMIDT),
                       expected,
                       rtol=1e-2)
예제 #22
0
def test_afgl_1986_rad_profile_levels(mode_mono,
                                      afgl_1986_test_absorption_data_sets):
    """
    Collision coefficients' shape match altitude levels shape.
    """
    n_layers = 101
    p = AFGL1986RadProfile(
        thermoprops=dict(
            levels=ureg.Quantity(np.linspace(0.0, 100.0, n_layers + 1), "km")),
        absorption_data_sets=afgl_1986_test_absorption_data_sets,
    )

    spectral_ctx = SpectralContext.new(wavelength=550.0)
    for field in ["sigma_a", "sigma_s", "sigma_t", "albedo"]:
        x = getattr(p, f"eval_{field}")(spectral_ctx)
        assert x.shape == (n_layers, )
예제 #23
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])
예제 #24
0
def test_air_refractive_index():
    """
    Test computation of the air refractive index for different wavelength
    values. We compare the results with the values given in Table III of
    :cite:`Peck1972DispersionAir`.
    """

    wavelength = ureg.Quantity(
        np.array([
            1.6945208, 1.01425728, 0.64402492, 0.54622707, 0.3889751, 0.230289
        ]),
        "micrometer",
    )

    indices = air_refractive_index(wavelength)

    # compute the refractivities in parts per 1e8
    results = (indices - 1) * 1e8

    expected = np.array(
        [27314.19, 27410.90, 27638.092, 27789.843, 28336.843, 30787.68])
    assert np.allclose(results, expected, rtol=1e-5)
예제 #25
0
def test_compute_sigma_a_no_t_coord():
    # data set with no t coordinate
    ds = xr.Dataset(
        data_vars={
            "xs": (("w", "p"), np.random.random((4, 3)), dict(units="cm^2"))
        },
        coords={
            "w": ("w", np.linspace(18000, 18010, 4), dict(units="cm^-1")),
            "p": ("p", np.linspace(90000, 110000, 3), dict(units="Pa")),
        },
    )

    # returns a single absorption coefficient value when input pressure is
    # a scalar
    wl = ureg.Quantity(555.5, "nm")
    x = compute_sigma_a(ds, wl=wl)
    assert isinstance(x, ureg.Quantity)
    assert x.check("[length]^-1")
    assert len(x) == 1

    # handles multiple wavelength values
    x = compute_sigma_a(ds, wl=ureg.Quantity(np.array([555.50, 555.51]), "nm"))
    assert len(x) == 2

    # does not raise when 'fill_value' is used
    x = compute_sigma_a(ds,
                        wl=wl,
                        p=ureg.Quantity(120000.0, "Pa"),
                        fill_values=dict(pt=0.0))

    # raises when wavelength out of range
    with pytest.raises(ValueError):
        compute_sigma_a(ds, wl=ureg.Quantity(560.0, "nm"))

    # raises when pressure out of range
    with pytest.raises(ValueError):
        compute_sigma_a(ds, wl=wl, p=ureg.Quantity(120000.0, "Pa"))

    # returns an array of absorption coefficient values when input pressure is
    # an array
    ds_p = to_quantity(ds.p)
    x = compute_sigma_a(ds=ds,
                        wl=wl,
                        p=np.linspace(ds_p.min(), ds_p.max(), num=10))
    assert isinstance(x, ureg.Quantity)
    assert x.check("[length]^-1")
    assert len(x) == 10

    # absorption coefficient scales with number density
    x = compute_sigma_a(ds,
                        wl=wl,
                        n=ureg.Quantity(np.array([1.0, 2.0]), "m^-3"))
    assert x[1] == 2 * x[0]

    # dataset with no temperature coordinate is not interpolated on temperature
    x = compute_sigma_a(ds,
                        wl=wl,
                        t=ureg.Quantity(200.0, "K"),
                        n=ureg.Quantity(1.0, "m^-3"))
    y = compute_sigma_a(ds,
                        wl=wl,
                        t=ureg.Quantity(300.0, "K"),
                        n=ureg.Quantity(1.0, "m^-3"))
    assert x == y
예제 #26
0
    assert my_singleton1 is my_singleton2


@pytest.mark.parametrize(
    "value, expected",
    [
        ("aaa", False),
        ([0, 1], False),
        ([0, 2], False),
        ([0, 1, 2], True),
        ([0.0, 1, 2], True),
        ([0, 1, "2"], False),
        (np.array([0, 1, 2]), True),
        (np.array([0, 1]), False),
        (np.array(["0", 1, 2]), False),
        (ureg.Quantity([0, 1, 2], "m"), True),
    ],
)
def test_is_vector3(value, expected):
    result = is_vector3(value)
    assert result == expected


def test_natsort():
    assert natsorted(["10", "1.2", "9"]) == ["1.2", "9", "10"]
    assert natsorted(["1.2", "a1", "9"]) == ["1.2", "9", "a1"]


def test_deduplicate():
    assert deduplicate([1, 1, 2, 3], preserve_order=True) == [1, 2, 3]
    assert deduplicate([2, 1, 3, 1], preserve_order=True) == [2, 1, 3]
예제 #27
0
# .. note::
#    When the atmosphere's width is not specified,
#    :class:`~eradiate.scenes.atmosphere.HomogeneousAtmosphere` automatically
#    determines the width so that the atmosphere is optically thick in the
#    horizontal direction.

# %%
# Set the atmosphere's dimensions
# -------------------------------
# Set the atmosphere's height and width using the ``top`` and ``width``
# attributes, respectively:

from eradiate import unit_registry as ureg

atmosphere = eradiate.scenes.atmosphere.HomogeneousAtmosphere(
    top=ureg.Quantity(120, "km"), width=ureg.Quantity(500, "km"))

# %%
# The atmosphere above now has the dimensions 500 x 500 x 120 km.

# %%
# Set the atmosphere's radiative properties
# -----------------------------------------
# Set the atmosphere's scattering and absorption coefficients using the
# ``sigma_s`` and ``sigma_a`` attributes, respectively:

atmosphere = eradiate.scenes.atmosphere.HomogeneousAtmosphere(
    sigma_s=ureg.Quantity(1e-3, "km^-1"), sigma_a=ureg.Quantity(1e-5, "km^-1"))

# %%
# The above atmosphere is now characterised by
예제 #28
0
# thermophysical model.
#
# Refer to the :mod:`~eradiate.radprops` module documentation for a list of
# available radiative properties profiles.

# %%
# Set the atmosphere's level altitude mesh
# ----------------------------------------
# Set the atmosphere's level altitude mesh using the ``levels`` attribute of
# the radiative properties profile object:

import numpy as np

atmosphere = eradiate.scenes.atmosphere.HeterogeneousAtmosphereLegacy(
    profile=eradiate.radprops.US76ApproxRadProfile(thermoprops=dict(
        levels=ureg.Quantity(np.linspace(0, 120, 61), "km"))))

# %%
# This level altitude mesh defines 60 layers, each 2 km-thick.
# The atmosphere's vertical extension automatically inherits that of the
# underlying radiative properties profile object so that, in the example above,
# the atmosphere also extends from 0 to 120 km.
# Use the ``levels`` parameter to control the atmosphere vertical extension as
# well as the number of atmospheric layers and their thicknesses.
#
# For example, you can define a completely arbitrary level altitude mesh with
# four layers of varying thicknesses:

atmosphere = eradiate.scenes.atmosphere.HeterogeneousAtmosphereLegacy(
    profile=eradiate.radprops.US76ApproxRadProfile(thermoprops=dict(
        levels=ureg.Quantity(np.array([0.0, 4.0, 12.0, 64.0, 100.0]), "km"))))
예제 #29
0
class HemisphericalDistantMeasure(Measure):
    """
    Hemispherical distant radiance measure scene element
    [``hdistant``, ``hemispherical_distant``].

    This scene element records radiance leaving the scene in a hemisphere
    defined by its ``direction`` parameter. A distinctive feature of this
    measure is that it samples continuously the direction space instead of
    computing radiance values for a fixed set of directions, thus potentially
    capturing effects much harder to distinguish using *e.g.* the
    :class:`.MultiDistantMeasure` class. On the other side, features located
    at a precise angle will not be captured very well by this measure.

    This measure is useful to get a global view of leaving radiance patterns
    over a surface.

    Notes
    -----
    * Setting the ``target`` parameter is required to get meaningful results.
      Experiment classes should take care of setting it appropriately.
    """

    # --------------------------------------------------------------------------
    #                           Fields and properties
    # --------------------------------------------------------------------------

    _film_resolution: t.Tuple[int, int] = documented(
        attr.ib(
            default=(32, 32),
            validator=attr.validators.deep_iterable(
                member_validator=attr.validators.instance_of(int),
                iterable_validator=validators.has_len(2),
            ),
        ),
        doc="Film resolution as a (width, height) 2-tuple. "
        "If the height is set to 1, direction sampling will be restricted to a "
        "plane.",
        type="array-like",
        default="(32, 32)",
    )

    orientation: pint.Quantity = documented(
        pinttr.ib(
            default=ureg.Quantity(0.0, ureg.deg),
            validator=[
                validators.is_positive, pinttr.validators.has_compatible_units
            ],
            units=ucc.deferred("angle"),
        ),
        doc="Azimuth angle defining the orientation of the sensor in the "
        "horizontal plane.\n"
        "\n"
        "Unit-enabled field (default: ucc['angle']).",
        type="float",
        default="0.0 deg",
    )

    direction = documented(
        attr.ib(
            default=[0, 0, 1],
            converter=np.array,
            validator=validators.is_vector3,
        ),
        doc="A 3-vector orienting the hemisphere mapped by the measure.",
        type="array-like",
        default="[0, 0, 1]",
    )

    flip_directions = documented(
        attr.ib(default=None, converter=attr.converters.optional(bool)),
        doc=" If ``True``, sampled directions will be flipped.",
        type="bool",
        default="False",
    )

    target: t.Optional[Target] = documented(
        attr.ib(
            default=None,
            converter=attr.converters.optional(Target.convert),
            validator=attr.validators.optional(
                attr.validators.instance_of((
                    TargetPoint,
                    TargetRectangle,
                ))),
            on_setattr=attr.setters.pipe(attr.setters.convert,
                                         attr.setters.validate),
        ),
        doc="Target specification. The target can be specified using an "
        "array-like with 3 elements (which will be converted to a "
        ":class:`.TargetPoint`) or a dictionary interpreted by "
        ":meth:`Target.convert() <.Target.convert>`. If set to "
        "``None`` (not recommended), the default target point selection "
        "method is used: rays will not target a particular region of the "
        "scene.",
        type=":class:`.Target` or None",
        init_type=":class:`.Target` or dict or array-like, optional",
    )

    @property
    def film_resolution(self):
        return self._film_resolution

    flags: MeasureFlags = documented(
        attr.ib(default=MeasureFlags.DISTANT,
                converter=MeasureFlags,
                init=False),
        doc=get_doc(Measure, "flags", "doc"),
        type=get_doc(Measure, "flags", "type"),
    )

    @property
    def viewing_angles(self) -> pint.Quantity:
        """
        quantity: Viewing angles computed from stored film coordinates as a
            (width, height, 2) array. The last dimension is ordered as
            (zenith, azimuth).
        """
        # Compute viewing angles at pixel locations
        # Angle computation must match the kernel plugin's direction sampling
        # routine
        angle_units = ucc.get("angle")

        # Compute pixel locations in film coordinates
        xs = (np.linspace(0, 1, self.film_resolution[0], endpoint=False) +
              0.5 / self.film_resolution[0])
        ys = (np.linspace(0, 1, self.film_resolution[1], endpoint=False) +
              0.5 / self.film_resolution[1])

        # Compute corresponding angles
        xy = np.array([(x, y) for x in xs for y in ys])
        angles = direction_to_angles(
            square_to_uniform_hemisphere(xy)).to(angle_units)

        # Normalise azimuth to [0, 2π]
        angles[:, 1] %= 360.0 * ureg.deg

        # Reshape array to match film size on first 2 dimensions
        return angles.reshape((len(xs), len(ys), 2))

    # --------------------------------------------------------------------------
    #                       Kernel dictionary generation
    # --------------------------------------------------------------------------

    def _kernel_dict(self, sensor_id, spp):
        result = {
            "type":
            "distant",
            "id":
            sensor_id,
            "direction":
            self.direction,
            "orientation": [
                np.cos(self.orientation.m_as(ureg.rad)),
                np.sin(self.orientation.m_as(ureg.rad)),
                0.0,
            ],
            "sampler": {
                "type": "independent",
                "sample_count": spp,
            },
            "film": {
                "type": "hdrfilm",
                "width": self.film_resolution[0],
                "height": self.film_resolution[1],
                "pixel_format": "luminance",
                "component_format": "float32",
                "rfilter": {
                    "type": "box"
                },
            },
        }

        if self.target is not None:
            result["ray_target"] = self.target.kernel_item()

        if self.flip_directions is not None:
            result["flip_directions"] = self.flip_directions

        return result

    def kernel_dict(self, ctx: KernelDictContext) -> KernelDict:
        sensor_ids = self._sensor_ids()
        sensor_spps = self._sensor_spps()
        result = KernelDict()

        for spp, sensor_id in zip(sensor_spps, sensor_ids):
            result.data[sensor_id] = self._kernel_dict(sensor_id, spp)

        return result

    # --------------------------------------------------------------------------
    #                        Post-processing information
    # --------------------------------------------------------------------------

    @property
    def var(self) -> t.Tuple[str, t.Dict]:
        return "radiance", {
            "standard_name": "radiance",
            "long_name": "radiance",
            "units": symbol(uck.get("radiance")),
        }
예제 #30
0
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]})