Source code for icat_esrf_definitions.tests.test_units

from typing import Any
from typing import List
from typing import Literal

import numpy
from pydantic import Field

from ..models._base.custom_types.metadata.quantity import REGISTRY
from ..models._base.custom_types.metadata.quantity import ensure_dimension
from ..models._base.custom_types.metadata.quantity import ensure_units
from ..models._base.custom_types.quantity import PydanticQuantity
from ..models._base.model import IcatNexusBaseModel
from ..models._base.nxmodels import NXobject
from ..models._base.quantity import MILLIMETERS
from . import compare
from .assert_raises import raises_validationerror
from .validation_error import dimensionality_error
from .validation_error import dimensionless_error
from .validation_error import magnitude_int_error
from .validation_error import sequence_int_error


[docs] def test_fixed_units_and_type(): class TestModel(NXobject): NX_class: Literal["NXparameters"] = Field("NXparameters", alias="@NX_class") length: PydanticQuantity[r"mm", int] # Test unit conversion model = TestModel(length=[1, "m"]) assert str(model.length) == "1000 mm" _assert_model_dump(model, REGISTRY.Quantity(1000, "mm"), 1000, 1000, 1000) model.length = 2, "m" assert str(model.length) == "2000 mm" # Test failing unit conversion with raises_validationerror( dimensionality_error([10, "kg"], "kilogram", "millimeter", "[mass]", "[length]") ): TestModel(length=[10, "kg"]) # Test value type coercion model = TestModel(length=[1.5, "m"]) assert str(model.length) == "1500 mm" _assert_model_dump(model, REGISTRY.Quantity(1500, "mm"), 1500, 1500, 1500) model = TestModel(length=["1.5", "m"]) assert str(model.length) == "1500 mm" _assert_model_dump(model, REGISTRY.Quantity(1500, "mm"), 1500, 1500, 1500) model.length = 2.5, "m" assert str(model.length) == "2500 mm" # Test default units model = TestModel(length=[10, None]) assert str(model.length) == "10 mm" _assert_model_dump(model, REGISTRY.Quantity(10, "mm"), 10, 10, 10) model = TestModel(length=[10]) assert str(model.length) == "10 mm" _assert_model_dump(model, REGISTRY.Quantity(10, "mm"), 10, 10, 10) model.length = 20 assert str(model.length) == "20 mm" model.length = 30 assert str(model.length) == "30 mm" # Test failing type coercion with raises_validationerror(magnitude_int_error(1.5, 1.5)): TestModel(length=1.5) with raises_validationerror(magnitude_int_error(2.5, 2.5)): model.length = 2.5 with raises_validationerror(magnitude_int_error([10, "µm"], 0.01)): model = TestModel(length=[10, "µm"]) with raises_validationerror(magnitude_int_error((10, "µm"), 0.01)): model.length = 10, "µm"
[docs] def test_fixed_dimensions_and_type(): class TestModel(NXobject): NX_class: Literal["NXparameters"] = Field("NXparameters", alias="@NX_class") length: PydanticQuantity[r"[length]", float] mass: PydanticQuantity[r"[mass]", int] model = TestModel(length=[5, "cm"], mass=[3000, "g"]) assert str(model.length) == "5.0 cm" assert str(model.mass) == "3000 g" model.length = 6, "m" model.mass = 10.0, "mg" assert str(model.length) == "6.0 m" assert str(model.mass) == "10 mg"
[docs] def test_fixed_units_and_any_type(): class TestModel(NXobject): NX_class: Literal["NXparameters"] = Field("NXparameters", alias="@NX_class") length: MILLIMETERS model = TestModel(length=[1, "m"]) assert str(model.length) == "1000 mm" _assert_model_dump(model, REGISTRY.Quantity(1000, "mm"), 1000, 1000, 1000) model = TestModel(length=[1.0, "m"]) assert str(model.length) == "1000.0 mm" _assert_model_dump(model, REGISTRY.Quantity(1000, "mm"), 1000, 1000, 1000) model = TestModel(length=[100, "um"]) assert str(model.length) == "0.1 mm" _assert_model_dump(model, REGISTRY.Quantity(0.1, "mm"), 0.1, 0.1, 0.1)
[docs] def test_dimensionless_and_any_type(): class TestModel(NXobject): NX_class: Literal["NXparameters"] = Field("NXparameters", alias="@NX_class") length: PydanticQuantity[None] model = TestModel(length=1) assert str(model.length) == "1" _assert_model_dump(model, REGISTRY.Quantity(1, "dimensionless"), 1, 1, 1) model = TestModel(length=1.0) assert str(model.length) == "1.0" _assert_model_dump(model, REGISTRY.Quantity(1, "dimensionless"), 1.0, 1.0, 1.0) with raises_validationerror(dimensionless_error([10, "kg"])): TestModel(length=[10, "kg"])
[docs] def test_dimensionless_and_fixed_type(): class TestModel(NXobject): NX_class: Literal["NXparameters"] = Field("NXparameters", alias="@NX_class") length: PydanticQuantity[None, int] model = TestModel(length=1) assert str(model.length) == "1" _assert_model_dump(model, REGISTRY.Quantity(1, "dimensionless"), 1, 1, 1) model = TestModel(length=1.0) assert str(model.length) == "1" _assert_model_dump(model, REGISTRY.Quantity(1, "dimensionless"), 1, 1, 1) with raises_validationerror(magnitude_int_error(1.5, 1.5)): TestModel(length=1.5) with raises_validationerror(dimensionless_error([10, "kg"])): TestModel(length=[10, "kg"])
[docs] def test_ensure_units(): q = ensure_units((10, "m"), "cm", float) assert isinstance(q.magnitude, float) assert str(q) == "1000.0 cm" for unit in ("photons/second", "photons/s", "photon/second", "photon/s"): value = ensure_units(100, unit, None) assert value.to("kHz").magnitude == 0.1
[docs] def test_check_dimension(): q = ensure_dimension((10, "keV"), "[energy]", float) assert isinstance(q.magnitude, float) assert str(q) == "10.0 keV" q = ensure_dimension((10, "J"), "[energy]", int) assert isinstance(q.magnitude, int) assert str(q) == "10 J"
[docs] def test_array_units(): class TestModel(NXobject): NX_class: Literal["NXparameters"] = Field("NXparameters", alias="@NX_class") length: PydanticQuantity[r"mm", List[int]] model = TestModel(length=[[1, 2, 3], "mm"]) assert str(model.length) == "[1 2 3] mm" _assert_model_dump( model, REGISTRY.Quantity([1, 2, 3], "mm"), [1, 2, 3], numpy.array([1, 2, 3]), "1 2 3", ),
[docs] def test_array_default_unit(): class TestModel(NXobject): NX_class: Literal["NXparameters"] = Field("NXparameters", alias="@NX_class") length: PydanticQuantity[r"mm", List[float]] model = TestModel(length=[[1, 2, 3], None]) assert str(model.length) == "[1.0 2.0 3.0] mm" _assert_model_dump( model, REGISTRY.Quantity([1, 2, 3], "mm"), [1, 2, 3], numpy.array([1, 2, 3]), "1.0 2.0 3.0", ) model = TestModel(length=[[1, 2, 3]]) assert str(model.length) == "[1.0 2.0 3.0] mm" _assert_model_dump( model, REGISTRY.Quantity([1, 2, 3], "mm"), [1, 2, 3], numpy.array([1, 2, 3]), "1.0 2.0 3.0", )
[docs] def test_array_unit_conversion(): class TestModel(NXobject): NX_class: Literal["NXparameters"] = Field("NXparameters", alias="@NX_class") length: PydanticQuantity[r"mm", List[float]] model = TestModel(length=[[1, 2, 3], "m"]) assert str(model.length) == "[1000.0 2000.0 3000.0] mm" _assert_model_dump( model, REGISTRY.Quantity([1000, 2000, 3000], "mm"), [1000, 2000, 3000], numpy.array([1000, 2000, 3000]), "1000.0 2000.0 3000.0", ) model.length = [0.5, 1.5, 2.5], "m" assert str(model.length) == "[500.0 1500.0 2500.0] mm" _assert_model_dump( model, REGISTRY.Quantity([500.0, 1500.0, 2500.0], "mm"), [500, 1500, 2500], numpy.array([500, 1500, 2500]), "500.0 1500.0 2500.0", )
[docs] def test_array_invalid_unit_conversion(): class TestModel(NXobject): NX_class: Literal["NXparameters"] = Field("NXparameters", alias="@NX_class") length: PydanticQuantity[r"mm", List[float]] with raises_validationerror( dimensionality_error( [[1, 2, 3], "kg"], "kilogram", "millimeter", "[mass]", "[length]" ) ): TestModel(length=[[1, 2, 3], "kg"])
[docs] def test_array_type_coercion(): class TestModel(NXobject): NX_class: Literal["NXparameters"] = Field("NXparameters", alias="@NX_class") length: PydanticQuantity[r"mm", List[int]] model = TestModel(length=[[1, 2, 3], "m"]) assert str(model.length) == "[1000 2000 3000] mm" _assert_model_dump( model, REGISTRY.Quantity([1000, 2000, 3000], "mm"), [1000, 2000, 3000], numpy.array([1000, 2000, 3000]), "1000 2000 3000", ) with raises_validationerror(sequence_int_error([[1.2345, 2.3456], "m"], 1234.5)): TestModel(length=[[1.2345, 2.3456], "m"])
[docs] def test_array_type_coercion_with_strings(): class TestModel(NXobject): NX_class: Literal["NXparameters"] = Field("NXparameters", alias="@NX_class") length: PydanticQuantity[r"mm", List[int]] model = TestModel(length=[["1", "2", "3"], "mm"]) assert str(model.length) == "[1 2 3] mm" _assert_model_dump( model, REGISTRY.Quantity([1, 2, 3], "mm"), [1, 2, 3], numpy.array([1, 2, 3]), "1 2 3", ) model = TestModel(length=[["1", "2", "3"], "m"]) assert str(model.length) == "[1000 2000 3000] mm" _assert_model_dump( model, REGISTRY.Quantity([1000, 2000, 3000], "mm"), [1000, 2000, 3000], numpy.array([1000, 2000, 3000]), "1000 2000 3000", )
[docs] def test_array_single_element_conversion(): class TestModel(NXobject): NX_class: Literal["NXparameters"] = Field("NXparameters", alias="@NX_class") length: PydanticQuantity[r"mm", List[float]] model = TestModel(length=[[1.5], "m"]) assert str(model.length) == "[1500.0] mm" _assert_model_dump( model, REGISTRY.Quantity([1500], "mm"), [1500], numpy.array([1500]), "1500.0" )
[docs] def test_array_empty_and_none_elements(): class TestModel(NXobject): NX_class: Literal["NXparameters"] = Field("NXparameters", alias="@NX_class") length: PydanticQuantity[r"mm", List[float]] model = TestModel(length=[[], None]) assert str(model.length) == "[] mm" _assert_model_dump(model, REGISTRY.Quantity([], "mm"), [], numpy.array([]), "") model = TestModel(length=[[1, None, 3], None]) assert str(model.length) == "[1.0 3.0] mm" _assert_model_dump( model, REGISTRY.Quantity([1, 3], "mm"), [1, 3], numpy.array([1, 3]), "1.0 3.0" )
[docs] def test_array_dimension(): class TestModel(NXobject): NX_class: Literal["NXparameters"] = Field("NXparameters", alias="@NX_class") length: PydanticQuantity[r"[length]", List[float]] model = TestModel(length=[[0.1, 0.2, 0.3], "m"]) assert str(model.length) == "[0.1 0.2 0.3] m" _assert_model_dump( model, REGISTRY.Quantity([0.1, 0.2, 0.3], "m"), [0.1, 0.2, 0.3], numpy.array([0.1, 0.2, 0.3]), "0.1 0.2 0.3", )
[docs] def test_array_dimension_conversion(): class TestModel(NXobject): NX_class: Literal["NXparameters"] = Field("NXparameters", alias="@NX_class") length: PydanticQuantity[r"[length]", List[float]] model = TestModel(length=[[10, 20, 30], "cm"]) assert str(model.length) == "[10.0 20.0 30.0] cm" _assert_model_dump( model, REGISTRY.Quantity([10, 20, 30], "cm"), [10, 20, 30], numpy.array([10, 20, 30]), "10.0 20.0 30.0", )
[docs] def test_array_dimension_empty(): class TestModel(NXobject): NX_class: Literal["NXparameters"] = Field("NXparameters", alias="@NX_class") length: PydanticQuantity[r"[length]", List[float]] model = TestModel(length=[[], "m"]) assert str(model.length) == "[] m" _assert_model_dump(model, REGISTRY.Quantity([], "m"), [], numpy.array([]), "")
[docs] def test_array_dimension_single_element(): class TestModel(NXobject): NX_class: Literal["NXparameters"] = Field("NXparameters", alias="@NX_class") length: PydanticQuantity[r"[length]", List[float]] model = TestModel(length=[[0.5], "m"]) assert str(model.length) == "[0.5] m" _assert_model_dump( model, REGISTRY.Quantity([0.5], "m"), [0.5], numpy.array([0.5]), "0.5" )
def _assert_model_dump( model: IcatNexusBaseModel, python_value: Any, json_value: Any, nexus_value: Any, icat_value: Any, ): compare.assert_equal_serialize_models( model.model_dump(mode="python"), {"NX_class": "NXparameters", "length": python_value}, ) compare.assert_equal_serialize_models( model.model_dump(mode="json"), {"NX_class": "NXparameters", "length": json_value}, ) compare.assert_equal_serialize_models( model.model_dump(mode="nexus"), {"NX_class": "NXparameters", "length": nexus_value}, ) compare.assert_equal_serialize_models( model.model_dump(mode="icat"), {"NX_class": "NXparameters", "length": icat_value}, )