Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EMsoft EBSD master pattern plugin can read a single energy, change energy parameter name #240

Merged
merged 2 commits into from
Oct 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 17 additions & 13 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ All notable changes to this project will be documented in this file. The format
is based on `Keep a Changelog <https://keepachangelog.com/en/1.1.0>`_, and this
project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`_.

Contributors to each release are listed in alphabetical order by first name.
Contributors to each release are listed in alphabetical order by first name.
List entries are sorted in descending chronological order.

Unreleased
==========
Expand All @@ -29,29 +30,32 @@ Added
dictionary of simulated patterns with known orientations.
(`#231 <https://github.com/pyxem/kikuchipy/pull/231>`_,
`#233 <https://github.com/pyxem/kikuchipy/pull/233>`_)
- Reader for EMsoft's simulated EBSD patterns returned by their ``EMEBSD.f90``
program. (`#202 <https://github.com/pyxem/kikuchipy/pull/202>`_)
- EBSD.xmap property storing an orix CrystalMap object. So far only read from
a EMsoft simulated EBSD pattern file. Relevant documentation updated.
(`#226 <https://github.com/pyxem/kikuchipy/pull/226>`_)
- Dependency on the diffsims package (https://github.com/pyxem/diffsims/) for
handling of electron scattering and diffraction.
(`#220 <https://github.com/pyxem/kikuchipy/pull/220>`_)
- Modified Lambert mapping, and its inverse, from points on the unit sphere to a
2D square grid, as implemented in Callahan and De Graef (2013).
(`#214 <https://github.com/pyxem/kikuchipy/pull/214>`_)
- EBSD detector class to handle detector parameters, including detector pixels'
gnomonic coordinates. EBSD reference frame documentation.
(`#204 <https://github.com/pyxem/kikuchipy/pull/204>`_,
`#215 <https://github.com/pyxem/kikuchipy/pull/215>`_)
- Geometrical EBSD simulations, projecting a set of Kikuchi bands and zone axes
onto a detector, which can be added to an EBSD signal as markers.
(`#204 <https://github.com/pyxem/kikuchipy/pull/204>`_,
`#219 <https://github.com/pyxem/kikuchipy/pull/219>`_,
`#232 <https://github.com/pyxem/kikuchipy/pull/232>`_)
- Dependency on the diffsims package (https://github.com/pyxem/diffsims/) for
handling of electron scattering and diffraction.
(`#220 <https://github.com/pyxem/kikuchipy/pull/220>`_)
- EBSD.xmap property storing an orix CrystalMap object. So far only read from
a EMsoft simulated EBSD pattern file. Relevant documentation updated.
(`#226 <https://github.com/pyxem/kikuchipy/pull/226>`_)
- EBSD detector class to handle detector parameters, including detector pixels'
gnomonic coordinates. EBSD reference frame documentation.
(`#204 <https://github.com/pyxem/kikuchipy/pull/204>`_,
`#215 <https://github.com/pyxem/kikuchipy/pull/215>`_)
- Reader for EMsoft's simulated EBSD patterns returned by their ``EMEBSD.f90``
program. (`#202 <https://github.com/pyxem/kikuchipy/pull/202>`_)

Changed
-------
- EMsoft EBSD master pattern plugin can read a single energy pattern. Parameter
`energy_range` changed to `energy`.
(`240 <https://github.com/pyxem/kikuchipy/pull/240>`_)
- Migrating the user guide from `reStructuredText` files to Jupyter Notebooks
built to HTML via the `nbsphinx` package.
(`#236 <https://github.com/pyxem/kikuchipy/pull/236>`_)
Expand Down
8 changes: 4 additions & 4 deletions doc/load_save_data.rst
Original file line number Diff line number Diff line change
Expand Up @@ -392,21 +392,21 @@ can be read into an :class:`~kikuchipy.signals.EBSDMasterPattern` object:
Here, the EMsoft EBSD master pattern
:func:`~kikuchipy.io.plugins.emsoft_ebsd_master_pattern.file_reader` is called,
which takes the optional arguments ``projection``, ``hemisphere`` and
``energy_range``. The spherical projection is read by default. Passing
``energy``. The spherical projection is read by default. Passing
``projection="lambert"`` will read the square Lambert projection instead. The
northern hemisphere is read by default. Passing ``hemisphere="south"`` or
``hemisphere="both"`` will read the southern hemisphere projection or both,
respectively. Master patterns for all beam energies are read by default. Passing
``energy_range=(10, 20)`` will read the master patterns with beam energies from
10 to 20 keV.
``energy=(10, 20)`` or ``energy=15`` will read the master pattern(s) with beam
energies from 10 to 20 keV, or just 15 keV, respectively:

.. code-block::

>>> s = kp.load(
... "master_patterns.h5",
... projection="lambert",
... hemisphere="both",
... energy_range=(10, 20)
... energy=(10, 20)
... )
>>> s
<EBSDMasterPattern, title: , dimensions: (2, 11|1001, 1001)>
Expand Down
49 changes: 25 additions & 24 deletions kikuchipy/io/plugins/emsoft_ebsd_master_pattern.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@

def file_reader(
filename: str,
energy_range: Optional[range] = None,
energy: Optional[range] = None,
projection: str = "spherical",
hemisphere: str = "north",
lazy: bool = False,
Expand All @@ -62,8 +62,8 @@ def file_reader(
----------
filename
Full file path of the HDF file.
energy_range
Range of beam energies for patterns to read. If None is passed
energy
Desired beam energy or energy range. If None is passed
(default), all available energies are read.
projection
Projection(s) to read. Options are "spherical" (default) or
Expand Down Expand Up @@ -120,18 +120,18 @@ def file_reader(
data_group = f["EMData/EBSDmaster"]
energies = data_group["EkeVs"][()]
data_shape, data_slices = _get_data_shape_slices(
npx=f["NMLparameters/EBSDMasterNameList/npx"][()],
npx=f["NMLparameters/EBSDMasterNameList/npx"][()][0],
energies=energies,
energy_range=energy_range,
energy=energy,
)
i_min = data_slices[0].start
i_min = 0 if i_min is None else i_min
min_energy = energies[i_min]

# Account for the Lambert projections being stored as having a 1-dimension
# before the energy dimension
# TODO: Figure out why EMsoft v4.3 have two Lambert projections in both
# northern and southern hemisphere.
# Account for the Lambert projections being stored as having a
# 1-dimension before the energy dimension
# TODO: Figure out why EMsoft v4.3 have two Lambert projections in
# both northern and southern hemisphere.
if projection.lower() == "lambert":
data_slices = (slice(0, 1),) + data_slices

Expand Down Expand Up @@ -166,7 +166,7 @@ def file_reader(
data = data.squeeze()

# Axes scales
energy_scale = energies[1] - energies[0]
energy_scale = f["NMLparameters/MCCLNameList/Ebinsize"][:][0]
scales = np.array([1, energy_scale, 1, 1])

ny, nx, sy, sx = data_shape
Expand Down Expand Up @@ -231,40 +231,41 @@ def _check_file_format(file: File):


def _get_data_shape_slices(
npx: int, energies: np.ndarray, energy_range: Optional[tuple] = None,
npx: int, energies: np.ndarray, energy: Optional[tuple] = None,
) -> Tuple[Tuple, Tuple[slice, ...]]:
"""Determine data shape from number of pixels in a master pattern
quadrant and an energy array.
"""Determine the data shape from half the master pattern side length
and an energy or energy range.

Parameters
----------
npx
Number of pixels along x-direction of the square master pattern.
Half the number of pixels along x-direction of the square master
pattern. Half is used because that is what EMsoft uses.
energies
Beam energies.
energy_range
Range of sought energies.
energy
Desired beam energy or energy range.

Returns
-------
data_shape
Shape of data.
data_slices
Data to get, determined from `energy_range`.

Data to get, determined from `energy`.
"""

data_shape = (npx * 2 + 1,) * 2
data_slices = (slice(None, None),) * 2
if energy_range is None:
if energy is None:
data_slices = (slice(None, None),) + data_slices
data_shape = (len(energies),) + data_shape
else:
i_min = np.argwhere(energies >= energy_range[0])[0][0]
i_max = np.argwhere(energies <= energy_range[1])[-1][0] + 1
elif hasattr(energy, "__iter__"):
i_min = np.argwhere(energies >= energy[0])[0][0]
i_max = np.argwhere(energies <= energy[1])[-1][0] + 1
data_slices = (slice(i_min, i_max),) + data_slices
data_shape = (i_max - i_min,) + data_shape

else: # Assume integer
data_slices = (slice(0, 1),) + data_slices
data_shape = (1,) + data_shape
return data_shape, data_slices


Expand Down
27 changes: 21 additions & 6 deletions kikuchipy/io/plugins/tests/test_emsoft_ebsd_masterpattern.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,11 @@ def test_check_file_format(self, save_path_hdf5):
data=np.array([b"EMEBSDmasterr.f90"], dtype="S17"),
)
with pytest.raises(IOError, match=".* is not in EMsoft's master "):
_ = _check_file_format(f)
_check_file_format(f)

@pytest.mark.parametrize(
(
"npx, energies, energy_range, expected_shape, expected_slices, "
"npx, energies, energy, expected_shape, expected_slices, "
"expected_min_max_energy"
),
[
Expand All @@ -199,19 +199,27 @@ def test_check_file_format(self, save_path_hdf5):
(slice(2, 7), slice(None, None), slice(None, None)),
(18, 24),
),
(
64,
np.linspace(10, 20, 11) * 1.5,
15,
(1, 129, 129),
(slice(0, 1), slice(None, None), slice(None, None)),
(15, 15),
),
],
)
def test_get_data_shape_slices(
self,
npx,
energies,
energy_range,
energy,
expected_shape,
expected_slices,
expected_min_max_energy,
):
data_shape, data_slices = _get_data_shape_slices(
npx=npx, energies=energies, energy_range=energy_range
npx=npx, energies=energies, energy=energy
)

assert data_shape == expected_shape
Expand All @@ -226,9 +234,7 @@ def test_get_data_shape_slices(
def test_load_lazy(self, projection):
"""The Lambert projection's southern hemisphere is stored
chunked.

"""

s = load(
EMSOFT_FILE, projection=projection, hemisphere="south", lazy=True
)
Expand Down Expand Up @@ -324,3 +330,12 @@ def test_crystal_data_2_metadata(self):
}

assert_dictionary(actual_d, desired_d)

@pytest.mark.parametrize(
"energy, desired_shape", [(20, (2, 13, 13)), ((15, 20), (2, 6, 13, 13))]
)
def test_load_energy(self, energy, desired_shape):
"""Ensure desired energy parameters can be passed."""
s = load(EMSOFT_FILE, energy=energy, hemisphere="both")

assert s.data.shape == desired_shape