From 5820dece601eaf1f9e78b4a96bb2358a1db215d7 Mon Sep 17 00:00:00 2001 From: deepanshs <21365911+deepanshs@users.noreply.github.com> Date: Sat, 27 May 2023 16:22:15 -0400 Subject: [PATCH 1/2] feature: phase correct constant and linear --- .../operations/operation_documentation.rst | 5 + src/mrsimulator/signal_processor/__init__.py | 1 + .../signal_processor/correction.py | 93 +++++++++++++++++++ .../tests/test_apodization.py | 2 + .../tests/test_signal_processor.py | 2 +- 5 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 src/mrsimulator/signal_processor/correction.py diff --git a/docs/api_py/operations/operation_documentation.rst b/docs/api_py/operations/operation_documentation.rst index a21cbe1da..f08ac619d 100644 --- a/docs/api_py/operations/operation_documentation.rst +++ b/docs/api_py/operations/operation_documentation.rst @@ -27,3 +27,8 @@ Documentation .. autoclass:: Shear .. autoclass:: Scale + +.. currentmodule:: mrsimulator.signal_processor.correction + +.. autoclass:: Phase +.. autoclass:: AutoPhaseCorrect diff --git a/src/mrsimulator/signal_processor/__init__.py b/src/mrsimulator/signal_processor/__init__.py index 5e7e39b6e..beffeb0f5 100644 --- a/src/mrsimulator/signal_processor/__init__.py +++ b/src/mrsimulator/signal_processor/__init__.py @@ -8,6 +8,7 @@ from . import affine as af # noqa:F401 from . import apodization as ap # noqa:F401 from . import baseline as bl # noqa:F401 +from . import correction as cf # noqa:F401 from ._base import Operation diff --git a/src/mrsimulator/signal_processor/correction.py b/src/mrsimulator/signal_processor/correction.py new file mode 100644 index 000000000..f36472ee0 --- /dev/null +++ b/src/mrsimulator/signal_processor/correction.py @@ -0,0 +1,93 @@ +"""The Event class.""" +from typing import ClassVar +from typing import Dict +from typing import Union + +import numpy as np +from pydantic import validator + +from .apodization import Apodization +from .utils import _str_to_quantity +from .utils import CONST + +__author__ = "Deepansh Srivastava" + + +class Correction(Apodization): + module_name: ClassVar[str] = __name__ + + @property + def function(self): + return "correction" + + +class Phase(Correction): + r"""Phase modulate the dependent variables of CSDM dataset following + + .. math:: + f(x) = e^{-i \phi_0} * e^{-i 2 \pi \phi_1 * x}, + + where :math:`x` are the dimension coordinates, and :math:`\phi_0`, + :math:`\phi_1` are the constant and linear phase, respectively. + + Arguments + --------- + + constant: + The constant phase, :math:`\phi_0` in radians. Default is 0. + + linear: + The linear phase, :math:`\phi_1`. Default is 0. + + dim_index: + CSDM dimension index along which the operation applies. The + default is the dimension at index 0. + + dv_index: + CSDM dependent variable index where the operation applies. If + unspecified, the operation applies to all dependent variables. + + Example + ------- + + >>> operation4 = sp.correction.Phase(constant=2.14, linear="25.5 Hz", dim_index=0) + """ + + constant: Union[float, str] = 0 + linear: Union[float, str] = 0 + property_units: Dict = {"constant": CONST, "linear": CONST} + + @validator("constant") + def str_to_quantity_constant(cls, v, values): + return _str_to_quantity(v, values, "constant") + + @validator("linear") + def str_to_quantity_linear(cls, v, values): + return _str_to_quantity(v, values, "linear") + + def fn(self, x): + x = self.get_coordinates_in_units(x, unit=1.0 / self.property_units["linear"]) + vec = np.exp(-1j * self.constant) * np.exp(-1j * 2 * np.pi * self.linear * x) + return vec + + +class AutoPhaseCorrect(Apodization): + def operate(self, dataset): + """Apply the operation function. + + Args: + dataset: A CSDM object. + """ + max_index = np.unravel_index(int(np.argmax(dataset)), dataset.shape[::-1]) + max_index = max_index[::-1] + + for dim_idx, dim in enumerate(dataset.x): + dim.coordinates_offset = -dim.coordinates[max_index[dim_idx]] + + dv_indexes = self._get_dv_indexes(self.dv_index, n=len(dataset.y)) + for index in dv_indexes: + phase_0 = np.angle(dataset[max_index].y[index].components[0]) + multiplier = np.exp(-1j * phase_0) + dataset.y[index].components *= multiplier + + return dataset diff --git a/src/mrsimulator/signal_processor/tests/test_apodization.py b/src/mrsimulator/signal_processor/tests/test_apodization.py index afb2106b9..b3cc51a37 100644 --- a/src/mrsimulator/signal_processor/tests/test_apodization.py +++ b/src/mrsimulator/signal_processor/tests/test_apodization.py @@ -252,6 +252,8 @@ def test_2D_area(): data = np.zeros((256, 128), dtype=float) data[128, 64] = 1.0 csdm_obj = cp.as_csdm(data) + csdm_obj.x[0].period = "1" + csdm_obj.x[1].period = "1" # test00 PS = [ diff --git a/src/mrsimulator/signal_processor/tests/test_signal_processor.py b/src/mrsimulator/signal_processor/tests/test_signal_processor.py index 0809524aa..3e31f28ee 100644 --- a/src/mrsimulator/signal_processor/tests/test_signal_processor.py +++ b/src/mrsimulator/signal_processor/tests/test_signal_processor.py @@ -33,7 +33,7 @@ def test_01(): with pytest.raises(ValueError, match="The dataset must be a CSDM object."): post_sim.apply_operations([]) - dataset = cp.as_csdm(np.arange(20)) + dataset = cp.as_csdm(np.arange(20, dtype=float)) dataset.x[0] = cp.LinearDimension(count=20, increment="10 K") post_sim.apply_operations(dataset) From 1f6a661684616e9ebe3fec04f38eebb2eee27c06 Mon Sep 17 00:00:00 2001 From: deepanshs <21365911+deepanshs@users.noreply.github.com> Date: Sun, 4 Jun 2023 20:38:19 -0400 Subject: [PATCH 2/2] test crash fix --- src/mrsimulator/signal_processor/tests/test_signal_processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mrsimulator/signal_processor/tests/test_signal_processor.py b/src/mrsimulator/signal_processor/tests/test_signal_processor.py index 3e31f28ee..80bdcf99f 100644 --- a/src/mrsimulator/signal_processor/tests/test_signal_processor.py +++ b/src/mrsimulator/signal_processor/tests/test_signal_processor.py @@ -34,7 +34,7 @@ def test_01(): post_sim.apply_operations([]) dataset = cp.as_csdm(np.arange(20, dtype=float)) - dataset.x[0] = cp.LinearDimension(count=20, increment="10 K") + dataset.x[0] = cp.LinearDimension(count=20, increment="10 K", period="200 K") post_sim.apply_operations(dataset) # to dict with units