From b89d78a6ad206f18788190e994d64a8eb83c5f7e Mon Sep 17 00:00:00 2001 From: Simon Heybrock Date: Wed, 13 Apr 2022 10:15:18 +0200 Subject: [PATCH 1/5] Test transformation chain with different transform unit --- tests/nxtransformations_test.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/nxtransformations_test.py b/tests/nxtransformations_test.py index d6dad9b0..fcb772cc 100644 --- a/tests/nxtransformations_test.py +++ b/tests/nxtransformations_test.py @@ -52,6 +52,33 @@ def test_Transformation_with_single_value(nxroot): assert sc.identical(t[()], expected) +def test_chain_with_single_values_and_different_unit(nxroot): + detector = create_detector(nxroot) + detector.create_field('depends_on', sc.scalar('/detector_0/transformations/t1')) + transformations = detector.create_class('transformations', + NX_class.NXtransformations) + value = sc.scalar(6.5, unit='mm') + offset = sc.spatial.translation(value=[1, 2, 3], unit='mm') + vector = sc.vector(value=[0, 0, 1]) + t = value.to(unit='m') * vector + value1 = transformations.create_field('t1', value) + value1.attrs['depends_on'] = 't2' + value1.attrs['transformation_type'] = 'translation' + value1.attrs['offset'] = offset.values + value1.attrs['offset_units'] = str(offset.unit) + value1.attrs['vector'] = vector.value + value2 = transformations.create_field('t2', value.to(unit='cm')) + value2.attrs['depends_on'] = '.' + value2.attrs['transformation_type'] = 'translation' + value2.attrs['vector'] = vector.value + + expected = sc.spatial.affine_transform(value=np.identity(4), unit=t.unit) + expected = expected * sc.spatial.translations( + dims=t.dims, values=2 * t.values, unit=t.unit) + expected = expected * sc.spatial.translation(value=[0.001, 0.002, 0.003], unit='m') + assert sc.identical(detector[...].coords['depends_on'], expected) + + def test_Transformation_with_multiple_values(nxroot): detector = create_detector(nxroot) detector.create_field('depends_on', sc.scalar('/detector_0/transformations/t1')) From 10906e4ef3da0805cc07fa787f4527604e151823 Mon Sep 17 00:00:00 2001 From: Simon Heybrock Date: Wed, 13 Apr 2022 10:21:52 +0200 Subject: [PATCH 2/5] Test transformation chain with time dependence --- tests/nxtransformations_test.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/nxtransformations_test.py b/tests/nxtransformations_test.py index fcb772cc..b513dc9b 100644 --- a/tests/nxtransformations_test.py +++ b/tests/nxtransformations_test.py @@ -108,3 +108,36 @@ def test_Transformation_with_multiple_values(nxroot): assert sc.identical(t.offset, offset) assert sc.identical(t.vector, vector) assert sc.identical(t[()], expected) + + +def test_chain_with_multiple_values(nxroot): + detector = create_detector(nxroot) + detector.create_field('depends_on', sc.scalar('/detector_0/transformations/t1')) + transformations = detector.create_class('transformations', + NX_class.NXtransformations) + log = sc.DataArray( + sc.array(dims=['time'], values=[1.1, 2.2], unit='m'), + coords={'time': sc.array(dims=['time'], values=[11, 22], unit='s')}) + log.coords['time'] = sc.epoch(unit='ns') + log.coords['time'].to(unit='ns') + offset = sc.spatial.translation(value=[1, 2, 3], unit='m') + vector = sc.vector(value=[0, 0, 1]) + t = log * vector + t.data = sc.spatial.translations(dims=t.dims, values=t.values, unit=t.unit) + value1 = transformations.create_class('t1', NX_class.NXlog) + value1['time'] = log.coords['time'] - sc.epoch(unit='ns') + value1['value'] = log.data + value1.attrs['depends_on'] = 't2' + value1.attrs['transformation_type'] = 'translation' + value1.attrs['offset'] = offset.values + value1.attrs['offset_units'] = str(offset.unit) + value1.attrs['vector'] = vector.value + value2 = transformations.create_class('t2', NX_class.NXlog) + value2['time'] = log.coords['time'] - sc.epoch(unit='ns') + value2['value'] = log.data + value2.attrs['depends_on'] = '.' + value2.attrs['transformation_type'] = 'translation' + value2.attrs['vector'] = vector.value + + expected = sc.spatial.affine_transform(value=np.identity(4), unit=t.unit) + expected = t * (t * (offset * expected)) + assert sc.identical(detector[...].coords['depends_on'].value, expected) From ba374ea43ecec587dafa69ea5150fd81555211c2 Mon Sep 17 00:00:00 2001 From: Simon Heybrock Date: Wed, 13 Apr 2022 10:49:05 +0200 Subject: [PATCH 3/5] Test and fix chains with different time units --- src/scippnexus/nxobject.py | 4 ++-- src/scippnexus/nxtransformations.py | 25 ++++++++++++++++----- tests/nxtransformations_test.py | 35 +++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/scippnexus/nxobject.py b/src/scippnexus/nxobject.py index a9f3239c..49a34964 100644 --- a/src/scippnexus/nxobject.py +++ b/src/scippnexus/nxobject.py @@ -187,13 +187,13 @@ def __getitem__(self, select) -> sc.Variable: self._dataset.read_direct(variable.values, source_sel=index) else: variable.values = self._dataset[index] - if self._is_time or _is_time(variable): + if _is_time(variable): starts = [] for name in self.attrs: if (dt := _as_datetime(self.attrs[name])) is not None: starts.append(dt) if self._is_time and len(starts) == 0: - starts.append(sc.epoch(unit='ns')) + starts.append(sc.epoch(unit=self.unit)) if len(starts) == 1: variable = convert_time_to_datetime64( variable, diff --git a/src/scippnexus/nxtransformations.py b/src/scippnexus/nxtransformations.py index d3a850f8..38a2732a 100644 --- a/src/scippnexus/nxtransformations.py +++ b/src/scippnexus/nxtransformations.py @@ -90,6 +90,18 @@ def _interpolate_transform(transform, xnew): fill_value="extrapolate")(xnew=xnew) +def _smaller_unit(a, b): + if a.unit == b.unit: + return a.unit + a = sc.scalar(1.0, unit=a.unit) + b = sc.scalar(1.0, unit=b.unit) + ratio = a / b + if ratio.value < 1.0: + return a.unit + else: + return b.unit + + def get_full_transformation(depends_on: Field) -> Union[None, sc.DataArray]: """ Get the 4x4 transformation matrix for a component, resulting @@ -105,12 +117,15 @@ def get_full_transformation(depends_on: Field) -> Union[None, sc.DataArray]: for transform in transformations: if isinstance(total_transform, sc.DataArray) and isinstance( transform, sc.DataArray): - time = sc.concat([ - total_transform.coords["time"].to(unit='ns', copy=False), - transform.coords["time"].to(unit='ns', copy=False) - ], + unit = _smaller_unit(transform.coords['time'], + total_transform.coords['time']) + total_transform.coords['time'] = total_transform.coords['time'].to( + unit=unit, copy=False) + transform.coords['time'] = transform.coords['time'].to(unit=unit, + copy=False) + time = sc.concat([total_transform.coords["time"], transform.coords["time"]], dim="time") - time = sc.datetimes(values=np.unique(time.values), dims=["time"], unit='ns') + time = sc.datetimes(values=np.unique(time.values), dims=["time"], unit=unit) total_transform = _interpolate_transform(transform, time) \ * _interpolate_transform(total_transform, time) else: diff --git a/tests/nxtransformations_test.py b/tests/nxtransformations_test.py index b513dc9b..16377313 100644 --- a/tests/nxtransformations_test.py +++ b/tests/nxtransformations_test.py @@ -141,3 +141,38 @@ def test_chain_with_multiple_values(nxroot): expected = sc.spatial.affine_transform(value=np.identity(4), unit=t.unit) expected = t * (t * (offset * expected)) assert sc.identical(detector[...].coords['depends_on'].value, expected) + + +def test_chain_with_multiple_values_and_different_time_unit(nxroot): + detector = create_detector(nxroot) + detector.create_field('depends_on', sc.scalar('/detector_0/transformations/t1')) + transformations = detector.create_class('transformations', + NX_class.NXtransformations) + # Making sure to not use nanoseconds since that is used internally and may thus + # mask bugs. + log = sc.DataArray( + sc.array(dims=['time'], values=[1.1, 2.2], unit='m'), + coords={'time': sc.array(dims=['time'], values=[11, 22], unit='s')}) + log.coords['time'] = sc.epoch(unit='us') + log.coords['time'].to(unit='us') + offset = sc.spatial.translation(value=[1, 2, 3], unit='m') + vector = sc.vector(value=[0, 0, 1]) + t = log * vector + t.data = sc.spatial.translations(dims=t.dims, values=t.values, unit=t.unit) + value1 = transformations.create_class('t1', NX_class.NXlog) + value1['time'] = log.coords['time'] - sc.epoch(unit='us') + value1['value'] = log.data + value1.attrs['depends_on'] = 't2' + value1.attrs['transformation_type'] = 'translation' + value1.attrs['offset'] = offset.values + value1.attrs['offset_units'] = str(offset.unit) + value1.attrs['vector'] = vector.value + value2 = transformations.create_class('t2', NX_class.NXlog) + value2['time'] = log.coords['time'].to(unit='ms') - sc.epoch(unit='ms') + value2['value'] = log.data + value2.attrs['depends_on'] = '.' + value2.attrs['transformation_type'] = 'translation' + value2.attrs['vector'] = vector.value + + expected = sc.spatial.affine_transform(value=np.identity(4), unit=t.unit) + expected = t * (t * (offset * expected)) + assert sc.identical(detector[...].coords['depends_on'].value, expected) From 73514964e9d0ce745386b78dbdf1672219241abf Mon Sep 17 00:00:00 2001 From: Simon Heybrock Date: Wed, 13 Apr 2022 10:58:12 +0200 Subject: [PATCH 4/5] Update dependencies --- conda/meta.yaml | 1 + setup.cfg | 1 + 2 files changed, 2 insertions(+) diff --git a/conda/meta.yaml b/conda/meta.yaml index 36a7cef6..2bd5c39c 100644 --- a/conda/meta.yaml +++ b/conda/meta.yaml @@ -11,6 +11,7 @@ requirements: - python>=3.8 - python-dateutil - scipp + - scipy - h5py test: diff --git a/setup.cfg b/setup.cfg index 18b45e56..28483a96 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,6 +19,7 @@ packages = find: install_requires = python-dateutil scipp>=0.12 + scipy # we use scipp.interpolate which depends on this h5py python_requires = >=3.8 include_package_data = True From a553221ad65a206f18c804ee7c7ecad74f6c54b5 Mon Sep 17 00:00:00 2001 From: Simon Heybrock Date: Tue, 19 Apr 2022 09:02:54 +0200 Subject: [PATCH 5/5] Fix check for smaller unit --- src/scippnexus/nxtransformations.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/scippnexus/nxtransformations.py b/src/scippnexus/nxtransformations.py index 38a2732a..42f24a7b 100644 --- a/src/scippnexus/nxtransformations.py +++ b/src/scippnexus/nxtransformations.py @@ -93,9 +93,7 @@ def _interpolate_transform(transform, xnew): def _smaller_unit(a, b): if a.unit == b.unit: return a.unit - a = sc.scalar(1.0, unit=a.unit) - b = sc.scalar(1.0, unit=b.unit) - ratio = a / b + ratio = sc.scalar(1.0, unit=a.unit).to(unit=b.unit) if ratio.value < 1.0: return a.unit else: