diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index f5cc5dd2f..e11db0d4c 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -8,6 +8,16 @@ The format is based on `Keep a Changelog `
and this project adheres to `Semantic Versioning `_.
+Unreleased
+==========
+Added
+-----
+- Added Examples for general plotting functions focusing on plotting diffraction patterns (#1108)
+
+Removed
+-------
+- Removed Dependency on pyfai. Azimuthal integration is all handled internally (#1103)
+
2024-06-10 - version 0.19.1
===========================
Fixed
diff --git a/doc/conf.py b/doc/conf.py
index 30a07f0b8..73122f366 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -107,6 +107,8 @@
"navigation_with_keys": True,
"show_toc_level": 2,
"use_edit_page_button": True,
+ "announcement": "Check out the new "
+ "Examples Gallery! ",
"switcher": {
"json_url": "https://pyxem.readthedocs.io/en/latest/_static/switcher.json",
"version_match": version_match,
diff --git a/doc/reference/index.rst b/doc/reference/index.rst
index 954992f3e..6b752cb1f 100644
--- a/doc/reference/index.rst
+++ b/doc/reference/index.rst
@@ -19,10 +19,8 @@ explanations of the functionality.
:toctree: generated
:template: custom-module-template.rst
- detectors
data
components
- dummy_data
generators
libraries
signals
diff --git a/examples/dpc/README.rst b/examples/dpc/README.rst
new file mode 100644
index 000000000..5ac84e75a
--- /dev/null
+++ b/examples/dpc/README.rst
@@ -0,0 +1,3 @@
+Differential Phase Contrast (DPC)
+=================================
+Below is a gallery of examples on how to do Differential Phase Contrast (DPC) in pyxem.
\ No newline at end of file
diff --git a/examples/plotting/README.rst b/examples/plotting/README.rst
new file mode 100644
index 000000000..a24bf9f2c
--- /dev/null
+++ b/examples/plotting/README.rst
@@ -0,0 +1,3 @@
+Plotting
+========
+Below is a gallery of examples on how to plot data in pyxem.
\ No newline at end of file
diff --git a/examples/plotting/fast_plotting_tricks.py b/examples/plotting/fast_plotting_tricks.py
new file mode 100644
index 000000000..4147957e2
--- /dev/null
+++ b/examples/plotting/fast_plotting_tricks.py
@@ -0,0 +1,84 @@
+"""
+Fast Plotting Tricks
+====================
+
+Sometimes you want to quickly plot a diffraction pattern but things seem slow,
+this mostly happens with "large" data that is loaded Lazily.
+
+There are a couple of different ways that plotting in hyperspy/pyxem can be slow:
+
+1. The data is too large and the navigator is being recalculated every time you plot. (i.e. calling s.plot()
+ takes a long time to render)
+2. Dragging the navigator is slow and laggy.
+"""
+
+from pyxem.data import fe_multi_phase_grains
+import numpy as np
+import hyperspy.api as hs
+
+s = fe_multi_phase_grains().as_lazy()
+
+# %%
+# Pre Computing a Navaigator
+# --------------------------
+# To solve the first problem, you can:
+#
+# 1. Precompute the navigator using the :meth:`hyperspy.api.signals.plot` method or
+# the :meth:`hyperspy._signals.LazySignal.compute_navigator` method
+# which will compute the navigator and store it in the signal. This will make plotting faster.
+
+s.compute_navigator()
+print(s.navigator)
+
+# %%
+# Setting a Navigator
+# -------------------
+# 2. You can also set the navigator directly using `s.navigator = ...` if you have a navigator
+# that you want to use. This is useful if a virtual image is created along with the signal when
+# the data is acquired. This will also save the navigator in the metadata. This is similar to the
+# :meth:`hyperspy._signals.LazySignal.compute_navigator`method of the signal and
+# will be saved when the signal is saved.
+
+dummy_navigator = hs.signals.Signal2D(np.ones((20, 20))) # just a dummy navigator
+s.navigator = dummy_navigator
+# or
+s.plot(navigator=dummy_navigator)
+
+# %%
+# Using a Slider to Navigate
+# --------------------------
+# 3. You also don't need to plot the navigator every time you plot the signal. You can set
+# `navigator = "slider"` to avoid plotting the navigator altogether and just use the sliders.
+
+s.plot(navigator="slider")
+
+
+# %%
+# Using the QT Backend and Blitting
+# ---------------------------------
+# To solve the second problem, you can:
+#
+# 1. Use the Qt backend by running `%matplotlib qt` in a Jupyter notebook cell. This will make the
+# navigator much more responsive using "blitting" which only updates the parts of the plot that
+# have changed. Note that the QT backend is not available in Google Colab or when running in a
+# Jupyter notebook on a remote server.
+#
+# Using Shift + Click to Jump
+# ---------------------------
+# 2. You can use the Shift + Click feature to "Jump" to a specific location in the navigator.
+# This is useful if you want to quickly move to a specific location in the navigator without
+# dragging the navigator and loading all the data in between.
+#
+# 3. You can also set the navigator point using the `axes_manager.indices` attribute.
+
+s.axes_manager.indices = (5, 5) # jump to the center of the navigator
+s.plot()
+
+# %%
+# Saving the Data
+# ---------------
+# 4. Finally, you can always consider saving the data in a more performant format like `.zspy`
+# This will make loading the data faster which will in turn make plotting faster!
+s.save("fast_and_compressed.zspy")
+
+hs.load("fast_and_compressed.zspy").plot() # reload the data and plot it
diff --git a/examples/plotting/plotting_a_diffraction_pattern.py b/examples/plotting/plotting_a_diffraction_pattern.py
new file mode 100644
index 000000000..232cafc33
--- /dev/null
+++ b/examples/plotting/plotting_a_diffraction_pattern.py
@@ -0,0 +1,50 @@
+"""
+Plotting a Diffraction Pattern
+==============================
+
+This is sometimes not as straightforward as it seems because you might have a zero
+beam that is too bright and regions of the diffraction pattern that are too dark.
+"""
+
+from pyxem.data import fe_multi_phase_grains
+
+mulit_phase = fe_multi_phase_grains()
+
+# %%
+# We can plot easily using the `plot` method. This will show the diffraction pattern
+# but the plot is static and not interactive. Additionally, the zero beam is too bright
+# and the high k values are too dark.
+
+mulit_phase.plot()
+
+# %%
+# Plotting the diffraction pattern with a logarithmic scale can help to see the high k values
+# But because most of the values are zero, the contrast is not great and is too stretched.
+
+mulit_phase.plot(norm="log")
+
+# %%
+# You can also use the `symlog` norm to plot the diffraction pattern with a logarithmic scale
+# but with a linear scale around zero. This can be useful to see the zero beam and the high k values.
+# additionally you can visualize negative and positive values as well.
+
+mulit_phase.plot(norm="symlog")
+
+# %%
+# We can also set vmin and vmax to control the contrast. This can be useful to see the high k values.
+# A very useful feature is the ability to plot the diffraction pattern with vmax set to the 99th percentile.
+# This sets the maximum value to the 99th percentile of the data. In general this works better than setting
+# norm='log' if you have zero values in the diffraction pattern.
+
+mulit_phase.plot(vmax="99th")
+
+# %%
+# We can also use a gamma correction to control and optimize the contrast.
+
+mulit_phase.plot(norm="power", gamma=0.4)
+
+
+# %%
+# Note: that any of the plots are interactive if you add:
+# %matplotlib ipympl or %matplotlib qt at the beginning of a Jupyter notebook cell.
+# %matplotlib inline will make the plots static.
diff --git a/examples/processing/determining_ellipticity.py b/examples/processing/determining_ellipticity.py
index 573d294ef..640b4517f 100644
--- a/examples/processing/determining_ellipticity.py
+++ b/examples/processing/determining_ellipticity.py
@@ -83,7 +83,7 @@
s.unit = "k_nm^-1"
s.beam_energy = 200
-s.set_ai(affine=affine)
+s.calibration.affine = affine
az = s.get_azimuthal_integral2d(npt=100, inplace=False)
corr = s.apply_affine_transformation(affine, inplace=False)
diff --git a/pyxem/__init__.py b/pyxem/__init__.py
index 04151cb6c..c0f81f6d8 100644
--- a/pyxem/__init__.py
+++ b/pyxem/__init__.py
@@ -26,7 +26,6 @@
except ImportError:
CUPY_INSTALLED = False
from pyxem import components
-from pyxem import detectors
from pyxem import signals
from pyxem import generators
from pyxem import data
@@ -39,7 +38,6 @@
__all__ = [
"components",
- "detectors",
"generators",
"signals",
"data",
diff --git a/pyxem/common.py b/pyxem/common.py
new file mode 100644
index 000000000..b86b41e15
--- /dev/null
+++ b/pyxem/common.py
@@ -0,0 +1,6 @@
+import numpy
+
+if numpy.__version__ >= "1.25.0":
+ from numpy.exceptions import VisibleDeprecationWarning
+else:
+ from numpy import VisibleDeprecationWarning
diff --git a/pyxem/data/__init__.py b/pyxem/data/__init__.py
index 6f392494a..3933bf34a 100644
--- a/pyxem/data/__init__.py
+++ b/pyxem/data/__init__.py
@@ -51,6 +51,7 @@
sped_ag,
pdcusi_insitu,
)
+from pyxem.data.simulated_dpc import simulated_stripes
__all__ = [
"au_grating",
@@ -65,6 +66,7 @@
"si_grains",
"si_grains_simple",
"si_rotations_line",
+ "simulated_stripes",
"fe_multi_phase_grains",
"fe_fcc_phase",
"fe_bcc_phase",
diff --git a/pyxem/detectors/__init__.py b/pyxem/detectors/__init__.py
deleted file mode 100644
index f7a96cb6e..000000000
--- a/pyxem/detectors/__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2016-2024 The pyXem developers
-#
-# This file is part of pyXem.
-#
-# pyXem is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# pyXem is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with pyXem. If not, see .
-
-"""Detectors defined for pyFAI integration"""
-
-from .generic_flat_detector import GenericFlatDetector
-from .medipix_256x256 import Medipix256x256Detector
-from .medipix_515x515 import Medipix515x515Detector
-
-
-__all__ = [
- "GenericFlatDetector",
- "Medipix256x256Detector",
- "Medipix515x515Detector",
-]
diff --git a/pyxem/detectors/generic_flat_detector.py b/pyxem/detectors/generic_flat_detector.py
deleted file mode 100644
index 26d7af438..000000000
--- a/pyxem/detectors/generic_flat_detector.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2016-2024 The pyXem developers
-#
-# This file is part of pyXem.
-#
-# pyXem is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# pyXem is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with pyXem. If not, see .
-
-"""
-A generic flat detector class for azimuthal integration and other similar
-operations using the pyFAI AzimuthalIntegrator for a
-
-"""
-
-from pyFAI.detectors import Detector
-from pyxem.utils._deprecated import deprecated
-
-
-class GenericFlatDetector(Detector):
- """
- A PyFAI Detector class for an arbitrarily sized flat detector (i.e. the
- calibration is assumed to be constant across the detector plane)
-
- The detector class is used for get_azimuthal_integral in a Diffraction2D
- signal. The data is assumed to be flat in the small angle approximation.
-
- PyFAI works in real space coordinates, the pixel size is assumed to be 1 (m)
- and the remaining parameters, via knowing the calibration and wavelength,
- are calculated to have an appropriate physical setup.
-
- Parameters
- ----------
- size_x : int
- The size (in pixels) of the detector in the x coordinate.
- size_y : int
- The size (in pixels) of the detector in the y coordinate.
-
- Examples
- --------
- >>> from pyxem.detectors import GenericFlatDetector
- >>> detector = GenericFlatDetector(512,512)
- >>> detector
- Detector GenericFlatDetector Spline= None
- PixelSize= 1.000e+00, 1.000e+00 m
- """
-
- IS_FLAT = True # this detector is flat
- IS_CONTIGUOUS = True # No gaps: all pixels are adjacents
- API_VERSION = "1.0"
- aliases = ["GenericFlatDetector"]
-
- @deprecated(since="0.18.0", removal="0.20.0")
- def __init__(self, size_x, size_y):
- MAX_SHAPE = size_x, size_y
- Detector.__init__(self, pixel1=1, pixel2=1, max_shape=MAX_SHAPE)
diff --git a/pyxem/detectors/medipix_256x256.py b/pyxem/detectors/medipix_256x256.py
deleted file mode 100644
index 659dbada4..000000000
--- a/pyxem/detectors/medipix_256x256.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2016-2024 The pyXem developers
-#
-# This file is part of pyXem.
-#
-# pyXem is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# pyXem is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with pyXem. If not, see .
-
-"""
-256x256 Medipix Direct Electron Detector class for azimuthal integration
-and other similar operations using pyFAI azimuthalIntegrator.
-"""
-
-from pyFAI.detectors import Detector
-from pyxem.utils._deprecated import deprecated
-
-
-class Medipix256x256Detector(Detector):
- """
- A PyFAI Detector class for a 256256 pixel Medipix direct electron detector.
- The detector class is used for get_azimuthal_integral in a Diffraction2D
- signal. The calibration is not assumed to be constant in scattering vector.
-
- Examples
- --------
- >>> from pyxem.detectors import Medipix256x256Detector
- >>> detector = Medipix256x256Detector()
- >>> detector
- Detector Medipix256x256Detector Spline= None
- PixelSize= 5.500e-05, 5.500e-05 m
- """
-
- IS_FLAT = False # this detector is not flat
- IS_CONTIGUOUS = True # No gaps: all pixels are adjacents
- API_VERSION = "1.0"
- aliases = ["Medipix256x256Detector"]
- MAX_SHAPE = 256, 256
-
- @deprecated(since="0.18.0", removal="0.20.0")
- def __init__(self):
- pixel1 = 55e-6 # 55 micron pixel size in x
- pixel2 = 55e-6 # 55 micron pixel size in y
- Detector.__init__(self, pixel1=pixel1, pixel2=pixel2)
diff --git a/pyxem/detectors/medipix_515x515.py b/pyxem/detectors/medipix_515x515.py
deleted file mode 100644
index b1110d492..000000000
--- a/pyxem/detectors/medipix_515x515.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2016-2024 The pyXem developers
-#
-# This file is part of pyXem.
-#
-# pyXem is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# pyXem is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with pyXem. If not, see .
-
-"""
-515x515 Medipix Direct Electron Detector class for azimuthal integration
-and other similar operations using pyFAI azimuthalIntegrator.
-The three central pixels in both orientations are blanks, so those and three
-and the surrounding ones are "masked".
-"""
-
-from pyFAI.detectors import Detector
-import numpy as np
-from pyxem.utils._deprecated import deprecated
-
-
-class Medipix515x515Detector(Detector):
- """
- A PyFAI Detector class for a 515x515 pixel Medipix Quad direct electron
- detector. A central 5x5 cross is not intepretable, and is stored as a
- calc_mask method.
-
- The detector class is used for get_azimuthal_integral in a Diffraction2D
- signal. The calibration is not assumed to be constant in scattering vector.
-
- Examples
- --------
- >>> from pyxem.detectors import Medipix515x515Detector
- >>> detector = Medipix515x515Detector()
- >>> detector
- Detector Medipix515x515Detector Spline= None
- PixelSize= 5.500e-05, 5.500e-05 m
- """
-
- IS_FLAT = False # this detector is not flat
- IS_CONTIGUOUS = True # No gaps: all pixels are adjacents
- API_VERSION = "1.0"
- aliases = ["Medipix515x515Detector"]
- MAX_SHAPE = 515, 515
-
- @deprecated(since="0.18.0", removal="0.20.0")
- def __init__(self):
- pixel1 = 55e-6 # 55 micron pixel size in x
- pixel2 = 55e-6 # 55 micron pixel size in y
- Detector.__init__(self, pixel1=pixel1, pixel2=pixel2)
-
- def calc_mask(self):
- """Defines a function to define a mask of missing and uninterpretable
- pixels in the detector plane, following
- The missing segment is a 5-wide cross in the middle of the detector.
- """
-
- mask = np.zeros((515, 515))
- mask[255:260, :] = 1
- mask[:, 255:260] = 1
- return mask.astype(np.int8)
diff --git a/pyxem/generators/calibration_generator.py b/pyxem/generators/calibration_generator.py
index d7fb3169d..c6d89ce67 100644
--- a/pyxem/generators/calibration_generator.py
+++ b/pyxem/generators/calibration_generator.py
@@ -31,7 +31,6 @@
generate_ring_pattern,
)
-from pyxem.utils.pyfai_utils import get_azimuthal_integrator, _get_setup
from pyxem.utils._deprecated import deprecated
from pyxem.signals import ElectronDiffraction2D
@@ -94,22 +93,6 @@ def __str__(self):
)
return information_string
- def to_ai(self, wavelength, **kwargs):
- sig_shape = np.shape(self.diffraction_pattern)
- unit = "k_A^-1"
- setup = _get_setup(wavelength, unit, self.diffraction_calibration)
- detector, dist, radial_range = setup
- ai = get_azimuthal_integrator(
- detector=detector,
- detector_distance=dist,
- shape=sig_shape,
- center=self.center,
- affine=self.affine_matrix,
- wavelength=wavelength,
- **kwargs,
- )
- return ai
-
def get_elliptical_distortion(
self,
mask_radius,
diff --git a/pyxem/signals/diffraction2d.py b/pyxem/signals/diffraction2d.py
index 1d13f6453..6baf9b636 100644
--- a/pyxem/signals/diffraction2d.py
+++ b/pyxem/signals/diffraction2d.py
@@ -36,14 +36,8 @@
from pyxem.signals import (
CommonDiffraction,
)
-from pyxem.utils.pyfai_utils import (
- get_azimuthal_integrator,
- _get_radial_extent,
- _get_setup,
-)
+
from pyxem.utils.diffraction import (
- azimuthal_integrate1d,
- azimuthal_integrate2d,
gain_normalise,
remove_bad_pixels,
circular_mask,
@@ -55,9 +49,6 @@
find_beam_center_interpolate,
find_center_of_mass,
find_hot_pixels,
- integrate_radially,
- medfilt_1d,
- sigma_clip,
)
from pyxem.utils._azimuthal_integrations import (
_slice_radial_integrate,
@@ -1652,184 +1643,14 @@ def get_variance(
)
return variance
- """ Methods associated with radial integration, not pyFAI based """
-
- @deprecated(
- since="0.17",
- alternative="pyxem.signals.diffraction2d.azimuthal_integral2d",
- removal="1.0.0",
- )
- def angular_slice_radial_average(
- self,
- angleN=20,
- centre_x=None,
- centre_y=None,
- slice_overlap=None,
- show_progressbar=True,
- ):
- """Do radial average of different angular slices.
- Useful for analysing anisotropy in round diffraction features,
- such as diffraction rings from polycrystalline materials or
- higher order Laue zone rings.
-
- Parameters
- ----------
- angleN : int, default 20
- Number of angular slices. If angleN=4, each slice
- will be 90 degrees. The average will start in the top left
- corner (0, 0) when plotting using s.plot(), and go clockwise.
- centre_x, centre_y : int or NumPy array, optional
- If given as int, all the diffraction patterns will have the same
- centre position. Each diffraction pattern can also have different
- centre position, by passing a NumPy array with the same dimensions
- as the navigation axes.
- Note: in either case both x and y values must be given. If one is
- missing, both will be set from the signal (0., 0.) positions.
- If no values are given, the (0., 0.) positions in the signal will
- be used.
- slice_overlap : float, optional
- Amount of overlap between the slices, given in fractions of
- angle slice (0 to 1). For angleN=4, each slice will be 90
- degrees. If slice_overlap=0.5, each slice will overlap by 45
- degrees on each side. The range of the slices will then be:
- (-45, 135), (45, 225), (135, 315) and (225, 45).
- Default off: meaning there is no overlap between the slices.
- show_progressbar : bool
- Default True
-
- Returns
- -------
- signal : HyperSpy 1D signal
- With one more navigation dimensions (the angular slices) compared
- to the input signal.
-
- Examples
- --------
- >>> s = pxm.data.dummy_data.get_holz_simple_test_signal()
- >>> s_com = s.center_of_mass(show_progressbar=False)
- >>> s_ar = s.angular_slice_radial_average(
- ... angleN=10, centre_x=s_com.inav[0].data,
- ... centre_y=s_com.inav[1].data, slice_overlap=0.2,
- ... show_progressbar=False)
- >>> s_ar.plot() # doctest: +SKIP
-
- """
- signal_list = []
- angle_list = []
- if slice_overlap is None:
- slice_overlap = 0
- else:
- if (slice_overlap < 0) or (slice_overlap > 1):
- raise ValueError(
- "slice_overlap is {0}. But must be between "
- "0 and 1".format(slice_overlap)
- )
- angle_step = 2 * np.pi / angleN
- for i in range(angleN):
- angle0 = (angle_step * i) - (angle_step * slice_overlap)
- angle1 = (angle_step * (i + 1)) + (angle_step * slice_overlap)
- angle_list.append((angle0, angle1))
- if (centre_x is None) or (centre_y is None):
- centre_x, centre_y = pst._make_centre_array_from_signal(self)
- elif (not isiterable(centre_x)) or (not isiterable(centre_y)):
- centre_x, centre_y = pst._make_centre_array_from_signal(
- self, x=centre_x, y=centre_y
- )
-
- for angle in tqdm(angle_list, disable=(not show_progressbar)):
- mask_array = self.angular_mask(
- angle[0], angle[1], centre_x_array=centre_x, centre_y_array=centre_y
- )
- s_r = self.radial_average(
- centre_x=centre_x,
- centre_y=centre_y,
- mask_array=mask_array,
- show_progressbar=show_progressbar,
- )
- signal_list.append(s_r)
- angle_scale = angle_list[1][1] - angle_list[0][1]
- signal = hs.stack(signal_list, new_axis_name="Angle slice")
- signal.axes_manager["Angle slice"].offset = angle_scale / 2
- signal.axes_manager["Angle slice"].scale = angle_scale
- signal.axes_manager["Angle slice"].units = "Radians"
- signal.axes_manager[-1].name = "Scattering angle"
- return signal
-
- """ Methods associated with radial integration, pyFAI based """
-
- @property
- def ai(self):
- try:
- return self.metadata.Signal["ai"]
- except AttributeError:
- raise ValueError("ai property is not currently set")
-
- @deprecated(
- since="0.18",
- removal="1.0.0",
- alternative="pyxem.signals.diffraction2d.calibration",
- )
- def set_ai(
- self, center=None, wavelength=None, affine=None, radial_range=None, **kwargs
- ):
- """This function sets the .ai parameter which stores an ~pyfai.AzimuthalIntegrator object based on
- the current calibration applied to the diffraction pattern.
-
- Parameters
- --------
- center: (x,y) or None
- The center of the diffraction pattern. If None, the center is the middle of the image.
- wavelength: float
- The wavelength of the energy in 1/meters. For proper treatment of Ewald Sphere
- affine: numpy.Array 3x3
- A 3x3 array which describes the affine distortion of the pattern. This is translated
- to a spline interpolation which is used in the pyFAI implementations
- radial_range: (start,stop)
- The start and stop of the radial range in real units
-
- Returns
- -------
- None :
- The metadata item Signal.ai is set
-
- """
- if wavelength is None and self.unit not in ["2th_deg", "2th_rad"]:
- raise ValueError(
- "if the unit is not '2th_deg' or '2th_rad' then a wavelength must be given."
- )
-
- pixel_scale = [
- self.axes_manager.signal_axes[0].scale,
- self.axes_manager.signal_axes[1].scale,
- ]
-
- sig_shape = self.axes_manager.signal_shape
- setup = _get_setup(wavelength, self.unit, pixel_scale, radial_range)
- detector, dist, radial_range = setup
- ai = get_azimuthal_integrator(
- detector=detector,
- detector_distance=dist,
- shape=sig_shape,
- center=center,
- affine=affine,
- wavelength=wavelength,
- **kwargs,
- )
- self.metadata.set_item("Signal.ai", ai)
- return None
+ """ Methods associated with radial integration """
- @deprecated_argument(
- name="lazy_result", since="0.14", removal="1.0.0", alternative="lazy_output"
- )
def get_azimuthal_integral1d(
self,
npt,
mask=None,
radial_range=None,
- azimuth_range=None,
inplace=False,
- method="splitpixel_pyxem",
- sum=False,
**kwargs,
):
"""Creates a polar reprojection using pyFAI's azimuthal integrate 2d. This method is designed
@@ -1852,13 +1673,7 @@ def get_azimuthal_integral1d(
from -pi to pi
inplace : bool
If the signal is overwritten or copied to a new signal
- method : str
- Can be “numpy”, “cython”, “BBox” or “splitpixel”, “lut”, “csr”,
- “nosplit_csr”, “full_csr”, “lut_ocl” and “csr_ocl” if you want
- to go on GPU. To Specify the device: “csr_ocl_1,2”
- sum : bool
- If true returns the pixel split sum rather than the azimuthal integration which
- gives the mean.
+
Other Parameters
-------
@@ -1898,52 +1713,22 @@ def get_azimuthal_integral1d(
--------
pyxem.signals.Diffraction2D.get_azimuthal_integral2d
"""
- usepyfai = method not in ["splitpixel_pyxem"]
- if not usepyfai:
- # get_slices1d should be sped up in the future by
- # getting rid of shapely and using numba on the for loop
- indexes, facts, factor_slices, radial_range = self.calibration.get_slices1d(
- npt, radial_range=radial_range
- )
- if mask is None:
- mask = self.calibration.mask
- integration = self.map(
- _slice_radial_integrate1d,
- indexes=indexes,
- factors=facts,
- factor_slices=factor_slices,
- inplace=inplace,
- mask=mask,
- **kwargs,
- )
- else:
- if "wavelength" in kwargs:
- warnings.warn(
- "The wavelength parameter was removed in 0.14. The wavelength "
- "can be set using the `set_ai` function or using `s.beam_energy`"
- " for `ElectronDiffraction2D` signals"
- )
- kwargs.pop("wavelength")
-
- sig_shape = self.axes_manager.signal_shape
- if radial_range is None:
- radial_range = _get_radial_extent(
- ai=self.ai, shape=sig_shape, unit=self.unit
- )
- radial_range[0] = 0
- integration = self.map(
- azimuthal_integrate1d,
- azimuthal_integrator=self.ai,
- npt_rad=npt,
- azimuth_range=azimuth_range,
- radial_range=radial_range,
- method=method,
- inplace=inplace,
- unit=self.unit,
- mask=mask,
- sum=sum,
- **kwargs,
- )
+ # get_slices1d should be sped up in the future by
+ # getting rid of shapely and using numba on the for loop
+ indexes, facts, factor_slices, radial_range = self.calibration.get_slices1d(
+ npt, radial_range=radial_range
+ )
+ if mask is None:
+ mask = self.calibration.mask
+ integration = self.map(
+ _slice_radial_integrate1d,
+ indexes=indexes,
+ factors=facts,
+ factor_slices=factor_slices,
+ inplace=inplace,
+ mask=mask,
+ **kwargs,
+ )
s = self if inplace else integration
k_axis = s.axes_manager.signal_axes[0]
if not isinstance(k_axis, UniformDataAxis):
@@ -1963,9 +1748,6 @@ def get_azimuthal_integral2d(
radial_range=None,
azimuth_range=None,
inplace=False,
- method="splitpixel_pyxem",
- sum=False,
- correctSolidAngle=True,
**kwargs,
):
"""Creates a polar reprojection using pyFAI's azimuthal integrate 2d. This method is designed
@@ -1990,11 +1772,6 @@ def get_azimuthal_integral2d(
from -pi to pi
inplace: bool
If the signal is overwritten or copied to a new signal
- method: str
- Can be “numpy”, “cython”, “BBox” or “splitpixel”, “lut”, “csr”,
- “nosplit_csr”, “full_csr”, “lut_ocl” and “csr_ocl” if you want
- to go on GPU. To Specify the device: “csr_ocl_1,2”. For pure
- pyxem based methods use "splitpixel_pyxem".
sum: bool
If true the radial integration is returned rather then the Azimuthal Integration.
correctSolidAngle: bool
@@ -2046,74 +1823,47 @@ def get_azimuthal_integral2d(
if azimuth_range is None:
azimuth_range = (-np.pi, np.pi)
- usepyfai = method not in ["splitpixel_pyxem"]
- if not usepyfai:
- # get_slices2d should be sped up in the future by
- # getting rid of shapely and using numba on the for loop
- slices, factors, factors_slice, radial_range = (
- self.calibration.get_slices2d(
- npt,
- npt_azim,
- radial_range=radial_range,
- azimuthal_range=azimuth_range,
- )
+ # get_slices2d should be sped up in the future by
+ # getting rid of shapely and using numba on the for loop
+ slices, factors, factors_slice, radial_range = self.calibration.get_slices2d(
+ npt,
+ npt_azim,
+ radial_range=radial_range,
+ azimuthal_range=azimuth_range,
+ )
+ if self._gpu and CUPY_INSTALLED: # pragma: no cover
+ from pyxem.utils._azimuthal_integrations import (
+ _slice_radial_integrate_cupy,
)
- if self._gpu and CUPY_INSTALLED: # pragma: no cover
- from pyxem.utils._azimuthal_integrations import (
- _slice_radial_integrate_cupy,
- )
-
- slices = cp.asarray(slices)
- factors = cp.asarray(factors)
- factors_slice = cp.asarray(factors_slice)
- integration = self._blockwise(
- _slice_radial_integrate_cupy,
- slices=slices,
- factors=factors,
- factors_slice=factors_slice,
- npt=npt,
- npt_azim=npt_azim,
- inplace=inplace,
- signal_shape=(npt, npt_azim),
- mask=mask,
- dtype=float,
- **kwargs,
- )
- else:
- if mask is None:
- mask = self.calibration.mask
- integration = self.map(
- _slice_radial_integrate,
- slices=slices,
- factors=factors,
- factors_slice=factors_slice,
- npt_rad=npt,
- npt_azim=npt_azim,
- inplace=inplace,
- mask=mask,
- **kwargs,
- )
+ slices = cp.asarray(slices)
+ factors = cp.asarray(factors)
+ factors_slice = cp.asarray(factors_slice)
+ integration = self._blockwise(
+ _slice_radial_integrate_cupy,
+ slices=slices,
+ factors=factors,
+ factors_slice=factors_slice,
+ npt=npt,
+ npt_azim=npt_azim,
+ inplace=inplace,
+ signal_shape=(npt, npt_azim),
+ mask=mask,
+ dtype=float,
+ **kwargs,
+ )
else:
- sig_shape = self.axes_manager.signal_shape
- if radial_range is None:
- radial_range = _get_radial_extent(
- ai=self.ai, shape=sig_shape, unit=self.unit
- )
- radial_range[0] = 0
+ if mask is None:
+ mask = self.calibration.mask
integration = self.map(
- azimuthal_integrate2d,
- azimuthal_integrator=self.ai,
+ _slice_radial_integrate,
+ slices=slices,
+ factors=factors,
+ factors_slice=factors_slice,
npt_rad=npt,
npt_azim=npt_azim,
- azimuth_range=azimuth_range,
- radial_range=radial_range,
- method=method,
inplace=inplace,
- unit=self.unit,
mask=mask,
- sum=sum,
- correctSolidAngle=correctSolidAngle,
**kwargs,
)
@@ -2139,320 +1889,6 @@ def get_azimuthal_integral2d(
return integration
- def get_radial_integral(
- self,
- npt,
- npt_rad,
- mask=None,
- radial_range=None,
- azimuth_range=None,
- inplace=False,
- method="splitpixel",
- sum=False,
- correctSolidAngle=True,
- **kwargs,
- ):
- """Calculate the radial integrated profile curve as I = f(chi)
-
- Parameters
- ----------
- npt: int
- The number of radial points to calculate
- npt_rad: int
- number of points in the radial space. Too few points may lead to huge rounding errors.
- mask: boolean array or BaseSignal
- A boolean mask to apply to the data to exclude some points.
- If mask is a BaseSignal then it is iterated over as well.
- radial_range: None or (float, float)
- The radial range over which to perform the integration. Default is
- the full frame
- azimuth_range:None or (float, float)
- The azimuthal range over which to perform the integration. Default is
- from -pi to pi
- inplace: bool
- If the signal is overwritten or copied to a new signal
- method: str
- Can be “numpy”, “cython”, “BBox” or “splitpixel”, “lut”, “csr”,
- “nosplit_csr”, “full_csr”, “lut_ocl” and “csr_ocl” if you want
- to go on GPU. To Specify the device: “csr_ocl_1,2”
- sum: bool
- If true the radial integration is returned rather then the Azimuthal Integration.
- correctSolidAngle: bool
- Account for Ewald sphere or not. From PYFAI.
-
- Other Parameters
- -------
- dummy: float
- Value for dead/masked pixels
- delta_dummy: float
- Percision value for dead/masked pixels
- correctSolidAngle: bool
- Correct for the solid angle of each pixel if True
- dark: ndarray
- The dark noise image
- flat: ndarray
- The flat field correction image
- safe: bool
- Do some extra checks to ensure LUT/CSR is still valid. False is faster.
- show_progressbar: bool
- If True shows a progress bar for the mapping function
-
- Returns
- -------
- polar: PolarDiffraction2D
- A polar diffraction signal
-
- Examples
- --------
- Basic case using "2th_deg" units (no wavelength needed)
-
- >>> ds.unit = "2th_deg"
- >>> ds.set_ai()
- >>> ds.get_radial_integral(npt=100, npt_rad=400)
-
- Basic case using a curved Ewald Sphere approximation and pyXEM units
- (wavelength needed)
-
- >>> ds.unit = "k_nm^-1" # setting units
- >>> ds.set_ai(wavelength=2.5e-12)
- >>> ds.get_radial_integral(npt=100,npt_rad=400)
-
- """
- sig_shape = self.axes_manager.signal_shape
- if radial_range is None:
- radial_range = _get_radial_extent(
- ai=self.ai, shape=sig_shape, unit=self.unit
- )
- radial_range[0] = 0
- integration = self.map(
- integrate_radially,
- azimuthal_integrator=self.ai,
- npt=npt,
- npt_rad=npt_rad,
- azimuth_range=azimuth_range,
- radial_range=radial_range,
- method=method,
- inplace=inplace,
- radial_unit=self.unit,
- mask=mask,
- sum=sum,
- correctSolidAngle=correctSolidAngle,
- **kwargs,
- )
-
- s = self if inplace else integration
-
- # Dealing with axis changes
- k_axis = s.axes_manager.signal_axes[0]
- k_axis.name = "Radius"
- k_axis.scale = (radial_range[1] - radial_range[0]) / npt
- k_axis.offset = radial_range[0]
-
- return integration
-
- def get_medfilt1d(
- self,
- npt_rad=1028,
- npt_azim=512,
- mask=None,
- inplace=False,
- method="splitpixel",
- sum=False,
- correctSolidAngle=True,
- **kwargs,
- ):
- """Calculate the radial integrated profile curve as I = f(chi)
-
- Parameters
- ----------
- npt_rad: int
- The number of radial points.
- npt_azim: int
- The number of radial points
- mask: boolean array or BaseSignal
- A boolean mask to apply to the data to exclude some points.
- If mask is a BaseSignal then it is iterated over as well.
- inplace: bool
- If the signal is overwritten or copied to a new signal
- method: str
- Can be “numpy”, “cython”, “BBox” or “splitpixel”, “lut”, “csr”,
- “nosplit_csr”, “full_csr”, “lut_ocl” and “csr_ocl” if you want
- to go on GPU. To Specify the device: “csr_ocl_1,2”
- sum: bool
- If true the radial integration is returned rather then the Azimuthal Integration.
- correctSolidAngle: bool
- Account for Ewald sphere or not. From PYFAI.
-
- Other Parameters
- -------
- dummy: float
- Value for dead/masked pixels
- delta_dummy: float
- Percision value for dead/masked pixels
- correctSolidAngle: bool
- Correct for the solid angle of each pixel if True
- dark: ndarray
- The dark noise image
- flat: ndarray
- The flat field correction image
- safe: bool
- Do some extra checks to ensure LUT/CSR is still valid. False is faster.
- show_progressbar: bool
- If True shows a progress bar for the mapping function
-
-
- Returns
- -------
- polar: PolarDiffraction2D
- A polar diffraction signal
-
- Examples
- --------
- Basic case using "2th_deg" units (no wavelength needed)
-
- >>> ds.unit = "2th_deg"
- >>> ds.set_ai()
- >>> ds.get_radial_integral(npt=100, npt_rad=400)
-
- Basic case using a curved Ewald Sphere approximation and pyXEM units
- (wavelength needed)
-
- >>> ds.unit = "k_nm^-1" # setting units
- >>> ds.set_ai(wavelength=2.5e-12)
- >>> ds.get_radial_integral(npt=100,npt_rad=400)
-
- """
- sig_shape = self.axes_manager.signal_shape
- radial_range = _get_radial_extent(ai=self.ai, shape=sig_shape, unit=self.unit)
- radial_range[0] = 0
- integration = self.map(
- medfilt_1d,
- azimuthal_integrator=self.ai,
- npt_rad=npt_rad,
- npt_azim=npt_azim,
- method=method,
- inplace=inplace,
- unit=self.unit,
- mask=mask,
- correctSolidAngle=correctSolidAngle,
- **kwargs,
- )
-
- s = self if inplace else integration
-
- # Dealing with axis changes
- k_axis = s.axes_manager.signal_axes[0]
- k_axis.name = "Radius"
- k_axis.scale = (radial_range[1] - radial_range[0]) / npt_rad
- # k_axis.units = unit.unit_symbol
- k_axis.offset = radial_range[0]
-
- return integration
-
- def sigma_clip(
- self,
- npt_rad=1028,
- npt_azim=512,
- mask=None,
- thres=3,
- max_iter=5,
- inplace=False,
- method="splitpixel",
- sum=False,
- correctSolidAngle=True,
- **kwargs,
- ):
- """Perform the 2D integration and perform a sigm-clipping
- iterative filter along each row. see the doc of scipy.stats.sigmaclip for the options.
-
- Parameters
- ----------
- npt_rad: int
- The number of radial points.
- npt_azim: int
- The number of radial points
- mask: boolean array or BaseSignal
- A boolean mask to apply to the data to exclude some points.
- If mask is a BaseSignal then it is iterated over as well.
- inplace: bool
- If the signal is overwritten or copied to a new signal
- method: str
- Can be “numpy”, “cython”, “BBox” or “splitpixel”, “lut”, “csr”,
- “nosplit_csr”, “full_csr”, “lut_ocl” and “csr_ocl” if you want
- to go on GPU. To Specify the device: “csr_ocl_1,2”
- sum: bool
- If true the radial integration is returned rather then the Azimuthal Integration.
- correctSolidAngle: bool
- Account for Ewald sphere or not. From PYFAI.
-
- Other Parameters
- -------
- dummy: float
- Value for dead/masked pixels
- delta_dummy: float
- Percision value for dead/masked pixels
- correctSolidAngle: bool
- Correct for the solid angle of each pixel if True
- dark: ndarray
- The dark noise image
- flat: ndarray
- The flat field correction image
- safe: bool
- Do some extra checks to ensure LUT/CSR is still valid. False is faster.
- show_progressbar: bool
- If True shows a progress bar for the mapping function
-
-
- Returns
- -------
- polar: PolarDiffraction2D
- A polar diffraction signal
-
- Examples
- --------
- Basic case using "2th_deg" units (no wavelength needed)
-
- >>> ds.unit = "2th_deg"
- >>> ds.set_ai()
- >>> ds.get_radial_integral(npt=100, npt_rad=400)
-
- Basic case using a curved Ewald Sphere approximation and pyXEM units
- (wavelength needed)
-
- >>> ds.unit = "k_nm^-1" # setting units
- >>> ds.set_ai(wavelength=2.5e-12)
- >>> ds.get_radial_integral(npt=100,npt_rad=400)
-
- """
- sig_shape = self.axes_manager.signal_shape
- radial_range = _get_radial_extent(ai=self.ai, shape=sig_shape, unit=self.unit)
- radial_range[0] = 0
- integration = self.map(
- sigma_clip,
- azimuthal_integrator=self.ai,
- npt_rad=npt_rad,
- npt_azim=npt_azim,
- method=method,
- max_iter=max_iter,
- thres=thres,
- inplace=inplace,
- unit=self.unit,
- mask=mask,
- correctSolidAngle=correctSolidAngle,
- **kwargs,
- )
-
- s = self if inplace else integration
-
- # Dealing with axis changes
- k_axis = s.axes_manager.signal_axes[0]
- k_axis.name = "Radius"
- k_axis.scale = (radial_range[1] - radial_range[0]) / npt_rad
- # k_axis.units = unit.unit_symbol
- k_axis.offset = radial_range[0]
-
- return integration
-
class LazyDiffraction2D(LazySignal, Diffraction2D):
pass
diff --git a/pyxem/signals/electron_diffraction2d.py b/pyxem/signals/electron_diffraction2d.py
index bdac91587..869bc9e12 100644
--- a/pyxem/signals/electron_diffraction2d.py
+++ b/pyxem/signals/electron_diffraction2d.py
@@ -61,24 +61,6 @@ def __init__(self, *args, **kwargs):
)
del self.metadata.Acquisition_instrument.SEM
- def set_ai(
- self, center=None, energy=None, affine=None, radial_range=None, **kwargs
- ):
- if energy is None and self.beam_energy is not None:
- energy = self.beam_energy
- if energy is not None:
- wavelength = get_electron_wavelength(energy) * 1e-10
- else:
- wavelength = None
- ai = super().set_ai(
- center=center,
- wavelength=wavelength,
- affine=affine,
- radial_range=radial_range,
- **kwargs
- )
- return ai
-
@property
def beam_energy(self):
try:
diff --git a/pyxem/tests/detectors/__init__.py b/pyxem/tests/detectors/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pyxem/tests/detectors/test_generic_flat_detector.py b/pyxem/tests/detectors/test_generic_flat_detector.py
deleted file mode 100644
index 69fa2f46f..000000000
--- a/pyxem/tests/detectors/test_generic_flat_detector.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2016-2024 The pyXem developers
-#
-# This file is part of pyXem.
-#
-# pyXem is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# pyXem is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with pyXem. If not, see .
-
-import pytest
-import numpy as np
-from pyxem.detectors.generic_flat_detector import GenericFlatDetector
-from pyFAI.detectors import Detector
-
-
-def test_generic_flat_detector_init():
- size_x, size_y = 256, 256
- detector = GenericFlatDetector(size_x, size_y)
- assert isinstance(detector, Detector)
- assert np.array_equal(detector.max_shape, (size_x, size_y))
diff --git a/pyxem/tests/detectors/test_medipix_256x256.py b/pyxem/tests/detectors/test_medipix_256x256.py
deleted file mode 100644
index e6646b518..000000000
--- a/pyxem/tests/detectors/test_medipix_256x256.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2016-2024 The pyXem developers
-#
-# This file is part of pyXem.
-#
-# pyXem is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# pyXem is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with pyXem. If not, see .
-
-import pytest
-from pyxem.detectors.medipix_256x256 import Medipix256x256Detector
-from pyFAI.detectors import Detector
-
-
-def test_medipix_256x256_init():
- detector = Medipix256x256Detector()
- assert isinstance(detector, Detector)
diff --git a/pyxem/tests/detectors/test_medipix_515x515.py b/pyxem/tests/detectors/test_medipix_515x515.py
deleted file mode 100644
index 36b19f533..000000000
--- a/pyxem/tests/detectors/test_medipix_515x515.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2016-2024 The pyXem developers
-#
-# This file is part of pyXem.
-#
-# pyXem is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# pyXem is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with pyXem. If not, see .
-
-import pytest
-from pyxem.detectors.medipix_515x515 import Medipix515x515Detector
-from pyFAI.detectors import Detector
-import numpy as np
-
-
-def test_medipix_515x515_init():
- detector = Medipix515x515Detector()
- assert isinstance(detector, Detector)
-
-
-def test_medipix_515x515_mask():
- detector = Medipix515x515Detector()
- mask = detector.calc_mask()
- mask_check = np.zeros((515, 515))
- mask_check[255:260, :] = 1
- mask_check[:, 255:260] = 1
- assert np.array_equal(mask, mask_check)
diff --git a/pyxem/tests/generators/test_calibration_generator.py b/pyxem/tests/generators/test_calibration_generator.py
index b43e20a8d..a73038d85 100644
--- a/pyxem/tests/generators/test_calibration_generator.py
+++ b/pyxem/tests/generators/test_calibration_generator.py
@@ -19,7 +19,6 @@
import pytest
import numpy as np
-from pyFAI.azimuthalIntegrator import AzimuthalIntegrator
from hyperspy.signals import Signal2D
from hyperspy.roi import Line2DROI
from diffsims.utils.ring_pattern_utils import generate_ring_pattern
@@ -277,17 +276,3 @@ def test_get_navigation_calibration_no_data(self, empty_calgen):
empty_calgen.get_navigation_calibration(
line_roi=line, x1=12.0, x2=172.0, n=1, xspace=500.0
)
-
- def test_to_ai(self, calgen):
- calgen.get_elliptical_distortion(
- mask_radius=10,
- direct_beam_amplitude=450,
- scale=95,
- amplitude=1200,
- asymmetry=1.5,
- spread=2.8,
- rotation=10,
- )
- calgen.diffraction_calibration = (1, 1)
- ai = calgen.to_ai(wavelength=(2.53 * 10**-12))
- assert isinstance(ai, AzimuthalIntegrator)
diff --git a/pyxem/tests/signals/test_beam_shift.py b/pyxem/tests/signals/test_beam_shift.py
index 6d0911ce6..5fa5c3b17 100644
--- a/pyxem/tests/signals/test_beam_shift.py
+++ b/pyxem/tests/signals/test_beam_shift.py
@@ -35,7 +35,7 @@ def test_simple(self):
s.change_dtype("float32")
s_orig = s.deepcopy()
s.make_linear_plane()
- assert s.data == approx(s_orig.data, abs=1e-7)
+ assert s.data == approx(s_orig.data, abs=1e-6)
class TestGetLinearPlane:
@@ -47,7 +47,7 @@ def test_simple(self):
s = BeamShift(data)
s.change_dtype("float32")
s_lp = s.get_linear_plane()
- assert s_lp.data == approx(s.data, abs=1e-7)
+ assert s_lp.data == approx(s.data, abs=1e-6)
def test_mask(self):
data_x, data_y = np.meshgrid(
@@ -63,7 +63,7 @@ def test_mask(self):
s.data[45:50, 36:41, 0] = 100000
s.data[45:50, 36:41, 1] = -100000
s_lp = s.get_linear_plane(mask=s_mask)
- assert s_lp.data == approx(s_orig.data, abs=1e-7)
+ assert s_lp.data == approx(s_orig.data, abs=1e-6)
def test_lazy_input_error(self):
s = LazyBeamShift(da.zeros((50, 40, 2)))
diff --git a/pyxem/tests/signals/test_diffraction2d.py b/pyxem/tests/signals/test_diffraction2d.py
index f7ad36280..c28fb6dc3 100644
--- a/pyxem/tests/signals/test_diffraction2d.py
+++ b/pyxem/tests/signals/test_diffraction2d.py
@@ -113,46 +113,20 @@ def test_unit(self, ones):
def test_unit_set(self, ones):
assert ones.unit == "2th_deg"
- @pytest.mark.parametrize(
- "unit", ["q_nm^-1", "q_A^-1", "k_nm^-1", "k_A^-1", "2th_deg", "2th_rad"]
- )
- def test_1d_azimuthal_integral_2th_units(self, ones, unit):
- ones.unit = unit
- ones.set_ai(wavelength=1e-9)
- az = ones.get_azimuthal_integral1d(
- npt=10, correctSolidAngle=False, method="bbox"
- )
- np.testing.assert_array_equal(az.data[0:8], np.ones(8))
-
def test_1d_azimuthal_integral_pyxem(self, ones):
ones.calibration.center = None
ones.calibration.scale = 0.2
az = ones.get_azimuthal_integral1d(
npt=10,
- method="splitpixel_pyxem",
- inplace=True,
- radial_range=[0.0, 0.8],
- )
- assert isinstance(ones, Diffraction1D)
- np.testing.assert_almost_equal(np.sum(ones.data), np.pi * 4**2, decimal=0)
- assert az is None
-
- def test_1d_azimuthal_integral_pyxem(self, ones):
- ones.calibration.center = None
- ones.calibration.scale = 0.2
- az = ones.get_azimuthal_integral1d(
- npt=10,
- method="splitpixel_pyxem",
inplace=True,
radial_range=[0.0, 0.8],
mean=True,
)
assert isinstance(ones, Diffraction1D)
- np.testing.assert_array_equal(ones.data[0:8], np.ones(8))
+ np.testing.assert_array_almost_equal(ones.data[0:8], np.ones(8))
assert az is None
def test_1d_azimuthal_integral_inplace(self, ones):
- ones.set_ai()
az = ones.get_azimuthal_integral1d(
npt=10,
inplace=True,
@@ -162,74 +136,9 @@ def test_1d_azimuthal_integral_inplace(self, ones):
def test_1d_azimuthal_integral_slicing(self, ones):
ones.unit = "2th_rad"
- ones.set_ai(center=(5.5, 5.5))
- az = ones.get_azimuthal_integral1d(
- npt=10,
- method="BBox",
- correctSolidAngle=False,
- radial_range=[0.0, 1.0],
- )
- np.testing.assert_array_equal(az.data[0:7], np.ones(7))
-
- @pytest.mark.parametrize(
- "unit", ["q_nm^-1", "q_A^-1", "k_nm^-1", "k_A^-1", "2th_deg", "2th_rad"]
- )
- def test_1d_axes_continuity(self, ones, unit):
- ones.unit = unit
- ones.set_ai(center=(5.5, 5.5), wavelength=1e-9)
- az1 = ones.get_azimuthal_integral1d(
- npt=10,
- radial_range=[0.0, 1.0],
- method="splitpixel",
- )
- assert np.allclose(az1.axes_manager.signal_axes[0].scale, 0.1)
-
- @pytest.mark.parametrize("radial_range", [None, [0.0, 1.0]])
- @pytest.mark.parametrize("azimuth_range", [None, [-np.pi, np.pi]])
- @pytest.mark.parametrize("center", [None, [9, 9]])
- @pytest.mark.parametrize("affine", [None, [[1, 0, 0], [0, 1, 0], [0, 0, 1]]])
- def test_1d_integration(
- self,
- ones,
- radial_range,
- azimuth_range,
- center,
- affine,
- ):
- ones.set_ai(center=center, affine=affine, radial_range=radial_range)
- az = ones.get_azimuthal_integral1d(
- npt=10,
- method="BBox",
- radial_range=radial_range,
- azimuth_range=azimuth_range,
- correctSolidAngle=False,
- inplace=False,
- )
- assert isinstance(az, Diffraction1D)
-
- def test_1d_azimuthal_integral_mask(self, ones):
- from hyperspy.signals import BaseSignal
-
- aff = [[1, 1, 0], [0, 1, 0], [0, 0, 1]]
- center = [1, 1]
- mask = np.zeros((10, 10))
- mask_bs = BaseSignal(data=mask)
- ones.set_ai(center=center, affine=aff)
- ones.get_azimuthal_integral1d(
- npt=10,
- method="BBox",
- correctSolidAngle=False,
- mask=mask_bs,
- )
-
- @pytest.mark.skip(reason="FAO: M.Nord, skipping to get green for new code")
- def test_1d_azimuthal_integral_sum(self, ones):
- ones.set_ai()
- integration = ones.get_azimuthal_integral1d(
- npt=5, radial_range=[0, 0.5], sum=True
- )
- # 5^2*pi = 78.5
- np.testing.assert_almost_equal(integration.data.sum(), 78.5, decimal=0)
+ ones.calibration.center = None
+ az = ones.get_azimuthal_integral1d(npt=10, radial_range=(0.0, 1.0), mean=True)
+ np.testing.assert_array_almost_equal(az.data[0:7], np.ones(7))
@pytest.mark.parametrize(
"shape", [(20, 16), (3, 20, 16), (4, 3, 20, 16), (6, 4, 3, 20, 16)]
@@ -238,7 +147,6 @@ def test_lazy_input_lazy_output_different_shapes(self, shape):
chunks = [5] * len(shape)
s = LazyDiffraction2D(da.ones(shape, chunks=chunks))
s.unit = "2th_deg"
- s.set_ai()
npt = 10
s_a = s.get_azimuthal_integral1d(npt=npt)
output_signal_shape = s.axes_manager.shape[:-2] + (npt,)
@@ -255,7 +163,6 @@ def test_lazy_input_lazy_output_different_shapes(self, shape):
def test_non_lazy_input_lazy_output(self, shape):
s = Diffraction2D(np.ones(shape))
s.unit = "2th_deg"
- s.set_ai()
npt = 10
s_a = s.get_azimuthal_integral1d(npt=npt, lazy_output=True)
output_signal_shape = s.axes_manager.shape[:-2] + (npt,)
@@ -273,7 +180,6 @@ def test_lazy_input_non_lazy_output(self, shape):
chunks = [5] * len(shape)
s = LazyDiffraction2D(da.ones(shape, chunks=chunks))
s.unit = "2th_deg"
- s.set_ai()
npt = 10
s_a = s.get_azimuthal_integral1d(npt=npt, lazy_output=False)
output_signal_shape = s.axes_manager.shape[:-2] + (npt,)
@@ -288,7 +194,6 @@ def test_lazy_input_lazy_result_inplace(self, shape):
chunks = [5] * len(shape)
s = LazyDiffraction2D(da.ones(shape, chunks=chunks))
s.unit = "2th_deg"
- s.set_ai()
npt = 10
output_signal_shape = s.axes_manager.shape[:-2] + (npt,)
output_data_shape = shape[:-2] + (npt,)
@@ -306,7 +211,6 @@ def test_lazy_input_non_lazy_result_inplace(self, shape):
chunks = [5] * len(shape)
s = LazyDiffraction2D(da.ones(shape, chunks=chunks))
s.unit = "2th_deg"
- s.set_ai()
npt = 10
output_signal_shape = s.axes_manager.shape[:-2] + (npt,)
output_data_shape = shape[:-2] + (npt,)
@@ -320,7 +224,6 @@ def test_lazy_input_non_lazy_result_inplace(self, shape):
def test_non_lazy_input_lazy_result_inplace(self, shape):
s = Diffraction2D(np.ones(shape))
s.unit = "2th_deg"
- s.set_ai()
npt = 10
output_signal_shape = s.axes_manager.shape[:-2] + (npt,)
output_data_shape = shape[:-2] + (npt,)
@@ -474,111 +377,6 @@ def ring(self):
ring_pattern.unit = "k_nm^-1"
return ring_pattern
- def test_2d_azimuthal_integral(self, ones):
- ones.set_ai()
- az = ones.get_azimuthal_integral2d(
- npt=10, npt_azim=10, method="BBox", correctSolidAngle=False
- )
- np.testing.assert_array_equal(az.data[0:8, :], np.ones((8, 10)))
-
- def test_2d_azimuthal_integral_scale(self, ring):
- ring.set_ai(wavelength=2.5e-12)
- az = ring.get_azimuthal_integral2d(npt=500, method="bbox")
- peak = np.argmax(az.sum(axis=0)).data * az.axes_manager[1].scale
- np.testing.assert_almost_equal(peak[0], 3, decimal=1)
- ring.unit = "k_A^-1"
- ring.set_ai(wavelength=2.5e-12)
- az = ring.get_azimuthal_integral2d(npt=500, method="bbox")
- peak = np.argmax(az.sum(axis=0)).data * az.axes_manager[1].scale
- np.testing.assert_almost_equal(peak[0], 3, decimal=1)
-
- def test_2d_azimuthal_integral_fast_slicing(self, ones):
- ones.set_ai(center=(5.5, 5.5))
- az1 = ones.get_azimuthal_integral2d(
- npt=10,
- npt_azim=10,
- radial_range=[0.0, 1.0],
- method="splitpixel",
- correctSolidAngle=False,
- )
- np.testing.assert_array_equal(az1.data[8:, :], np.zeros(shape=(2, 10)))
-
- @pytest.mark.parametrize(
- "unit", ["q_nm^-1", "q_A^-1", "k_nm^-1", "k_A^-1", "2th_deg", "2th_rad"]
- )
- def test_2d_axes_continuity(self, ones, unit):
- ones.unit = unit
- ones.set_ai(wavelength=1e-9, center=(5.5, 5.5))
- az1 = ones.get_azimuthal_integral2d(
- npt=10,
- npt_azim=20,
- radial_range=[0.0, 1.0],
- method="splitpixel",
- )
- assert np.allclose(az1.axes_manager.signal_axes[1].scale, 0.1)
-
- def test_2d_azimuthal_integral_inplace(self, ones):
- ones.set_ai()
- az = ones.get_azimuthal_integral2d(
- npt=10,
- npt_azim=10,
- correctSolidAngle=False,
- inplace=True,
- method="BBox",
- )
- assert isinstance(ones, PolarDiffraction2D)
- np.testing.assert_array_equal(ones.data[0:8, :], np.ones((8, 10)))
- assert az is None
-
- @pytest.mark.parametrize("radial_range", [None, [0, 1.0]])
- @pytest.mark.parametrize("azimuth_range", [None, [-np.pi, 0]])
- @pytest.mark.parametrize("correctSolidAngle", [True, False])
- @pytest.mark.parametrize("center", [None, [7, 7]])
- @pytest.mark.parametrize("affine", [None, [[1, 0, 0], [0, 1, 0], [0, 0, 1]]])
- def test_2d_azimuthal_integral_params(
- self, ones, radial_range, azimuth_range, correctSolidAngle, center, affine
- ):
- ones.set_ai(
- center=center, affine=affine, radial_range=radial_range, wavelength=1e-9
- )
- az = ones.get_azimuthal_integral2d(
- npt=10,
- npt_azim=10,
- radial_range=radial_range,
- azimuth_range=azimuth_range,
- correctSolidAngle=correctSolidAngle,
- method="BBox",
- )
- assert isinstance(az, PolarDiffraction2D)
-
- def test_2d_azimuthal_integral_mask_iterate(self, ones):
- from hyperspy.signals import BaseSignal
-
- aff = [[1, 1, 0], [0, 1, 0], [0, 0, 1]]
- center = [1, 1]
- ones.set_ai(center=center, affine=aff, wavelength=1e-9)
- mask = np.zeros((10, 10))
- mask_bs = BaseSignal(data=mask)
- ones.get_azimuthal_integral2d(
- npt=10,
- npt_azim=10,
- method="BBox",
- correctSolidAngle=False,
- mask=mask_bs,
- )
-
- def test_2d_azimuthal_integral_sum(self, ones):
- ones.set_ai()
- integration = ones.get_azimuthal_integral2d(
- npt=10, npt_azim=15, radial_range=[0, 0.5], sum=True
- )
- # mostly correct except for at the very center where things get weird...
- mask = np.ones((10, 10), dtype=bool)
- mask[0:5] = False
- integration2 = ones.get_azimuthal_integral2d(
- npt=10, npt_azim=15, radial_range=[0, 0.5], sum=True, mask=mask
- )
-
def test_internal_azimuthal_integration(self, ring):
ring.calibration(scale=1)
az = ring.get_azimuthal_integral2d(npt=40, npt_azim=100, radial_range=(0, 40))
@@ -661,74 +459,6 @@ def test_azimuthal_integration_range(
assert np.allclose(quadrant.data[~np.isnan(quadrant.data)], expected_output)
-class TestPyFAIIntegration:
- @pytest.fixture
- def ones(self):
- ones_diff = Diffraction2D(data=np.ones(shape=(10, 10)))
- ones_diff.axes_manager.signal_axes[0].scale = 0.1
- ones_diff.axes_manager.signal_axes[1].scale = 0.1
- ones_diff.axes_manager.signal_axes[0].name = "kx"
- ones_diff.axes_manager.signal_axes[1].name = "ky"
- ones_diff.unit = "q_nm^-1"
- return ones_diff
-
- def test_integrate_radial(self, ones):
- ones.set_ai(center=(5.5, 5.5), wavelength=1e-9)
- assert isinstance(ones, Diffraction2D)
- integration = ones.get_radial_integral(
- npt=10,
- npt_rad=100,
- method="BBox",
- correctSolidAngle=False,
- )
- assert isinstance(integration, Diffraction1D)
- np.testing.assert_array_equal(integration, np.ones(10))
- integration = ones.get_radial_integral(
- npt=10,
- npt_rad=100,
- method="BBox",
- correctSolidAngle=False,
- sum=True,
- )
- integration = ones.get_radial_integral(
- npt=10, npt_rad=100, method="BBox", correctSolidAngle=False, inplace=True
- )
- np.testing.assert_array_equal(ones, np.ones(10))
- assert integration is None
-
- def test_integrate_med_filter(self, ones):
- ones.set_ai(center=(5.5, 5.5), wavelength=1e-9)
- integration = ones.get_medfilt1d(
- npt_rad=10, npt_azim=100, method="BBox", correctSolidAngle=False
- )
- np.testing.assert_array_equal(integration, np.ones(10))
- integration = ones.get_medfilt1d(
- npt_rad=10,
- npt_azim=100,
- method="BBox",
- correctSolidAngle=False,
- inplace=True,
- )
- np.testing.assert_array_equal(ones, np.ones(10))
- assert integration is None
-
- def test_integrate_sigma_clip(self, ones):
- ones.set_ai(center=(5.5, 5.5), wavelength=1e-9)
- integration = ones.sigma_clip(
- npt_rad=10, npt_azim=100, method="CSR", correctSolidAngle=False
- ) # only CSR works
- np.testing.assert_array_equal(integration, np.ones(10))
- integration = ones.sigma_clip(
- npt_rad=10,
- npt_azim=100,
- method="CSR",
- correctSolidAngle=False,
- inplace=True,
- )
- np.testing.assert_array_equal(ones, np.ones(10))
- assert integration is None
-
-
class TestVirtualImaging:
# Tests that virtual imaging runs without failure
diff --git a/pyxem/tests/signals/test_diffraction_variance2d.py b/pyxem/tests/signals/test_diffraction_variance2d.py
index ca6911ec2..299daf1b7 100644
--- a/pyxem/tests/signals/test_diffraction_variance2d.py
+++ b/pyxem/tests/signals/test_diffraction_variance2d.py
@@ -42,7 +42,6 @@ def test_1d_azimuthal_integration(self):
)
)
var.unit = "2th_rad"
- var.set_ai()
integration = var.get_azimuthal_integral1d(npt=10)
assert isinstance(integration, DiffractionVariance1D)
diff --git a/pyxem/tests/signals/test_electron_diffraction2d.py b/pyxem/tests/signals/test_electron_diffraction2d.py
index 4407ca174..6e7bf1c81 100644
--- a/pyxem/tests/signals/test_electron_diffraction2d.py
+++ b/pyxem/tests/signals/test_electron_diffraction2d.py
@@ -276,14 +276,12 @@ def ones(self):
@pytest.mark.parametrize("energy", [None, 200])
def test_1d_azimuthal_integration(self, ones, energy):
ones.beam_energy = energy
- ones.set_ai()
integration = ones.get_azimuthal_integral1d(npt=10)
assert isinstance(integration, ElectronDiffraction1D)
@pytest.mark.parametrize("energy", [None, 200])
def test_2d_azimuthal_integration(self, ones, energy):
ones.beam_energy = energy
- ones.set_ai()
integration = ones.get_azimuthal_integral2d(npt=10)
assert isinstance(integration, PolarDiffraction2D)
diff --git a/pyxem/tests/signals/test_indexation_results.py b/pyxem/tests/signals/test_indexation_results.py
index 3aae69683..1993b990b 100644
--- a/pyxem/tests/signals/test_indexation_results.py
+++ b/pyxem/tests/signals/test_indexation_results.py
@@ -30,6 +30,7 @@
from orix.sampling import get_sample_reduced_fundamental
from orix.quaternion import Rotation, Orientation
from orix.crystal_map import CrystalMap
+from orix.vector import Vector3d
from pyxem.generators import TemplateIndexationGenerator
from pyxem.signals import VectorMatchingResults, DiffractionVectors, OrientationMap
@@ -227,7 +228,7 @@ def simple_multi_rot_orientation_result(self):
reciprocal_radius=2,
with_direct_beam=True,
)
- polar = polar**0.5
+ polar = polar**1
orientations = polar.get_orientation(sims)
return orientations, r, s
@@ -278,10 +279,14 @@ def test_grain_orientation_result(self, simple_multi_rot_orientation_result):
assert isinstance(orientations, OrientationMap)
orients = orientations.to_single_phase_orientations()
+ v1 = (orients * Vector3d.zvector()).in_fundamental_sector(orients.symmetry)
+ v2 = (rotations * Vector3d.zvector()).in_fundamental_sector(rotations.symmetry)
+
# Check that the orientations are within 2 degrees of the expected value.
# Use 2 degrees since that is the angular resolution of the polar dataset
- degrees_between = orients.angle_with(rotations, degrees=True)
- assert np.all(np.min(degrees_between, axis=2) <= 2)
+ degrees_between = v1.angle_with(v2, degrees=True)
+ min_deg = np.min(degrees_between, axis=2)
+ np.testing.assert_allclose(min_deg, 0, atol=3)
def test_to_crystal_map(self, simple_multi_rot_orientation_result):
orientations, rotations, s = simple_multi_rot_orientation_result
diff --git a/pyxem/tests/utils/test_calibration_utils.py b/pyxem/tests/utils/test_calibration_utils.py
index ad9f84a03..1989333b1 100644
--- a/pyxem/tests/utils/test_calibration_utils.py
+++ b/pyxem/tests/utils/test_calibration_utils.py
@@ -185,29 +185,3 @@ def test_to_string(self, calibration):
== "Calibration for , "
"Ewald sphere: curved, shape: (10, 10), affine: False, mask: False"
)
-
- def test_to_pyfai_no_unit(self, calibration):
- with pytest.raises(ValueError):
- calibration.to_pyfai()
-
- def test_to_pyfai_flat(self, calibration):
- from pyFAI.azimuthalIntegrator import AzimuthalIntegrator
-
- calibration.units = "k_nm^-1"
- calibration.beam_energy = 200
- ai = calibration.to_pyfai()
- assert isinstance(ai, AzimuthalIntegrator)
-
- def test_to_pyfai_curved(self, calibration):
- from pyFAI.azimuthalIntegrator import AzimuthalIntegrator
-
- calibration.beam_energy = 200
- calibration.detector(pixel_size=1, detector_distance=1, units="k_nm^-1")
- ai = calibration.to_pyfai()
- assert isinstance(ai, AzimuthalIntegrator)
-
- def test_to_pyfai_failure(self, calibration):
- calibration.signal.axes_manager.signal_axes[0].convert_to_non_uniform_axis()
- calibration.signal.axes_manager.signal_axes[1].convert_to_non_uniform_axis()
- with pytest.raises(ValueError):
- ai = calibration.to_pyfai()
diff --git a/pyxem/tests/utils/test_deprecation.py b/pyxem/tests/utils/test_deprecation.py
index af4d21f74..05f2a3ebb 100644
--- a/pyxem/tests/utils/test_deprecation.py
+++ b/pyxem/tests/utils/test_deprecation.py
@@ -22,6 +22,7 @@
import pytest
from pyxem.utils._deprecated import deprecated, deprecated_argument
+from pyxem.common import VisibleDeprecationWarning
class TestDeprecationWarning:
@@ -36,7 +37,7 @@ def foo(n):
"""Some docstring."""
return n + 1
- with pytest.warns(np.VisibleDeprecationWarning) as record:
+ with pytest.warns(VisibleDeprecationWarning) as record:
assert foo(4) == 5
desired_msg = (
"Function `foo()` is deprecated and will be removed in version 0.8. Use "
@@ -59,7 +60,7 @@ def foo2(n):
"""
return n + 2
- with pytest.warns(np.VisibleDeprecationWarning) as record2:
+ with pytest.warns(VisibleDeprecationWarning) as record2:
assert foo2(4) == 6
desired_msg2 = "Function `foo2()` is deprecated."
assert str(record2[0].message) == desired_msg2
@@ -76,7 +77,7 @@ def test_deprecation_no_old_doc(self):
def foo(n):
return n + 1
- with pytest.warns(np.VisibleDeprecationWarning) as record:
+ with pytest.warns(VisibleDeprecationWarning) as record:
assert foo(4) == 5
desired_msg = (
"Function `foo()` is deprecated and will be removed in version 0.8. Use "
@@ -115,7 +116,7 @@ def bar_arg_alt(self, **kwargs):
assert my_foo.bar_arg(b=1) == {"b": 1}
# Warns
- with pytest.warns(np.VisibleDeprecationWarning) as record2:
+ with pytest.warns(VisibleDeprecationWarning) as record2:
assert my_foo.bar_arg(a=2) == {"a": 2}
assert str(record2[0].message) == (
r"Argument `a` is deprecated and will be removed in version 1.4. "
@@ -124,7 +125,7 @@ def bar_arg_alt(self, **kwargs):
)
# Warns with alternative
- with pytest.warns(np.VisibleDeprecationWarning) as record3:
+ with pytest.warns(VisibleDeprecationWarning) as record3:
assert my_foo.bar_arg_alt(a=3) == {"b": 3}
assert str(record3[0].message) == (
r"Argument `a` is deprecated and will be removed in version 1.4. "
diff --git a/pyxem/tests/utils/test_expt_utils.py b/pyxem/tests/utils/test_expt_utils.py
index cc13e4aa9..a4d4aaa20 100644
--- a/pyxem/tests/utils/test_expt_utils.py
+++ b/pyxem/tests/utils/test_expt_utils.py
@@ -21,7 +21,6 @@
from scipy.ndimage import gaussian_filter
from matplotlib import pyplot as plt
-from pyFAI.detectors import Detector
from pyxem.utils.diffraction import (
_index_coords,
@@ -33,9 +32,6 @@
investigate_dog_background_removal_interactive,
find_beam_center_blur,
find_beam_center_interpolate,
- azimuthal_integrate1d,
- azimuthal_integrate2d,
- find_hot_pixels,
)
@@ -194,45 +190,3 @@ def test_find_beam_center_interpolate_2(center_expected, sigma):
z = gaussian_filter(z, sigma=sigma)
centers = find_beam_center_interpolate(z, sigma=5, upsample_factor=100, kind=3)
assert np.allclose(centers, center_expected, atol=0.2)
-
-
-class TestAzimuthalIntegration:
- @pytest.fixture
- def radial_pattern(self):
- x, y = np.ogrid[-5:5, -5:5]
- radial = (x**2 + y**2) * np.pi
- radial[radial == 0] = 1
- return 100 / radial
-
- def test_1d_integrate(self, radial_pattern):
- from pyxem.utils.pyfai_utils import get_azimuthal_integrator
-
- dect = Detector(pixel1=1e-4, pixel2=1e-4)
- ai = get_azimuthal_integrator(
- detector=dect, detector_distance=1, shape=np.shape(radial_pattern)
- )
- integration = azimuthal_integrate1d(
- radial_pattern,
- ai,
- npt_rad=100,
- method="numpy",
- unit="2th_rad",
- correctSolidAngle=True,
- )
-
- def test_2d_integrate(self, radial_pattern):
- from pyxem.utils.pyfai_utils import get_azimuthal_integrator
-
- dect = Detector(pixel1=1e-4, pixel2=1e-4)
- ai = get_azimuthal_integrator(
- detector=dect, detector_distance=1, shape=np.shape(radial_pattern)
- )
- integration = azimuthal_integrate2d(
- radial_pattern,
- ai,
- npt_rad=100,
- npt_azim=100,
- method="numpy",
- unit="2th_rad",
- correctSolidAngle=True,
- )
diff --git a/pyxem/tests/utils/test_pyfai_utils.py b/pyxem/tests/utils/test_pyfai_utils.py
deleted file mode 100644
index 757ce3dfc..000000000
--- a/pyxem/tests/utils/test_pyfai_utils.py
+++ /dev/null
@@ -1,86 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2016-2024 The pyXem developers
-#
-# This file is part of pyXem.
-#
-# pyXem is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# pyXem is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with pyXem. If not, see .
-
-import pytest
-
-from pyxem.utils.pyfai_utils import (
- _get_radial_extent,
- get_azimuthal_integrator,
- _get_displacements,
- _get_setup,
-)
-from pyFAI.detectors import Detector
-from pyFAI.azimuthalIntegrator import AzimuthalIntegrator
-import numpy as np
-
-
-class Test_PyFai_utils:
- def test_get_azimuthal_integrator(self):
- dect = Detector(pixel1=1e-4, pixel2=1e-4, max_shape=(20, 20))
- ai = get_azimuthal_integrator(
- detector=dect, detector_distance=0.001, shape=(20, 20), center=(10.5, 10.5)
- )
- assert isinstance(ai, AzimuthalIntegrator)
- ai_mask = get_azimuthal_integrator(
- detector=dect,
- detector_distance=1,
- shape=(20, 20),
- center=(10.5, 10.5),
- mask=np.zeros((20, 20)),
- )
-
- assert isinstance(ai_mask, AzimuthalIntegrator)
- aff = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
- ai_affine = get_azimuthal_integrator(
- detector=dect,
- detector_distance=1,
- shape=(20, 20),
- center=(10.5, 10.5),
- mask=np.zeros((20, 20)),
- affine=aff,
- )
- assert isinstance(ai_affine, AzimuthalIntegrator)
-
- def test_get_displacements(self):
- aff = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
- dis = _get_displacements((10.5, 10.5), shape=(20, 20), affine=aff)
- np.testing.assert_array_equal(dis, np.zeros(shape=(2, 21, 21)))
-
- def test_get_extent(self):
- dect = Detector(pixel1=1e-4, pixel2=1e-4)
- ai = AzimuthalIntegrator(detector=dect, dist=0.1)
- ai.setFit2D(directDist=1000, centerX=50.5, centerY=50.5)
- extent = _get_radial_extent(ai=ai, shape=(100, 100), unit="2th_rad")
- max_rad = 50 * np.sqrt(2)
- calc_extent = np.arctan(max_rad * 1e-4 / 1)
- np.testing.assert_almost_equal(
- extent[1],
- calc_extent,
- )
-
- @pytest.mark.parametrize("wavelength", [0, 1e-9])
- @pytest.mark.parametrize(
- "unit", ["2th_deg", "2th_rad", "q_nm^-1", "q_A^-1", "k_nm^-1", "k_A^-1"]
- )
- def test_get_setup(self, wavelength, unit):
- curve = _get_setup(
- wavelength=wavelength,
- pyxem_unit=unit,
- pixel_scale=[1, 1],
- radial_range=[0, 1],
- )
diff --git a/pyxem/utils/_azimuthal_integrations.py b/pyxem/utils/_azimuthal_integrations.py
index 108a55f97..f6455acfe 100644
--- a/pyxem/utils/_azimuthal_integrations.py
+++ b/pyxem/utils/_azimuthal_integrations.py
@@ -179,7 +179,7 @@ def _slice_radial_integrate1d(
for index, fa in zip(ind, f):
total = total + img[index[0], index[1]] * fa
if mean:
- total_f = 0.0
+ total_f = np.finfo(np.float32).eps
if mask is not None:
total_f = total_f + mask[ind[0], ind[1]] * fa
else:
diff --git a/pyxem/utils/_deprecated.py b/pyxem/utils/_deprecated.py
index e089c234e..9bf2ecd20 100644
--- a/pyxem/utils/_deprecated.py
+++ b/pyxem/utils/_deprecated.py
@@ -30,7 +30,7 @@
from typing import Callable, Optional, Union
import warnings
-import numpy as np
+from pyxem.common import VisibleDeprecationWarning
class deprecated:
@@ -87,12 +87,12 @@ def __call__(self, func: Callable):
@functools.wraps(func)
def wrapped(*args, **kwargs):
warnings.simplefilter(
- action="always", category=np.VisibleDeprecationWarning, append=True
+ action="always", category=VisibleDeprecationWarning, append=True
)
func_code = func.__code__
warnings.warn_explicit(
message=msg,
- category=np.VisibleDeprecationWarning,
+ category=VisibleDeprecationWarning,
filename=func_code.co_filename,
lineno=func_code.co_firstlineno + 1,
)
@@ -139,12 +139,12 @@ def wrapped(*args, **kwargs):
kwargs[self.alternative] = kwargs.pop(self.name)
msg += f"See the documentation of `{func.__name__}()` for more details."
warnings.simplefilter(
- action="always", category=np.VisibleDeprecationWarning
+ action="always", category=VisibleDeprecationWarning
)
func_code = func.__code__
warnings.warn_explicit(
message=msg,
- category=np.VisibleDeprecationWarning,
+ category=VisibleDeprecationWarning,
filename=func_code.co_filename,
lineno=func_code.co_firstlineno + 1,
)
diff --git a/pyxem/utils/calibration.py b/pyxem/utils/calibration.py
index 3ad99315d..0218fdfbd 100644
--- a/pyxem/utils/calibration.py
+++ b/pyxem/utils/calibration.py
@@ -502,54 +502,6 @@ def center(self, center=None):
for ax, off in zip(self.signal.axes_manager.signal_axes, center):
ax.offset = -off * ax.scale
- def to_pyfai(self):
- """
- Convert the calibration to a pyfai AzimuthalIntegrator.
-
- Returns
- -------
- """
- from pyFAI.detectors import Detector
- from pyxem.utils.pyfai_utils import _get_setup, get_azimuthal_integrator
-
- if self.flat_ewald:
- pixel_scale = self.scale
- if self.wavelength is None:
- raise ValueError(
- "The wavelength must be set before converting to a pyfai AzimuthalIntegrator"
- )
- setup = _get_setup(
- wavelength=self.wavelength,
- pyxem_unit=self.units[0],
- pixel_scale=pixel_scale,
- )
- detector, dist, radial_range = setup
- else:
- pixel_size = self.signal.metadata.get_item(
- "Acquisition_instrument.TEM.pixel_size"
- )
- dist = self.signal.metadata.get_item(
- "Acquisition_instrument.TEM.detector_distance"
- )
- if pixel_size is None or dist is None:
- raise ValueError(
- "The dector must be first initialized with the s.calibration.detector method"
- )
- detector = Detector(
- pixel1=pixel_size,
- pixel2=pixel_size,
- )
-
- ai = get_azimuthal_integrator(
- detector=detector,
- detector_distance=dist,
- shape=self.shape,
- center=self.center,
- affine=self.affine,
- wavelength=self.wavelength,
- )
- return ai
-
@deprecated(
since="0.18.0",
diff --git a/pyxem/utils/diffraction.py b/pyxem/utils/diffraction.py
index f917c19f3..47bd8c2e5 100644
--- a/pyxem/utils/diffraction.py
+++ b/pyxem/utils/diffraction.py
@@ -35,7 +35,6 @@
from tqdm import tqdm
from packaging.version import Version
-from pyxem.utils.pyfai_utils import get_azimuthal_integrator
from pyxem.utils.cuda_utils import is_cupy_array
from pyxem.utils._deprecated import deprecated
import pyxem.utils._pixelated_stem_tools as pst
@@ -130,200 +129,6 @@ def _polar2cart(r, theta):
return x, y
-def azimuthal_integrate1d(
- z, azimuthal_integrator, npt_rad, mask=None, sum=False, **kwargs
-):
- """Calculate the azimuthal integral of z around a determined origin.
-
- This method is used for signals where the origin is constant, compared to
- azimuthal_integrate which is used when the origin in the data changes and
- is iterated over.
-
- Parameters
- ----------
- z : numpy.ndarray
- Two-dimensional data array containing the signal.
- azimuthal_integrator : pyFAI.azimuthal_integrator.AzimuthalIntegrator object
- An AzimuthalIntegrator that is already initialised and used to calculate
- the integral.
- npt_rad:
- The number of radial points to integrate
- mask: Boolean Array
- A boolean array with pixels to ignore
- sum: bool
- Returns the integrated intensity rather than the mean.
- **kwargs :
- Keyword arguments to be passed to ai.integrate2d
-
- Returns
- -------
- tth : numpy.ndarray
- One-dimensional scattering vector axis of z.
- I : numpy.ndarray
- One-dimensional azimuthal integral of z.
- """
- output = azimuthal_integrator.integrate1d(z, npt=npt_rad, mask=mask, **kwargs)
- if sum:
- return np.transpose(output._sum_signal)
- else:
- return output[1]
-
-
-def azimuthal_integrate2d(
- z, azimuthal_integrator, npt_rad, npt_azim=None, mask=None, sum=False, **kwargs
-):
- """Calculate the azimuthal integral of z around a determined origin.
-
- This method is used for signals where the origin is constant, compared to
- azimuthal_integrate which is used when the origin in the data changes and
- is iterated over.
-
- Parameters
- ----------
- z : numpy.ndarray
- Two-dimensional data array containing the signal.
- azimuthal_integrator : pyFAI.azimuthal_integrator.AzimuthalIntegrator object
- An AzimuthalIntegrator that is already initialised and used to calculate
- the integral.
- npt_rad: int
- The number of radial points to integrate
- npt_azim: int
- The number of azimuthal points to integrate
- mask: Boolean Array
- The mask used to ignore points.
- sum: bool
- If True the sum is returned, otherwise the average is returned.
- **kwargs :
- Keyword arguments to be passed to ai.integrate2d
-
- Returns
- -------
- I : numpy.ndarray
- Two-dimensional azimuthal integral of z.
- """
- output = azimuthal_integrator.integrate2d(
- z, npt_rad=npt_rad, npt_azim=npt_azim, mask=mask, **kwargs
- )
- if sum:
- return np.transpose(output._sum_signal)
- else:
- return np.transpose(output[0])
-
-
-def integrate_radially(
- z, azimuthal_integrator, npt, npt_rad, mask=None, sum=False, **kwargs
-):
- """Calculate the radial integrated profile curve as I = f(chi)
-
- Parameters
- ----------
- z : numpy.ndarray
- Two-dimensional data array containing the signal.
- azimuthal_integrator : pyFAI.azimuthal_integrator.AzimuthalIntegrator object
- An AzimuthalIntegrator that is already initialised and used to calculate
- the integral.
- npt: int
- The number of points in the output pattern
- npt_rad: int
- The number of points in the radial space. Too few points may lead to huge rounding errors.
- mask: Boolean Array
- A boolean array with pixels to ignore
- sum: bool
- Returns the integrated intensity rather than the mean.
- **kwargs :
- Keyword arguments to be passed to ai.integrate2d
-
- Returns
- -------
- tth : numpy.ndarray
- One-dimensional scattering vector axis of z.
- I : numpy.ndarray
- One-dimensional azimuthal integral of z.
- """
- output = azimuthal_integrator.integrate_radial(
- z, npt=npt, npt_rad=npt_rad, mask=mask, **kwargs
- )
- if sum:
- return np.transpose(output._sum_signal)
- else:
- return output[1]
-
-
-def medfilt_1d(z, azimuthal_integrator, npt_rad, npt_azim, mask=None, **kwargs):
- """Perform the 2D integration and filter along each row using a median filter
-
- Parameters
- ----------
- z : numpy.ndarray
- Two-dimensional data array containing the signal.
- azimuthal_integrator : pyFAI.azimuthal_integrator.AzimuthalIntegrator object
- An AzimuthalIntegrator that is already initialised and used to calculate
- the integral.
- npt: int
- The number of points in the output pattern
- npt_rad: int
- The number of points in the radial space. Too few points may lead to huge rounding errors.
- mask: Boolean Array
- A boolean array with pixels to ignore
- sum: bool
- Returns the integrated intensity rather than the mean.
- **kwargs :
- Keyword arguments to be passed to ai.integrate2d
-
- Returns
- -------
- tth : numpy.ndarray
- One-dimensional scattering vector axis of z.
- I : numpy.ndarray
- One-dimensional azimuthal integral of z.
- """
- output = azimuthal_integrator.medfilt1d(
- z, npt_rad=npt_rad, npt_azim=npt_azim, mask=mask, **kwargs
- )
- return output[1]
-
-
-def sigma_clip(z, azimuthal_integrator, npt_rad, npt_azim, mask=None, **kwargs):
- """Perform the 2D integration and perform a sigm-clipping iterative
- filter along each row. see the doc of scipy.stats.sigmaclip for the options.
-
-
- Parameters
- ----------
- z : numpy.ndarray
- Two-dimensional data array containing the signal.
- azimuthal_integrator : pyFAI.azimuthal_integrator.AzimuthalIntegrator object
- An AzimuthalIntegrator that is already initialised and used to calculate
- the integral.
- npt_rad: int
- The number of points in the output pattern
- npt_azim: int
- The number of points in the radial space. Too few points may lead to huge rounding errors.
- mask: Boolean Array
- A boolean array with pixels to ignore
- sum: bool
- Returns the integrated intensity rather than the mean.
- **kwargs :
- Keyword arguments to be passed to ai.integrate2d
-
- Returns
- -------
- tth : numpy.ndarray
- One-dimensional scattering vector axis of z.
- I : numpy.ndarray
- One-dimensional azimuthal integral of z.
- """
- from pyFAI._version import version
-
- if Version(version) >= Version("2023.2.0"):
- output = azimuthal_integrator.sigma_clip_ng(z, npt=npt_rad, mask=mask, **kwargs)
- else:
- output = azimuthal_integrator.sigma_clip(
- z, npt_rad=npt_rad, npt_azim=npt_azim, mask=mask, **kwargs
- )
- return output[1]
-
-
def gain_normalise(z, dref, bref):
"""Apply gain normalization to experimentally acquired electron
diffraction pattern.
diff --git a/pyxem/utils/pyfai_utils.py b/pyxem/utils/pyfai_utils.py
deleted file mode 100644
index 2de0d4705..000000000
--- a/pyxem/utils/pyfai_utils.py
+++ /dev/null
@@ -1,199 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2016-2024 The pyXem developers
-#
-# This file is part of pyXem.
-#
-# pyXem is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# pyXem is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with pyXem. If not, see .
-
-# TODO: Delete this entire module
-
-"""Utils for pyfai transformations."""
-
-import numpy as np
-from pyFAI.azimuthalIntegrator import AzimuthalIntegrator
-from pyFAI.detectors import Detector
-from pyFAI.units import register_radial_unit, eq_q
-
-from pyxem.utils._deprecated import deprecated
-
-
-@deprecated(since="0.18.0", removal="1.0.0")
-def get_azimuthal_integrator(
- detector,
- detector_distance,
- shape,
- center=None,
- affine=None,
- mask=None,
- wavelength=None,
- **kwargs
-):
- """Basic method for creating a azimuthal integrator.
-
- This helps to deal with taking some of the pyXEM standards and apply them to pyFAI
-
- Parameters
- ----------
- detector: pyFAI.detectors.Detector
- The detector to be integrated
- detector_distance:
- distance sample - detector plan (orthogonal distance, not along the beam), in meter.
- shape: (int, int)
- The shape of the signal we are operating on. For the
- center: (float, float)
- The center of the diffraction pattern
- affine: (3x3)
- The affine transformation to apply to the data
- mask: np.array
- A boolean array to be added to the integrator.
- wavelength: float
- The wavelength of the beam in meter. Needed to accounting for the
- Ewald sphere.
- kwargs: dict
- Any additional arguments to the Azimuthal Integrator class
- """
- if center is None:
- center = np.divide(shape, 2) # Center is middle of the image
- if affine is not None:
- # create spline representation with (dx,dy) displacements
- dx, dy = _get_displacements(center=center, shape=shape, affine=affine)
- detector.max_shape = shape
- detector.shape = shape
- detector.set_dx(dx)
- detector.set_dy(dy)
- ai = AzimuthalIntegrator(
- detector=detector, dist=detector_distance, wavelength=wavelength, **kwargs
- )
- if mask is not None:
- ai.set_mask(mask)
- if wavelength is not None:
- ai.wavelength = wavelength
- ai.setFit2D(
- directDist=detector_distance * 1000, centerX=center[1], centerY=center[0]
- )
- return ai
-
-
-@deprecated(since="0.18.0", removal="1.0.0")
-def _get_radial_extent(ai, shape=None, unit=None):
- """Takes an Azimuthal Integrator and calculates the domain of the output.
-
- Note: this method isn't perfect.
-
- Parameters
- ----------
- ai: AzimuthalIntegrator
- The integrator to operate on
- shape: (int, int)
- The shape of the detector.
- unit:
- The unit to calculate the radial extent with.
- """
- postions = ai.array_from_unit(shape=shape, unit=unit, typ="center")
- return [np.min(postions), np.max(postions)]
-
-
-@deprecated(since="0.18.0", removal="1.0.0")
-def _get_displacements(center, shape, affine):
- """Gets the displacements for a set of points based on some affine transformation
- about some center point.
-
- Parameters
- ----------
- center: (tuple)
- The center to preform the affine transformation around
- shape: (tuple)
- The shape of the array
- affine: 3x3 array
- The affine transformation to apply to the image
-
- Returns
- -------
- dx: np.array
- The displacement in the x direction of shape = shape
- dy: np.array
- The displacement in the y direction of shape = shape
- """
- # all x and y coordinates on the grid
- shape_plus = np.add(shape, 1)
- x = range(shape_plus[0], 0, -1)
- y = range(shape_plus[1], 0, -1)
- xx, yy = np.meshgrid(x, y)
- xx = np.subtract(xx, center[1])
- yy = np.subtract(yy, center[0])
- coord = np.array(
- [xx.flatten(), yy.flatten(), np.ones((shape_plus[0]) * (shape_plus[1]))]
- )
- corrected = np.reshape(np.matmul(coord.T, affine), newshape=(*shape_plus, -1))
- dx = xx - corrected[:, :, 0]
- dy = yy - corrected[:, :, 1]
- return dx, dy
-
-
-@deprecated(since="0.18.0", removal="1.0.0")
-def _get_setup(wavelength, pyxem_unit, pixel_scale, radial_range=None):
- """Returns a generic set up for a flat detector with accounting for Ewald sphere effects."""
- units_table = {
- "2th_deg": None,
- "2th_rad": None,
- "q_nm^-1": 1e-9,
- "q_A^-1": 1e-10,
- "k_nm^-1": 1e-9,
- "k_A^-1": 1e-10,
- }
- wavelength_scale = units_table[pyxem_unit]
- detector_distance = 1
- if wavelength_scale is None:
- if pyxem_unit == "2th_deg":
- pixel_1_size = np.tan((pixel_scale[0] / 180) * np.pi)
- pixel_2_size = np.tan((pixel_scale[1] / 180) * np.pi)
- if pyxem_unit == "2th_rad":
- pixel_1_size = np.tan(pixel_scale[0])
- pixel_2_size = np.tan(pixel_scale[1])
- else:
- theta0 = pixel_scale[0] * (wavelength / wavelength_scale)
- theta1 = pixel_scale[1] * (wavelength / wavelength_scale)
- pixel_1_size = np.tan(theta0) * detector_distance
- pixel_2_size = np.tan(theta1) * detector_distance
- detector = Detector(pixel1=pixel_1_size, pixel2=pixel_2_size)
- if radial_range is not None:
- radial_range = [radial_range[0], radial_range[1]]
- return (
- detector,
- detector_distance,
- radial_range,
- )
-
-
-register_radial_unit(
- "k_A^-1",
- center="qArray",
- delta="deltaQ",
- scale=0.1 / (2 * np.pi),
- label=r"Scattering vector $k$ ($\AA^{-1}$)",
- equation=eq_q,
- short_name="k",
- unit_symbol=r"\AA^{-1}",
-)
-
-register_radial_unit(
- "k_nm^-1",
- center="qArray",
- delta="deltaQ",
- scale=1 / (2 * np.pi),
- label=r"Scattering vector $k$ ($\nm^{-1}$)",
- equation=eq_q,
- short_name="k",
- unit_symbol=r"\nm^{-1}",
-)
diff --git a/setup.py b/setup.py
index 4ac7943db..726219cbe 100644
--- a/setup.py
+++ b/setup.py
@@ -94,11 +94,9 @@
"matplotlib >= 3.7.5",
"numba",
"numpy",
- "numexpr != 2.8.6", # bug in 2.8.6 for greek letters need for pyfai
"orix >= 0.12.1",
"pooch",
"psutil",
- "pyfai <= 2023.9.0", # breaking changes
"scikit-image >= 0.19.0, !=0.21.0", # regression in ellipse fitting"
"scikit-learn >= 1.0",
"shapely > 2.0.0", # major changes