From c9c642bd1483caa4c9236d4f884d34e67423a35c Mon Sep 17 00:00:00 2001 From: Hampus Naesstroem <hampus.naesstroem@physik.hu-berlin.de> Date: Tue, 19 Dec 2023 17:20:34 +0000 Subject: [PATCH] Added Possibility to Assign Dimensionless Pint Quantity to Unitless Metainfo Quantity This makes it possible to assign a `pint.Quantity` with `units.dimensionless == True` to a nomad metainfo quantity with no unit. Changelog: Added --- nomad/metainfo/metainfo.py | 11 ++++++++--- nomad/metainfo/util.py | 4 ++-- tests/metainfo/test_metainfo.py | 31 ++++++++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/nomad/metainfo/metainfo.py b/nomad/metainfo/metainfo.py index ca1da3adfc..68c034e334 100644 --- a/nomad/metainfo/metainfo.py +++ b/nomad/metainfo/metainfo.py @@ -3551,12 +3551,17 @@ class PrimitiveQuantity(Quantity): # versa is not allowed for primitive types. if isinstance(value, pint.Quantity): if self.unit is None: - raise TypeError(f'The quantity {self} does not have a unit, but value {value} has.') - if self.type in MTypes.int: + if value.units.dimensionless: + value = value.magnitude + else: + raise TypeError( + f'The quantity {self} does not have a unit, but value {value} has.') + elif self.type in MTypes.int: raise TypeError( f'Cannot save data with unit conversion into the quantity {self} ' 'with integer data type due to possible precision loss.') - value = value.to(self.unit).magnitude + else: + value = value.to(self.unit).magnitude if self._list: if not isinstance(value, list): diff --git a/nomad/metainfo/util.py b/nomad/metainfo/util.py index 27cbd71228..4a005fe5d6 100644 --- a/nomad/metainfo/util.py +++ b/nomad/metainfo/util.py @@ -701,13 +701,13 @@ def to_numpy(np_type, shape: list, unit: Optional[pint.Unit], definition, value: # the stored unit would not be serialized flexible_unit = getattr(definition, 'flexible_unit', False) - if not flexible_unit and unit is None: + if not flexible_unit and not value.units.dimensionless and unit is None: raise TypeError(f'The quantity {definition} does not have a unit, but value {value} does.') if type(value.magnitude) == np.ndarray and np_type != value.dtype: value = value.astype(np_type) - if not flexible_unit: + if not flexible_unit and not value.units.dimensionless: value = value.to(unit).magnitude else: value = value.magnitude diff --git a/tests/metainfo/test_metainfo.py b/tests/metainfo/test_metainfo.py index bc95914a1e..88ce353075 100644 --- a/tests/metainfo/test_metainfo.py +++ b/tests/metainfo/test_metainfo.py @@ -27,7 +27,7 @@ import pint.quantity from nomad.metainfo.metainfo import ( MSection, MCategory, Section, Quantity, SubSection, Definition, Package, DeriveError, MetainfoError, Environment, Annotation, AnnotationModel, SectionAnnotation, Context, - DefinitionAnnotation, derived) + DefinitionAnnotation, derived, MTypes) from nomad.metainfo.example import Run, VaspRun, System, SystemHash, Parsing, SCC, m_package as example_package from nomad import utils from nomad.units import ureg @@ -592,6 +592,35 @@ class TestM1: assert system.atom_positions.units == ureg.meter assert system.atom_positions[0][0] < 0.1 * ureg.meter + @pytest.mark.parametrize('dtype', MTypes.num) + @pytest.mark.parametrize('shape', [None, [1, 2]]) + def test_setting_with_dimensionless_unit(self, dtype, shape): + if dtype not in MTypes.numpy: + shape = None + class TestSection(MSection): + test_quantity = Quantity(type=dtype, shape=shape) + + test_section = TestSection() + if dtype in MTypes.int: + value = 42 + elif dtype in MTypes.float: + value = 3.14 + elif dtype in MTypes.complex: + value = 1+2j + else: + raise Exception('Unsupported type') + if shape: + value = np.full(shape, value, dtype=dtype) + test_section.test_quantity = value * ureg.dimensionless + if shape: + assert np.all(test_section.test_quantity == value) + elif dtype == np.float16: + assert test_section.test_quantity == pytest.approx(value, 1e-2) + elif dtype == np.float32: + assert test_section.test_quantity == pytest.approx(value, 1e-7) + else: + assert test_section.test_quantity == value + def test_synonym(self): system = System() system.lattice_vectors = [[1.2e-10, 0, 0], [0, 1.2e-10, 0], [0, 0, 1.2e-10]] -- GitLab