`.
+
Learning resources
==================
@@ -81,6 +85,7 @@ Learning resources
kikuchipy is a community project maintained for and by its users. There are many
ways you can help!
+
Citing kikuchipy
================
diff --git a/doc/tutorials/load_save_data.ipynb b/doc/tutorials/load_save_data.ipynb
index 9d9b5dd9..4a3ec60b 100644
--- a/doc/tutorials/load_save_data.ipynb
+++ b/doc/tutorials/load_save_data.ipynb
@@ -495,6 +495,7 @@
"\n",
"| Format | Read | Write |\n",
"| ------------------------------------------------------------------------- | ---- | ----- |\n",
+ "| [ebsdsim EBSD master pattern npz](#ebsdsim-ebsd-master-pattern-npz) | Yes | No |\n",
"| [EMsoft simulated EBSD HDF5](#EMsoft-simulated-EBSD-HDF5) | Yes | No |\n",
"| [EMsoft EBSD master pattern HDF5](#EMsoft-EBSD-master-pattern-HDF5) | Yes | No |\n",
"| [EMsoft ECP master pattern HDF5](#EMsoft-ECP-master-pattern-HDF5) | Yes | No |\n",
@@ -518,6 +519,35 @@
""
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### ebsdsim EBSD master pattern npz\n",
+ "\n",
+ "Dynamically simulated EBSD patterns saved by [ebsdsim](https://github.com/ZacharyVarley/ebsdsim) in *.npz files can be read as an `EBSDMasterPattern` signal.\n",
+ "\n",
+ "\n",
+ "\n",
+ "Note\n",
+ "\n",
+ "This reader requires that *ebsdsim* is installed, which is an optional requirement.\n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# mp_ebsdsim = kp.load(\n",
+ "# \"file.npz\",\n",
+ "# hemisphere=\"upper\", # \"upper\", \"lower\", \"both\"\n",
+ "# )"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -1210,7 +1240,7 @@
],
"metadata": {
"kernelspec": {
- "display_name": "Python 3 (ipykernel)",
+ "display_name": "kp",
"language": "python",
"name": "python3"
},
@@ -1224,7 +1254,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.12.7"
+ "version": "3.14.5"
},
"widgets": {
"application/vnd.jupyter.widget-state+json": {
diff --git a/doc/user/index.rst b/doc/user/index.rst
index d939c05a..2f53c1ef 100644
--- a/doc/user/index.rst
+++ b/doc/user/index.rst
@@ -24,4 +24,4 @@ User guide
applications.rst
open_datasets.rst
related_projects.rst
- License
+ licensing.rst
diff --git a/doc/user/installation.rst b/doc/user/installation.rst
index 5d5bebc8..ec6efa90 100644
--- a/doc/user/installation.rst
+++ b/doc/user/installation.rst
@@ -125,6 +125,9 @@ This is a list of core package dependencies:
Some functionality requires optional dependencies:
+* `ebsdsim `__: Dynamical EBSD simulations and
+ reading of simulations produced by `ebsdsim`.
+ Currently unavailable from conda-forge.
* :doc:`ipywidgets `: Interactive widgets in Jupyter notebooks.
* `IPython `__: Interactive widgets in Jupyter
notebooks.
diff --git a/doc/user/licensing.rst b/doc/user/licensing.rst
new file mode 100644
index 00000000..e52b1f71
--- /dev/null
+++ b/doc/user/licensing.rst
@@ -0,0 +1,16 @@
+=========
+Licensing
+=========
+
+kikuchipy is licensed under the `GNU General Public License v3+
+`__.
+Select parts of the public API are individually licensed under the `BSD 3-Clause License
+`__, allowing reuse *of those parts only*
+in both GPL and non-GPL projects.
+The parts of the API licensed under BSD are marked as such in the
+:doc:`/reference/index`.
+
+Our licenses can be found here:
+
+- `GPLv3+ `__
+- `BSD 3-Clause `__
diff --git a/doc/user/related_projects.rst b/doc/user/related_projects.rst
index 2a807892..1b6ffae5 100644
--- a/doc/user/related_projects.rst
+++ b/doc/user/related_projects.rst
@@ -2,8 +2,8 @@
Related projects
================
-This is a non-exhaustive list of related, open-source projects for analysis of
-EBSD data that users of kikuchipy might find useful:
+This is a non-exhaustive list of related, open-source projects for analysis of EBSD data
+that users of kikuchipy might find useful:
- :doc:`HyperSpy `: Python library with tools for multi-dimensional data
analysis. kikuchipy extends this library for EBSD analysis.
@@ -21,6 +21,9 @@ EBSD data that users of kikuchipy might find useful:
- :doc:`PyEBSDIndex `: Python library for Hough/Radon based EBSD
indexing.
kikuchipy depends on this library for Hough indexing.
+- `ebsdsim `__: Python library for fast
+ dynamical EBSD master pattern simulation.
+ kikuchipy can read master patterns generated with this software.
- `OpenECCI `__: GUI-based software for controlled
Electron Channelling Contrast Imaging (ECCI) analysis of crystal defects in an SEM.
- `MTEX `__: MATLAB toolbox for analyzing and modeling
@@ -38,3 +41,8 @@ EBSD data that users of kikuchipy might find useful:
digital image correlation data.
- `pycotem `__: Python package for working with
crystal orientations in transmission electron microscopy.
+- `ReciPro `__: Standalone multipurpose
+ crystallographic GUI software for Windows.
+ Has numerous features, including EBSD simulation.
+- `EMSphinxEBSD `__: GUI
+ for indexing EBSD patterns using spherical harmonics, part of the EMSphinx software.
diff --git a/pyproject.toml b/pyproject.toml
index 1807665d..109f3774 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -9,6 +9,9 @@ name = "kikuchipy"
authors = [{name = "kikuchipy developers"}]
maintainers = [{name = "Håkon Wiik Ånes", email = "hwaanes@gmail.com"}]
description = "Processing, simulating, and indexing of electron backscatter diffraction (EBSD) patterns"
+# TODO: Use once this syntax is picked up by 'pip show kikuchipy' after install
+# license-expression = "GPL-3.0-or-later AND BSD-3-Clause"
+# license-files = ["LICENSE", "LICENSE-BSD-3-Clause"]
license = {file = "LICENSE"}
readme = {file = "README.rst", content-type = "text/x-rst"}
dynamic = ["version"]
@@ -23,6 +26,7 @@ classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
+ "License :: OSI Approved :: BSD License",
"Natural Language :: English",
"Operating System :: OS Independent",
"Topic :: Scientific/Engineering",
@@ -65,24 +69,27 @@ dependencies = [
[project.optional-dependencies]
all = [
+ "ebsdsim >= 0.1.3",
"ipywidgets",
"IPython",
"nlopt",
- "pooch >= 1.3.0",
+ "pooch >= 1.3.0",
"psygnal",
# Restriction due to https://github.com/USNavalResearchLaboratory/PyEBSDIndex/issues/80
"pyebsdindex >= 0.3.9.2, != 0.3.10",
"pyvista",
]
doc = [
+ "ebsdsim >= 0.1.3",
"ipywidgets",
"IPython",
"ipympl",
"memory_profiler",
"nbsphinx >= 0.7",
# Restriction due to https://github.com/pyxem/kikuchipy/issues/739
- "numpydoc != 1.9.0",
+ "numpydoc != 1.9.0",
"nlopt",
+ "pooch >= 1.3.0",
"pydata-sphinx-theme",
"pyebsdindex >= 0.3.9.2, != 0.3.10",
"pyvista",
@@ -96,22 +103,22 @@ doc = [
]
tests = [
"numpydoc",
- "pooch",
- "pytest >= 5.4",
+ "pooch >= 1.3.0",
+ "pytest >= 5.4",
"pytest-benchmark",
"pytest-rerunfailures",
"pytest-xdist",
]
coverage = [
- "coverage >= 5.0",
- "pytest-cov >= 2.8.1",
+ "coverage >= 5.0",
+ "pytest-cov >= 2.8.1",
]
dev = [
- "black[jupyter] >= 23.1",
+ "black[jupyter] >= 23.1",
"hatch",
"isort",
"outdated",
- "pre-commit >= 1.16",
+ "pre-commit >= 1.16",
"ruff",
"kikuchipy[doc,tests,coverage]",
]
@@ -191,6 +198,7 @@ filterwarnings = [
]
markers = [
"weekly: mark test as running only weekly on CI",
+ "gpu: mark test as running only when wgpu has a GPU available",
]
xfail_strict = true
diff --git a/src/kikuchipy/_constants.py b/src/kikuchipy/_constants.py
index 8959cbef..0fd71463 100644
--- a/src/kikuchipy/_constants.py
+++ b/src/kikuchipy/_constants.py
@@ -1,20 +1,7 @@
#
-# Copyright 2019-2026 the kikuchipy developers
+# Copyright 2026 the kikuchipy developers
#
-# This file is part of kikuchipy.
-#
-# kikuchipy 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.
-#
-# kikuchipy 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 kikuchipy. If not, see .
+# SPDX-License-Identifier: BSD-3-Clause
#
"""Constants and such useful across modules."""
@@ -32,6 +19,7 @@
"rosettasciio",
"scikit-image",
# Optional
+ "ebsdsim",
"IPython",
"ipywidgets",
"nlopt",
diff --git a/src/kikuchipy/data/_dummy_files/ebsdsim_master_pattern_npz.py b/src/kikuchipy/data/_dummy_files/ebsdsim_master_pattern_npz.py
new file mode 100644
index 00000000..686e839a
--- /dev/null
+++ b/src/kikuchipy/data/_dummy_files/ebsdsim_master_pattern_npz.py
@@ -0,0 +1,41 @@
+#
+# Copyright 2019-2026 the kikuchipy developers
+#
+# This file is part of kikuchipy.
+#
+# kikuchipy 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.
+#
+# kikuchipy 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 kikuchipy. If not, see .
+#
+
+"""Creation of a small EBSD master pattern simulation using ebsdsim for
+testing and IO documentation.
+"""
+
+from pathlib import Path
+
+import ebsdsim as es
+
+
+def create_small_ebsdsim_npz_file(path: Path) -> None:
+ mp = es.master_pattern_from_cif(
+ "Ni.cif",
+ halfw=10,
+ voltage_kv=20.0,
+ sigma_deg=70.0,
+ omega_deg=0.0,
+ energy_binwidth_keV=10.0,
+ mc_backend="surrogate",
+ rank=3,
+ dmin=0.2,
+ )
+ es.save_master_pattern(mp, path)
diff --git a/src/kikuchipy/io/_io.py b/src/kikuchipy/io/_io.py
index 3aca21f0..d93f4169 100644
--- a/src/kikuchipy/io/_io.py
+++ b/src/kikuchipy/io/_io.py
@@ -1,5 +1,5 @@
#
-# Copyright 2019-2025 the kikuchipy developers
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -19,6 +19,7 @@
import glob
import importlib
+import logging
import os
from pathlib import Path
from typing import TYPE_CHECKING
@@ -27,11 +28,20 @@
from hyperspy.misc.utils import find_subclasses
from hyperspy.signal import BaseSignal
import numpy as np
+import orix.crystal_map as ocm
import yaml
from kikuchipy.io._util import _ensure_directory, _get_input_bool, _overwrite
import kikuchipy.signals
+if TYPE_CHECKING: # pragma: no cover
+ from kikuchipy.signals.ebsd import EBSD, LazyEBSD
+ from kikuchipy.signals.ebsd_master_pattern import (
+ EBSDMasterPattern,
+ LazyEBSDMasterPattern,
+ )
+ from kikuchipy.signals.ecp_master_pattern import ECPMasterPattern
+
PLUGINS: list = []
WRITE_EXTENSIONS = []
specification_paths = list(Path(__file__).parent.rglob("specification.yaml"))
@@ -45,13 +55,7 @@
WRITE_EXTENSIONS.append(ext)
-if TYPE_CHECKING: # pragma: no cover
- from kikuchipy.signals.ebsd import EBSD, LazyEBSD
- from kikuchipy.signals.ebsd_master_pattern import (
- EBSDMasterPattern,
- LazyEBSDMasterPattern,
- )
- from kikuchipy.signals.ecp_master_pattern import ECPMasterPattern
+_logger = logging.getLogger(__name__)
def load(
@@ -182,6 +186,15 @@ def _dict2signal(
if "Signal" in md and "signal_type" in md["Signal"]:
signal_type = md["Signal"]["signal_type"]
+ if "phase" in signal_dict and isinstance(signal_dict["phase"], dict):
+ try:
+ signal_dict["phase"] = ocm.Phase(**signal_dict["phase"])
+ except Exception as err:
+ _logger.debug(
+ "An error was raised when trying to parse phase information from the "
+ f"reader: {err}"
+ )
+
signal = _assign_signal_subclass(
signal_dimension=2,
signal_type=signal_type,
diff --git a/src/kikuchipy/io/plugins/__init__.py b/src/kikuchipy/io/plugins/__init__.py
index 85e196a9..c9151729 100644
--- a/src/kikuchipy/io/plugins/__init__.py
+++ b/src/kikuchipy/io/plugins/__init__.py
@@ -1,4 +1,5 @@
-# Copyright 2019-2024 The kikuchipy developers
+#
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -14,6 +15,7 @@
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see .
+#
"""Input/output plugins.
@@ -27,6 +29,7 @@
bruker_h5ebsd
ebsd_directory
+ ebsdsim_master_pattern
edax_binary
edax_h5ebsd
emsoft_ebsd
diff --git a/src/kikuchipy/io/plugins/bruker_h5ebsd/__init__.py b/src/kikuchipy/io/plugins/bruker_h5ebsd/__init__.py
index 2f857066..f2ce0c16 100644
--- a/src/kikuchipy/io/plugins/bruker_h5ebsd/__init__.py
+++ b/src/kikuchipy/io/plugins/bruker_h5ebsd/__init__.py
@@ -1,4 +1,5 @@
-# Copyright 2019-2024 The kikuchipy developers
+#
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -14,6 +15,9 @@
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see .
+#
+
+"""Reader of EBSD data from a Bruker Nano h5ebsd file."""
import lazy_loader
diff --git a/src/kikuchipy/io/plugins/bruker_h5ebsd/_api.py b/src/kikuchipy/io/plugins/bruker_h5ebsd/_api.py
index a7ab28f5..9295c52e 100644
--- a/src/kikuchipy/io/plugins/bruker_h5ebsd/_api.py
+++ b/src/kikuchipy/io/plugins/bruker_h5ebsd/_api.py
@@ -17,8 +17,6 @@
# along with kikuchipy. If not, see .
#
-"""Reader of EBSD data from a Bruker Nano h5ebsd file."""
-
from pathlib import Path
import h5py
diff --git a/src/kikuchipy/io/plugins/ebsd_directory/__init__.py b/src/kikuchipy/io/plugins/ebsd_directory/__init__.py
index 2f857066..ddaaa997 100644
--- a/src/kikuchipy/io/plugins/ebsd_directory/__init__.py
+++ b/src/kikuchipy/io/plugins/ebsd_directory/__init__.py
@@ -1,4 +1,5 @@
-# Copyright 2019-2024 The kikuchipy developers
+#
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -14,6 +15,9 @@
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see .
+#
+
+"""Reader of EBSD patterns from a dictionary of images."""
import lazy_loader
diff --git a/src/kikuchipy/io/plugins/ebsd_directory/_api.py b/src/kikuchipy/io/plugins/ebsd_directory/_api.py
index 6b182c48..723c08cf 100644
--- a/src/kikuchipy/io/plugins/ebsd_directory/_api.py
+++ b/src/kikuchipy/io/plugins/ebsd_directory/_api.py
@@ -17,8 +17,6 @@
# along with kikuchipy. If not, see .
#
-"""Reader of EBSD patterns from a dictionary of images."""
-
import glob
import logging
import os
diff --git a/src/kikuchipy/io/plugins/ebsdsim_master_pattern/__init__.py b/src/kikuchipy/io/plugins/ebsdsim_master_pattern/__init__.py
new file mode 100644
index 00000000..7b2c8160
--- /dev/null
+++ b/src/kikuchipy/io/plugins/ebsdsim_master_pattern/__init__.py
@@ -0,0 +1,13 @@
+#
+# Copyright 2026 the kikuchipy developers
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+"""Reader for ebsdsim master patterns stored in NumPy .npz files."""
+
+import lazy_loader
+
+__getattr__, __dir__, __all__ = lazy_loader.attach_stub(__name__, __file__)
+
+del lazy_loader
diff --git a/src/kikuchipy/io/plugins/ebsdsim_master_pattern/__init__.pyi b/src/kikuchipy/io/plugins/ebsdsim_master_pattern/__init__.pyi
new file mode 100644
index 00000000..e29969f6
--- /dev/null
+++ b/src/kikuchipy/io/plugins/ebsdsim_master_pattern/__init__.pyi
@@ -0,0 +1,9 @@
+#
+# Copyright 2026 the kikuchipy developers
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+from ._api import file_reader
+
+__all__ = ["file_reader"]
diff --git a/src/kikuchipy/io/plugins/ebsdsim_master_pattern/_api.py b/src/kikuchipy/io/plugins/ebsdsim_master_pattern/_api.py
new file mode 100644
index 00000000..86bf09d2
--- /dev/null
+++ b/src/kikuchipy/io/plugins/ebsdsim_master_pattern/_api.py
@@ -0,0 +1,247 @@
+#
+# Copyright 2026 the kikuchipy developers
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+from kikuchipy._constants import verify_dependency_or_raise
+
+verify_dependency_or_raise("ebsdsim", "Reading of an EBSD master pattern from ebsdsim")
+
+from pathlib import Path
+from typing import Any, Literal, TypedDict, get_args
+
+from diffpy.structure import Atom, Lattice, Structure
+import ebsdsim as es
+import numpy as np
+
+HEMISPHERE = Literal["upper", "lower", "both"]
+
+
+class ebsdsimSite(TypedDict):
+ index: int
+ atomic_number: int
+ symbol: str
+ fract: list[float]
+ occupancy: float
+ b_iso_angstrom_sq: float
+ b_iso_nm_sq: float
+ multiplicity: int
+
+
+class ebsdsimCell(TypedDict):
+ a_angstrom: float
+ b_angstrom: float
+ c_angstrom: float
+ alpha_deg: float
+ beta_deg: float
+ gamma_deg: float
+ volume_angstrom3: float
+ density_g_cm3: float
+ lattice_centering: Literal["A", "B", "C", "F", "I", "R", "H", "P"]
+ space_group: int
+ pg_num: int
+ average_atomic_number: float
+ average_atomic_weight: float
+ n_sites: int
+ sites: list[ebsdsimSite]
+
+
+def get_orix_phase_dictionary_from_ebsdsim_cell(cell: ebsdsimCell) -> dict[str, Any]:
+ """Return a dictionary that can be passed directly to the orix phase
+ constructor, parsed from the ebsdsim cell dictionary.
+ """
+ sg = cell["space_group"]
+ atoms = []
+ for site in cell["sites"]:
+ atom = Atom(
+ atype=site["symbol"],
+ xyz=site["fract"],
+ occupancy=site["occupancy"],
+ Uisoequiv=site["b_iso_angstrom_sq"] / (8 * np.pi**2),
+ )
+ atoms.append(atom)
+ lattice = Lattice(
+ a=cell["a_angstrom"],
+ b=cell["b_angstrom"],
+ c=cell["c_angstrom"],
+ alpha=cell["alpha_deg"],
+ beta=cell["beta_deg"],
+ gamma=cell["gamma_deg"],
+ )
+ structure = Structure(lattice=lattice, atoms=atoms)
+
+ return {"space_group": sg, "structure": structure}
+
+
+def file_reader(
+ filename: str | Path, hemisphere: HEMISPHERE = "upper", lazy: bool = False
+) -> list[dict]:
+ """Read an ebsdsim EBSD master pattern from a NumPy .npz file.
+
+ Not meant to be used directly; use :func:`~kikuchipy.load`.
+
+ Parameters
+ ----------
+ filename
+ Path to the .npz file written by
+ :func:`ebsdsim.save_master_pattern`.
+ hemisphere
+ Which hemisphere to return: "upper" (default), "lower", or
+ "both".
+ lazy
+ Not supported; included for API compatibility. Data are always
+ loaded eagerly.
+
+ Returns
+ -------
+ signal_dict_list
+ Data, axes, metadata, original metadata, and a phase dict.
+
+ Notes
+ -----
+ This function carries a BSD 3-Clause license.
+ """
+ hemi = hemisphere.lower()
+ hemi_options = get_args(HEMISPHERE)
+ if hemi not in hemi_options:
+ hemi_options_str = ", ".join(hemi_options)
+ raise ValueError(
+ f"Hemisphere must be one of {hemi_options_str}, not {hemisphere!r}"
+ )
+
+ fpath = Path(filename)
+
+ loaded = es.load_master_pattern(fpath)
+
+ # loaded.data shape: (E, S, H, side, side)
+ # Take site-integrated (S index 0) and drop that axis.
+ energy_data = loaded.data[:, 0, :, :, :] # (E, H, side, side)
+
+ axes_meta = loaded.axes
+ n_bins = loaded.n_bins
+ n_hemispheres = axes_meta["hemisphere_dim"]
+
+ # Use per-bin energy slices when available; fall back to the
+ # integrated slice
+ if n_bins > 0 and axes_meta["energy_dim"] > 1:
+ bin_indices = axes_meta["bin_to_energy_index"]
+ energy_data = energy_data[bin_indices, :, :, :] # (n_bins, H, side, side)
+ voltages = np.asarray(loaded.bin_voltages_kv, dtype=np.float32)
+ else:
+ energy_data = energy_data[0:1, :, :, :] # (1, H, side, side)
+ voltages = np.zeros(1, dtype=np.float32)
+
+ n_energy = energy_data.shape[0]
+
+ # Hemisphere selection
+ if hemi == "upper":
+ data = energy_data[:, 0, :, :] # (n_energy, side, side)
+ elif hemi == "lower":
+ if n_hemispheres < 2:
+ raise ValueError(
+ "File does not contain lower hemisphere data; use hemisphere='upper'"
+ )
+ data = energy_data[:, 1, :, :] # (n_energy, side, side)
+ else: # "both"
+ # For centrosymmetric materials, ebsdsim only stores the northern
+ # hemisphere (hemisphere_dim=1). Duplicate it so "both" always
+ # yields a size-2 hemisphere axis, consistent with EMsoft behaviour.
+ nh = energy_data[:, 0:1, :, :] # (n_energy, 1, side, side)
+ if n_hemispheres == 1:
+ sh = nh
+ else:
+ sh = energy_data[:, 1:2, :, :]
+ data = np.moveaxis(
+ np.concatenate([nh, sh], axis=1), 1, 0
+ ) # (2, n_energy, side, side)
+
+ # Squeeze size-1 leading navigation axes so single-energy files
+ # produce a plain 2-D (or 1-D navigation) signal.
+ if n_energy == 1:
+ if hemi == "both":
+ data = data[:, 0, :, :] # (2, side, side)
+ else:
+ data = data[0, :, :] # (side, side)
+
+ # Build axes list
+ side = loaded.side
+ axes = []
+ idx = 0
+
+ if hemi == "both":
+ axes.append(
+ {
+ "size": 2,
+ "index_in_array": idx,
+ "name": "hemisphere",
+ "scale": 1.0,
+ "offset": 0.0,
+ "units": "",
+ }
+ )
+ idx += 1
+
+ if n_energy > 1:
+ energy_scale = float(np.mean(np.diff(voltages)))
+ energy_offset = float(voltages[0]) - energy_scale / 2
+ axes.append(
+ {
+ "size": n_energy,
+ "index_in_array": idx,
+ "name": "energy",
+ "scale": energy_scale,
+ "offset": energy_offset,
+ "units": "keV",
+ }
+ )
+ idx += 1
+
+ axes.append(
+ {
+ "size": side,
+ "index_in_array": idx,
+ "name": "height",
+ "scale": 1.0,
+ "offset": -(side // 2),
+ "units": "px",
+ }
+ )
+ axes.append(
+ {
+ "size": side,
+ "index_in_array": idx + 1,
+ "name": "width",
+ "scale": 1.0,
+ "offset": -(side // 2),
+ "units": "px",
+ }
+ )
+
+ # Metadata
+ md = {
+ "Signal": {"signal_type": "EBSDMasterPattern", "record_by": "image"},
+ "General": {"title": fpath.stem, "original_filename": fpath.name},
+ }
+
+ # Return the raw cell dictionary so that the caller can build an
+ # orix phase
+ phase_dict = get_orix_phase_dictionary_from_ebsdsim_cell(loaded.meta["cell"])
+ source = loaded.meta.get("source", None)
+ if source is not None:
+ name = Path(source).stem
+ else:
+ name = fpath.stem
+ phase_dict["name"] = name
+
+ return [
+ {
+ "data": data,
+ "axes": axes,
+ "metadata": md,
+ "original_metadata": loaded.meta,
+ "projection": "lambert",
+ "phase": phase_dict,
+ "hemisphere": hemisphere,
+ }
+ ]
diff --git a/src/kikuchipy/io/plugins/ebsdsim_master_pattern/specification.yaml b/src/kikuchipy/io/plugins/ebsdsim_master_pattern/specification.yaml
new file mode 100644
index 00000000..276ee9c6
--- /dev/null
+++ b/src/kikuchipy/io/plugins/ebsdsim_master_pattern/specification.yaml
@@ -0,0 +1,16 @@
+##
+## Copyright 2026 the kikuchipy developers
+##
+## SPDX-License-Identifier: BSD-3-Clause
+##
+
+name: ebsdsim_master_pattern
+description: >
+ Read support for a dynamical electron backscatter diffraction master
+ pattern simulated produced by the Python package 'ebsdsim' and stored
+ in a NumPy zipped archive *.npz file.
+file_extensions: ['npz']
+default_extension: 0
+writes: False
+manufacturer: ebsdsim
+footprints: []
diff --git a/src/kikuchipy/io/plugins/edax_binary/__init__.py b/src/kikuchipy/io/plugins/edax_binary/__init__.py
index 2f857066..7d54db8f 100644
--- a/src/kikuchipy/io/plugins/edax_binary/__init__.py
+++ b/src/kikuchipy/io/plugins/edax_binary/__init__.py
@@ -1,4 +1,5 @@
-# Copyright 2019-2024 The kikuchipy developers
+#
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -14,6 +15,12 @@
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see .
+#
+
+"""Reader of EBSD data from EDAX TSL UP1/2 files.
+
+The reader is adapted from the EDAX UP1/2 reader in PyEBSDIndex.
+"""
import lazy_loader
diff --git a/src/kikuchipy/io/plugins/edax_binary/_api.py b/src/kikuchipy/io/plugins/edax_binary/_api.py
index ad9e269c..6c0adf25 100644
--- a/src/kikuchipy/io/plugins/edax_binary/_api.py
+++ b/src/kikuchipy/io/plugins/edax_binary/_api.py
@@ -17,11 +17,6 @@
# along with kikuchipy. If not, see .
#
-"""Reader of EBSD data from EDAX TSL UP1/2 files.
-
-The reader is adapted from the EDAX UP1/2 reader in PyEBSDIndex.
-"""
-
from pathlib import Path
from typing import BinaryIO
import warnings
diff --git a/src/kikuchipy/io/plugins/edax_h5ebsd/__init__.py b/src/kikuchipy/io/plugins/edax_h5ebsd/__init__.py
index 2f857066..eb6a6e75 100644
--- a/src/kikuchipy/io/plugins/edax_h5ebsd/__init__.py
+++ b/src/kikuchipy/io/plugins/edax_h5ebsd/__init__.py
@@ -1,4 +1,5 @@
-# Copyright 2019-2024 The kikuchipy developers
+#
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -14,6 +15,9 @@
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see .
+#
+
+"""Reader of EBSD data from an EDAX TSL h5ebsd file."""
import lazy_loader
diff --git a/src/kikuchipy/io/plugins/edax_h5ebsd/_api.py b/src/kikuchipy/io/plugins/edax_h5ebsd/_api.py
index 359fd2d2..724ed072 100644
--- a/src/kikuchipy/io/plugins/edax_h5ebsd/_api.py
+++ b/src/kikuchipy/io/plugins/edax_h5ebsd/_api.py
@@ -17,8 +17,6 @@
# along with kikuchipy. If not, see .
#
-"""Reader of EBSD data from an EDAX TSL h5ebsd file."""
-
from pathlib import Path
import h5py
diff --git a/src/kikuchipy/io/plugins/emsoft_ebsd/__init__.py b/src/kikuchipy/io/plugins/emsoft_ebsd/__init__.py
index 2f857066..70d98c34 100644
--- a/src/kikuchipy/io/plugins/emsoft_ebsd/__init__.py
+++ b/src/kikuchipy/io/plugins/emsoft_ebsd/__init__.py
@@ -1,4 +1,5 @@
-# Copyright 2019-2024 The kikuchipy developers
+#
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -14,6 +15,9 @@
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see .
+#
+
+"""Reader of simulated EBSD patterns from an EMsoft HDF5 file."""
import lazy_loader
diff --git a/src/kikuchipy/io/plugins/emsoft_ebsd/_api.py b/src/kikuchipy/io/plugins/emsoft_ebsd/_api.py
index bcbe68e2..8b27d489 100644
--- a/src/kikuchipy/io/plugins/emsoft_ebsd/_api.py
+++ b/src/kikuchipy/io/plugins/emsoft_ebsd/_api.py
@@ -17,8 +17,6 @@
# along with kikuchipy. If not, see .
#
-"""Reader of simulated EBSD patterns from an EMsoft HDF5 file."""
-
import os
from pathlib import Path
diff --git a/src/kikuchipy/io/plugins/emsoft_ebsd_master_pattern/__init__.py b/src/kikuchipy/io/plugins/emsoft_ebsd_master_pattern/__init__.py
index 2f857066..0d7afd53 100644
--- a/src/kikuchipy/io/plugins/emsoft_ebsd_master_pattern/__init__.py
+++ b/src/kikuchipy/io/plugins/emsoft_ebsd_master_pattern/__init__.py
@@ -1,4 +1,5 @@
-# Copyright 2019-2024 The kikuchipy developers
+#
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -14,6 +15,9 @@
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see .
+#
+
+"""Reader of simulated EBSD master patterns from an EMsoft HDF5 file."""
import lazy_loader
diff --git a/src/kikuchipy/io/plugins/emsoft_ebsd_master_pattern/_api.py b/src/kikuchipy/io/plugins/emsoft_ebsd_master_pattern/_api.py
index 3ed98290..64f2e36f 100644
--- a/src/kikuchipy/io/plugins/emsoft_ebsd_master_pattern/_api.py
+++ b/src/kikuchipy/io/plugins/emsoft_ebsd_master_pattern/_api.py
@@ -1,5 +1,5 @@
#
-# Copyright 2019-2025 the kikuchipy developers
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -10,13 +10,12 @@
#
# kikuchipy 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
+# 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 kikuchipy. If not, see .#
-
-"""Reader of simulated EBSD master patterns from an EMsoft HDF5 file."""
+# along with kikuchipy. If not, see .
+#
from pathlib import Path
diff --git a/src/kikuchipy/io/plugins/emsoft_ecp_master_pattern/__init__.py b/src/kikuchipy/io/plugins/emsoft_ecp_master_pattern/__init__.py
index 2f857066..a8b39b04 100644
--- a/src/kikuchipy/io/plugins/emsoft_ecp_master_pattern/__init__.py
+++ b/src/kikuchipy/io/plugins/emsoft_ecp_master_pattern/__init__.py
@@ -1,4 +1,5 @@
-# Copyright 2019-2024 The kikuchipy developers
+#
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -14,6 +15,9 @@
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see .
+#
+
+"""Reader of simulated ECP master patterns from an EMsoft HDF5 file."""
import lazy_loader
diff --git a/src/kikuchipy/io/plugins/emsoft_ecp_master_pattern/_api.py b/src/kikuchipy/io/plugins/emsoft_ecp_master_pattern/_api.py
index bb1b9efd..a38c717d 100644
--- a/src/kikuchipy/io/plugins/emsoft_ecp_master_pattern/_api.py
+++ b/src/kikuchipy/io/plugins/emsoft_ecp_master_pattern/_api.py
@@ -1,5 +1,5 @@
#
-# Copyright 2019-2025 the kikuchipy developers
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -10,13 +10,12 @@
#
# kikuchipy 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
+# 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 kikuchipy. If not, see .#
-
-"""Reader of simulated ECP master patterns from an EMsoft HDF5 file."""
+# along with kikuchipy. If not, see .
+#
from pathlib import Path
diff --git a/src/kikuchipy/io/plugins/emsoft_tkd_master_pattern/__init__.py b/src/kikuchipy/io/plugins/emsoft_tkd_master_pattern/__init__.py
index 2f857066..b04c5362 100644
--- a/src/kikuchipy/io/plugins/emsoft_tkd_master_pattern/__init__.py
+++ b/src/kikuchipy/io/plugins/emsoft_tkd_master_pattern/__init__.py
@@ -1,4 +1,5 @@
-# Copyright 2019-2024 The kikuchipy developers
+#
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -14,6 +15,9 @@
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see .
+#
+
+"""Reader of simulated TKD master patterns from an EMsoft HDF5 file."""
import lazy_loader
diff --git a/src/kikuchipy/io/plugins/emsoft_tkd_master_pattern/_api.py b/src/kikuchipy/io/plugins/emsoft_tkd_master_pattern/_api.py
index 75624965..acf8571f 100644
--- a/src/kikuchipy/io/plugins/emsoft_tkd_master_pattern/_api.py
+++ b/src/kikuchipy/io/plugins/emsoft_tkd_master_pattern/_api.py
@@ -1,5 +1,5 @@
#
-# Copyright 2019-2025 the kikuchipy developers
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -10,13 +10,12 @@
#
# kikuchipy 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
+# 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 kikuchipy. If not, see .#
-
-"""Reader of simulated TKD master patterns from an EMsoft HDF5 file."""
+# along with kikuchipy. If not, see .
+#
from pathlib import Path
diff --git a/src/kikuchipy/io/plugins/kikuchipy_h5ebsd/__init__.py b/src/kikuchipy/io/plugins/kikuchipy_h5ebsd/__init__.py
index 2f857066..9048de84 100644
--- a/src/kikuchipy/io/plugins/kikuchipy_h5ebsd/__init__.py
+++ b/src/kikuchipy/io/plugins/kikuchipy_h5ebsd/__init__.py
@@ -1,4 +1,5 @@
-# Copyright 2019-2024 The kikuchipy developers
+#
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -14,6 +15,9 @@
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see .
+#
+
+"""Reader and writer of EBSD data from a kikuchipy h5ebsd file."""
import lazy_loader
diff --git a/src/kikuchipy/io/plugins/kikuchipy_h5ebsd/_api.py b/src/kikuchipy/io/plugins/kikuchipy_h5ebsd/_api.py
index c8042d26..da4c6d99 100644
--- a/src/kikuchipy/io/plugins/kikuchipy_h5ebsd/_api.py
+++ b/src/kikuchipy/io/plugins/kikuchipy_h5ebsd/_api.py
@@ -17,8 +17,6 @@
# along with kikuchipy. If not, see .
#
-"""Reader and writer of EBSD data from a kikuchipy h5ebsd file."""
-
import os
from pathlib import Path
from typing import TYPE_CHECKING
diff --git a/src/kikuchipy/io/plugins/nordif/__init__.py b/src/kikuchipy/io/plugins/nordif/__init__.py
index 2f857066..82458559 100644
--- a/src/kikuchipy/io/plugins/nordif/__init__.py
+++ b/src/kikuchipy/io/plugins/nordif/__init__.py
@@ -1,4 +1,5 @@
-# Copyright 2019-2024 The kikuchipy developers
+#
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -14,6 +15,9 @@
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see .
+#
+
+"""Reader and writer of EBSD patterns from a NORDIF binary file."""
import lazy_loader
diff --git a/src/kikuchipy/io/plugins/nordif/_api.py b/src/kikuchipy/io/plugins/nordif/_api.py
index f812e7bb..f2790800 100644
--- a/src/kikuchipy/io/plugins/nordif/_api.py
+++ b/src/kikuchipy/io/plugins/nordif/_api.py
@@ -17,8 +17,6 @@
# along with kikuchipy. If not, see .
#
-"""Reader and writer of EBSD patterns from a NORDIF binary file."""
-
from io import TextIOWrapper
import logging
import os
diff --git a/src/kikuchipy/io/plugins/nordif_calibration_patterns/__init__.py b/src/kikuchipy/io/plugins/nordif_calibration_patterns/__init__.py
index 2f857066..c4b84430 100644
--- a/src/kikuchipy/io/plugins/nordif_calibration_patterns/__init__.py
+++ b/src/kikuchipy/io/plugins/nordif_calibration_patterns/__init__.py
@@ -1,4 +1,5 @@
-# Copyright 2019-2024 The kikuchipy developers
+#
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -14,6 +15,9 @@
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see .
+#
+
+"""Reader of EBSD calibration patterns from NORDIF files."""
import lazy_loader
diff --git a/src/kikuchipy/io/plugins/nordif_calibration_patterns/_api.py b/src/kikuchipy/io/plugins/nordif_calibration_patterns/_api.py
index bf399870..a5d1d614 100644
--- a/src/kikuchipy/io/plugins/nordif_calibration_patterns/_api.py
+++ b/src/kikuchipy/io/plugins/nordif_calibration_patterns/_api.py
@@ -17,8 +17,6 @@
# along with kikuchipy. If not, see .
#
-"""Reader of EBSD calibration patterns from NORDIF files."""
-
import os
from pathlib import Path
import warnings
diff --git a/src/kikuchipy/io/plugins/oxford_binary/__init__.py b/src/kikuchipy/io/plugins/oxford_binary/__init__.py
index 2f857066..0c720a5f 100644
--- a/src/kikuchipy/io/plugins/oxford_binary/__init__.py
+++ b/src/kikuchipy/io/plugins/oxford_binary/__init__.py
@@ -1,4 +1,5 @@
-# Copyright 2019-2024 The kikuchipy developers
+#
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -14,6 +15,14 @@
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see .
+#
+
+"""Reader of uncompressed EBSD patterns from a Oxford Instruments binary
+.ebsp file.
+
+Information about the file format was generously provided by Oxford
+Instruments.
+"""
import lazy_loader
diff --git a/src/kikuchipy/io/plugins/oxford_binary/_api.py b/src/kikuchipy/io/plugins/oxford_binary/_api.py
index 66ed8138..f750b00f 100644
--- a/src/kikuchipy/io/plugins/oxford_binary/_api.py
+++ b/src/kikuchipy/io/plugins/oxford_binary/_api.py
@@ -1,4 +1,5 @@
-# Copyright 2019-2024 The kikuchipy developers
+#
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -14,13 +15,7 @@
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see .
-
-"""Reader of uncompressed EBSD patterns from a Oxford Instruments binary
-.ebsp file.
-
-Information about the file format was generously provided by Oxford
-Instruments.
-"""
+#
import logging
import os
diff --git a/src/kikuchipy/io/plugins/oxford_h5ebsd/__init__.py b/src/kikuchipy/io/plugins/oxford_h5ebsd/__init__.py
index 2f857066..d4148e43 100644
--- a/src/kikuchipy/io/plugins/oxford_h5ebsd/__init__.py
+++ b/src/kikuchipy/io/plugins/oxford_h5ebsd/__init__.py
@@ -1,4 +1,5 @@
-# Copyright 2019-2024 The kikuchipy developers
+#
+# Copyright 2019-2026 the kikuchipy developers
#
# This file is part of kikuchipy.
#
@@ -14,6 +15,9 @@
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see .
+#
+
+"""Reader of EBSD data from an Oxford Instruments h5ebsd (H5OINA) file."""
import lazy_loader
diff --git a/src/kikuchipy/io/plugins/oxford_h5ebsd/_api.py b/src/kikuchipy/io/plugins/oxford_h5ebsd/_api.py
index 5daa1790..4943e274 100644
--- a/src/kikuchipy/io/plugins/oxford_h5ebsd/_api.py
+++ b/src/kikuchipy/io/plugins/oxford_h5ebsd/_api.py
@@ -17,8 +17,6 @@
# along with kikuchipy. If not, see .
#
-"""Reader of EBSD data from an Oxford Instruments h5ebsd (H5OINA) file."""
-
import logging
from pathlib import Path
import re
diff --git a/tests/test_io/test_ebsdsim_master_pattern.py b/tests/test_io/test_ebsdsim_master_pattern.py
new file mode 100644
index 00000000..9b657442
--- /dev/null
+++ b/tests/test_io/test_ebsdsim_master_pattern.py
@@ -0,0 +1,111 @@
+#
+# Copyright 2019-2026 the kikuchipy developers
+#
+# This file is part of kikuchipy.
+#
+# kikuchipy 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.
+#
+# kikuchipy 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 kikuchipy. If not, see .
+#
+
+"""Tests for the ebsdsim master pattern reader."""
+
+import pytest
+
+_ = pytest.importorskip("ebsdsim", reason="ebsdsim is not installed")
+import numpy as np
+
+import kikuchipy as kp
+from kikuchipy.io.plugins.ebsdsim_master_pattern import file_reader
+
+
+@pytest.mark.gpu
+class TestEbsdsimMasterPatternReader:
+ # The Ni file has halfw=10 (side=21), 2 energy bins ([15, 5] keV),
+ # and hemisphere_dim=1 (centrosymmetric → only northern hemisphere stored).
+
+ def test_default_load(self, ebsdsim_master_pattern_file):
+ mp = kp.load(ebsdsim_master_pattern_file)
+
+ assert isinstance(mp, kp.signals.EBSDMasterPattern)
+ assert mp.data.shape == (2, 21, 21)
+ assert mp.projection == "lambert"
+ assert mp.hemisphere == "upper"
+
+ def test_phase(self, ebsdsim_master_pattern_file):
+ mp: kp.signals.EBSDMasterPattern = kp.load(ebsdsim_master_pattern_file)
+
+ assert mp.phase.name == "Ni"
+ assert mp.phase.space_group.number == 225 # Fm-3m
+ assert str(mp.phase.point_group.name) == "m-3m"
+
+ def test_metadata(self, ebsdsim_master_pattern_file):
+ mp = kp.load(ebsdsim_master_pattern_file)
+
+ assert mp.metadata["Signal"]["signal_type"] == "EBSDMasterPattern"
+ assert mp.metadata["General"]["title"] == "ni_master_pattern"
+
+ def test_energy_axis(self, ebsdsim_master_pattern_file):
+ mp = kp.load(ebsdsim_master_pattern_file)
+
+ energy_axis = mp.axes_manager["energy"]
+ assert energy_axis.size == 2
+ assert energy_axis.units == "keV"
+ # Bins are at 20 and 10 keV; offset is the first bin, scale is
+ # the step
+ np.testing.assert_allclose(energy_axis.axis, [20.0, 10.0], atol=1.0)
+
+ def test_signal_axes(self, ebsdsim_master_pattern_file):
+ mp = kp.load(ebsdsim_master_pattern_file)
+
+ assert mp.axes_manager["height"].size == 21
+ assert mp.axes_manager["width"].size == 21
+ assert mp.axes_manager["height"].units == "px"
+ assert mp.axes_manager["width"].units == "px"
+
+ def test_hemisphere_both(self, ebsdsim_master_pattern_file):
+ mp = kp.load(ebsdsim_master_pattern_file, hemisphere="both")
+
+ # Centrosymmetric: northern hemisphere is duplicated to form the south.
+ assert mp.data.shape == (2, 2, 21, 21)
+ assert mp.hemisphere == "both"
+ np.testing.assert_array_equal(mp.data[0], mp.data[1])
+
+ def test_hemisphere_lower_raises_for_centrosymmetric(
+ self, ebsdsim_master_pattern_file
+ ):
+ with pytest.raises(ValueError, match="lower hemisphere"):
+ kp.load(ebsdsim_master_pattern_file, hemisphere="lower")
+
+ def test_invalid_hemisphere_raises(self, ebsdsim_master_pattern_file):
+ with pytest.raises(ValueError, match="Hemisphere must be one of"):
+ kp.load(ebsdsim_master_pattern_file, hemisphere="north")
+
+ def test_file_reader_returns_list(self, ebsdsim_master_pattern_file):
+ result = file_reader(ebsdsim_master_pattern_file)
+
+ assert isinstance(result, list)
+ assert len(result) == 1
+ d = result[0]
+ assert set(d).issuperset(
+ {
+ "data",
+ "axes",
+ "metadata",
+ "original_metadata",
+ "phase",
+ "projection",
+ "hemisphere",
+ }
+ )
+ assert d["projection"] == "lambert"
+ assert isinstance(d["phase"], dict)
diff --git a/tests/test_signals/test_ebsd_master_pattern.py b/tests/test_signals/test_ebsd_master_pattern.py
index a5bbe3fb..ba74ae11 100644
--- a/tests/test_signals/test_ebsd_master_pattern.py
+++ b/tests/test_signals/test_ebsd_master_pattern.py
@@ -780,26 +780,27 @@ class TestFitPatternDetectorOrientation:
detector orientations.
"""
- detector = kp.detectors.EBSDDetector(
- shape=(480, 640), px_size=50, pc=(20, 20, 15000), convention="emsoft4"
- )
+ def setup_method(self):
+ self.detector = kp.detectors.EBSDDetector(
+ shape=(480, 640), px_size=50, pc=(20, 20, 15000), convention="emsoft4"
+ )
- phase = kp.data.nickel_ebsd_master_pattern_small().phase
+ self.phase = kp.data.nickel_ebsd_master_pattern_small().phase
- hkl = [(1, 1, 1), (2, 0, 0), (2, 2, 0), (3, 1, 1)]
+ self.hkl = [(1, 1, 1), (2, 0, 0), (2, 2, 0), (3, 1, 1)]
- ref = ReciprocalLatticeVector(phase=phase, hkl=hkl)
- ref = ref.symmetrise()
+ ref = ReciprocalLatticeVector(phase=self.phase, hkl=self.hkl)
+ self.ref = ref.symmetrise()
- simulator = kp.simulations.KikuchiPatternSimulator(ref)
+ self.simulator = kp.simulations.KikuchiPatternSimulator(self.ref)
- rotations = Rotation.from_euler([23, 14, 5], degrees=True)
+ self.rotations = Rotation.from_euler([23, 14, 5], degrees=True)
- # Transformation from CSs to cartesian crystal reference frame CSc
- u_o = rotations.to_matrix().squeeze()
+ # Transformation from CSs to cartesian crystal reference frame CSc
+ self.u_o = self.rotations.to_matrix().squeeze()
- # Transformation from CSc to direct crystal reference frame CSk
- u_a = phase.structure.lattice.base
+ # Transformation from CSc to direct crystal reference frame CSk
+ self.u_a = self.phase.structure.lattice.base
def setup_detector_sim_and_u_os(self, tilt, azimuthal, twist):
det = self.detector.deepcopy()