Skip to content

Commit

Permalink
Merge pull request #3357 from ericpre/use_pint_global_registry
Browse files Browse the repository at this point in the history
Use `pint.get_application_registry` to improve interoperability with other modules
  • Loading branch information
jlaehne committed Apr 15, 2024
2 parents a45c709 + 5f61ce9 commit f7ad2a1
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 2 deletions.
1 change: 1 addition & 0 deletions doc/conf.py
Expand Up @@ -329,6 +329,7 @@
"mdp": ("https://mdp-toolkit.github.io/", None),
"matplotlib": ("https://matplotlib.org/stable", None),
"numpy": ("https://numpy.org/doc/stable", None),
"pint": ("https://pint.readthedocs.io/en/stable", None),
"python": ("https://docs.python.org/3", None),
"rsciio": ("https://hyperspy.org/rosettasciio/", None),
"scipy": ("https://docs.scipy.org/doc/scipy", None),
Expand Down
1 change: 1 addition & 0 deletions doc/user_guide/index.rst
Expand Up @@ -32,6 +32,7 @@ User guide
region_of_interest.rst
events.rst
interactive_operations.rst
pint_unit_registry.rst

.. toctree::
:caption: Bibliography
Expand Down
33 changes: 33 additions & 0 deletions doc/user_guide/pint_unit_registry.rst
@@ -0,0 +1,33 @@
.. _pint_unit_registry:

Unit Handling with Pint Quantity
********************************

HyperSpy uses the `pint <https://pint.readthedocs.io>`_ library to handle unit conversion.
To be interoperatable with other modules, HyperSpy uses the default pint :class:`pint.UnitRegistry`
provided by the :func:`pint.get_application_registry` function as described in the sections
`having a shared registry <https://pint.readthedocs.io/en/stable/getting/pint-in-your-projects.html>`_
and `serialization <https://pint.readthedocs.io/en/stable/advanced/serialization.html>`_
of the pint user guide.

For example, in the case of the ``scale_as_quantify`` pint quantity object from :class:`~.axes.UniformDataAxis`,
the default pint :class:`pint.UnitRegistry` is used:

.. code-block:: python
>>> s = hs.signals.Signal1D(np.arange(10))
>>> s.axes_manager[0].scale_as_quantity
<Quantity(1.0, 'dimensionless')>
>>> s.axes_manager[0].scale_as_quantity = '2.5 µm'
>>> s.axes_manager[0].scale_as_quantity
<Quantity(2.5, 'micrometer')>
Then, using :func:`pint.get_application_registry` get the handle of the same instance of :class:`pint.UnitRegistry`
used by HyperSpy and use it to operate on this pint quantity:

>>> import pint
>>> ureg = pint.get_application_registry()
>>> scale = 2E-6 * ureg.meter
>>> s.axes_manager[0].scale_as_quantity += scale
>>> s.axes_manager[0].scale_as_quantity
<Quantity(4.5, 'micrometer')>
4 changes: 2 additions & 2 deletions hyperspy/api.py
Expand Up @@ -149,9 +149,9 @@ def __getattr__(name):
# Special case _ureg to use it as a singleton
elif name == "_ureg":
if "_ureg" not in globals():
from pint import UnitRegistry
import pint

setattr(sys.modules[__name__], "_ureg", UnitRegistry())
setattr(sys.modules[__name__], "_ureg", pint.get_application_registry())
return getattr(sys.modules[__name__], "_ureg")

raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
12 changes: 12 additions & 0 deletions hyperspy/tests/test_import.py
Expand Up @@ -247,3 +247,15 @@ def test_dir_utils_samfire4():
"LocalStrategy",
"ReducedChiSquaredStrategy",
]


def test_pint_default_unit_registry():
import pint

import hyperspy.api as hs

# the pint unit registry used by hyperspy must be the
# same as pint default for interoperability reason
# See https://github.com/hgrecco/pint/issues/108
# and https://github.com/hgrecco/pint/issues/623
assert id(hs._ureg) == id(pint.get_application_registry())
1 change: 1 addition & 0 deletions upcoming_changes/3357.enhancements.rst
@@ -0,0 +1 @@
Use :func:`pint.get_application_registry` to get :class:`pint.UnitRegistry` and facilitate interoperability of pint quantity operation with other modules .

0 comments on commit f7ad2a1

Please sign in to comment.