# -*- coding: utf-8 -*-
# Copyright 2024 Matthew Fitzpatrick.
#
# This program 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, version 3.
#
# This program 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
# this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
r"""For loading quantities from output files generated by ``prismatique``.
"""
#####################################
## Load libraries/packages/modules ##
#####################################
# For deserializing JSON objects.
import json
# For general array handling.
import numpy as np
# For validating and converting objects.
import czekitout.check
import czekitout.convert
# For loading objects from HDF5 files.
import h5pywrappers
# For creating ``hyperspy`` axes and signals.
import hyperspy.axes
import hyperspy.signals
# For calculating quantities related to the modelling of samples.
import prismatique.sample
# For convenient helper functions related to probe scan patterns.
import prismatique.scan
# For convenient helper functions related to STEM simulation output.
import prismatique.stem.output
# For constructing instances of the class :class:`prismatique.stem.sim.Params`
# and for some convenient helper functions.
import prismatique.stem.sim
# For constructing instances of the class :class:`prismatique.hrtem.sim.Params`
# and for some convenient helper functions.
import prismatique.hrtem.sim
##################################
## Define classes and functions ##
##################################
# List of public objects in objects.
__all__ = ["scan_pattern_type",
"grid_dims_in_units_of_probe_shifts",
"probe_positions",
"output_layer_depths",
"defocii",
"num_slices",
"num_frozen_phonon_configs_in_subset",
"cbed_k_x_coords",
"cbed_k_y_coords",
"k_xy_coords_of_3d_stem_output",
"integration_limits_of_2d_stem_output",
"S_matrix_k_xy_vectors",
"hrtem_beam_tilts",
"hrtem_image_x_coords",
"hrtem_image_y_coords"
"potential_slices",
"S_matrix_wavefunctions",
"cbed_wavefunctions",
"cbed_intensity_patterns",
"com_momenta",
"stem_intensity_images",
"azimuthally_integrated_cbed_intensity_patterns",
"hrtem_image_wavefunctions",
"hrtem_intensity_image"]
###########################
## Define error messages ##
###########################
def _check_and_convert_filename(params):
obj_name = "filename"
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
filename = czekitout.convert.to_str_from_str_like(**kwargs)
return filename
def _check_and_convert_skip_validation_and_conversion(params):
module_alias = prismatique.sample
func_alias = module_alias._check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
return skip_validation_and_conversion
_module_alias = \
prismatique.sample
_default_skip_validation_and_conversion = \
_module_alias._default_skip_validation_and_conversion
[docs]def scan_pattern_type(filename,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given STEM simulation output file, load the probe scan pattern
type stored or specified therein.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing or specifying the
probe scan pattern type. Any non-temporary file generated by the
function :func:`prismatique.stem.sim.run` that does not store potential
slice or S-matrix data, is a valid file. In other words, any file
generated by a STEM simulation with originally the basename
``"stem_sim_intensity_output.h5"``, ``"stem_sim_params.json"``, or a
basename of the form
``"stem_sim_wavefunction_output_of_subset_"+str(i)+".h5"``, where ``i``
is a nonnegative integer.
See the documentation for the class
:class:`prismatique.stem.output.Params` for a discussion on the layout
and structure of ``prismatique`` STEM simulation output files.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
pattern_type : "rectangular grid" | "jittered rectangular grid" | "no underlying rectangular grid"
If ``scan_pattern_type=="rectangular grid"``, then the probe positions
making up the scan pattern lie exactly on a regular rectangular grid.
If ``scan_pattern_type=="jittered rectangular grid"``, then the set of
probe positions making up the scan pattern lie is equal to the set of
positions obtained by generating an underlying rectangular grid to which
a random positional deviation is applied to each point. In this case,
the pattern is irregular but rectangular grid-like. If
``scan_pattern_type=="no underlying rectangular grid"``, then the scan
pattern is irregular and not rectangular grid-like, i.e. this case
is different from the previous two.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
pattern_type = _scan_pattern_type(**kwargs)
return pattern_type
def _scan_pattern_type(filename):
current_func_name = "_scan_pattern_type"
try:
sim_params = \
prismatique.stem.sim.Params.load(filename)
sim_params_core_attrs = \
sim_params.get_core_attrs(deep_copy=False)
stem_system_model_params = \
sim_params_core_attrs["stem_system_model_params"]
stem_system_model_params_core_attrs = \
stem_system_model_params.get_core_attrs(deep_copy=False)
scan_config = \
stem_system_model_params_core_attrs["scan_config"]
pattern_type = \
prismatique.scan.pattern_type(scan_config)
except:
try:
path_in_file = "/metadata/probe_positions"
obj_id = h5pywrappers.obj.ID(filename, path_in_file)
attr_name = "pattern type"
attr_id = h5pywrappers.attr.ID(obj_id, attr_name)
pattern_type = h5pywrappers.attr.load(attr_id)
kwargs = {"obj": pattern_type, "obj_name": "pattern_type"}
pattern_type = czekitout.convert.to_str_from_str_like(**kwargs)
kwargs["obj"] = pattern_type
kwargs["accepted_strings"] = ("rectangular grid",
"jittered rectangular grid",
"no underlying rectangular grid")
czekitout.check.if_one_of_any_accepted_strings(**kwargs)
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename,
current_func_name[1:])
raise IOError(err_msg)
return pattern_type
[docs]def grid_dims_in_units_of_probe_shifts(filename,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given STEM simulation output file, load the underlying grid
dimensions of the probe scan pattern, in units of probe shifts, stored or
specified therein.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing or specifying
underlying grid dimensions of the probe scan pattern. Any non-temporary
file generated by the function :func:`prismatique.stem.sim.run` that
does not store potential slice or S-matrix data, is a valid file. In
other words, any file generated by a STEM simulation with originally the
basename ``"stem_sim_intensity_output.h5"``, ``"stem_sim_params.json"``,
or a basename of the form
``"stem_sim_wavefunction_output_of_subset_"+str(i)+".h5"``, where ``i``
is a nonnegative integer.
See the documentation for the class
:class:`prismatique.stem.output.Params` for a discussion on the layout
and structure of ``prismatique`` STEM simulation output files.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
grid_dims : "N/A" | `array_like` (`float`, shape=(``2``))
If ``prismatique.load.scan_pattern_type(filename) == "no underlying
rectangular grid"``, then ``grid_dimensions_in_units_of_probe_shifts ==
"N/A"``, indicating that there is no notion of grid dimensions that is
applicable to the scan pattern used. Otherwise, if
``prismatique.load.scan_pattern_type(filename) != "no underlying
rectangular grid"``, then ``grid_dims[0]`` and ``grid_dims[1]`` are the
number of probe positions along the :math:`x`- and :math:`y`-dimensions
respectively of the underlying rectangular grid of the scanning pattern.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
grid_dims = _grid_dims_in_units_of_probe_shifts(**kwargs)
return grid_dims
def _grid_dims_in_units_of_probe_shifts(filename):
current_func_name = "_grid_dims_in_units_of_probe_shifts"
try:
sim_params = \
prismatique.stem.sim.Params.load(filename)
sim_params_core_attrs = \
sim_params.get_core_attrs(deep_copy=False)
stem_system_model_params = \
sim_params_core_attrs["stem_system_model_params"]
stem_system_model_params_core_attrs = \
stem_system_model_params.get_core_attrs(deep_copy=False)
kwargs = \
{"sample_specification": \
stem_system_model_params_core_attrs["sample_specification"],
"scan_config": \
stem_system_model_params_core_attrs["scan_config"]}
grid_dims = \
prismatique.scan.grid_dims_in_units_of_probe_shifts(**kwargs)
except:
try:
path_in_file = "/metadata/probe_positions"
obj_id = h5pywrappers.obj.ID(filename, path_in_file)
attr_name = "grid dims in units of probe shifts"
attr_id = h5pywrappers.attr.ID(obj_id, attr_name)
grid_dims = h5pywrappers.attr.load(attr_id)
try:
kwargs = {"obj": grid_dims, "obj_name": "grid_dims"}
grid_dims = czekitout.convert.to_pair_of_positive_ints(**kwargs)
except:
kwargs = {"obj": grid_dims, "obj_name": "grid_dims"}
grid_dims = czekitout.convert.to_str_from_str_like(**kwargs)
kwargs["obj"] = grid_dims
kwargs["accepted_strings"] = ("N/A",)
czekitout.check.if_one_of_any_accepted_strings(**kwargs)
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename,
current_func_name[1:])
raise IOError(err_msg)
return grid_dims
def _check_and_convert_force_2_col_shape(params):
obj_name = "force_2_col_shape"
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
force_2_col_shape = czekitout.convert.to_bool(**kwargs)
return force_2_col_shape
_default_force_2_col_shape = True
[docs]def probe_positions(filename,
force_2_col_shape=\
_default_force_2_col_shape,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given STEM simulation output file, load the probe positions
stored or specified therein.
Note that the cartesian coordinate system used to define the probe positions
has its origin coinciding with the bottom left corner of the sample's
supercell [see the documentation for the class
:class:`prismatique.discretization.Params` for a discussion on sample
supercells].
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the probe positions
or specifying the scan pattern. Any non-temporary file generated by the
function :func:`prismatique.stem.sim.run` that does not store potential
slice or S-matrix data, is a valid file. In other words, any file
generated by a STEM simulation with originally the basename
``"stem_sim_intensity_output.h5"``, ``"stem_sim_params.json"``, or a
basename of the form
``"stem_sim_wavefunction_output_of_subset_"+str(i)+".h5"``, where ``i``
is a nonnegative integer.
See the documentation for the class
:class:`prismatique.stem.output.Params` for a discussion on the layout
and structuring of ``prismatique`` STEM simulation output files.
force_2_col_shape : `bool`, optional
If ``force_2_col_shape`` is set to ``False`` and
``prismatique.load.scan_pattern_type(filename) == "rectangular grid"``,
then the return object storing the probe positions, ``probe_positions``,
is not a two-column array. Otherwise, ``probe_positions`` is a
two-column array. See the description below of ``probe_positions`` for
more details.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
positions : `array_like` (`float`, shape=(``num_probe_positions``, ``2``)) | `array_like` (`float`, shape=(``Y_dim``, ``X_dim``, ``2``))
Let ``num_probe_positions`` be the number of probe positions. If
``force_2_col_shape==True`` or
``prismatique.load.scan_pattern_type(filename) != "rectangular grid"``,
then ``positions[i][0]`` and ``positions[i][1]`` are the :math:`x`- and
:math:`y`-components of the :math:`i^{\text{th}}` probe position in
units of Å, where ``i`` is an integer satisfying
``0<=i<num_probe_positions``.
If ``prismatique.load.scan_pattern_type(filename) ==
"rectangular grid"``, then the probe positions lie exactly on a
rectangular grid with ``X_dim`` positions along the :math:`x`-axis,
``Y_dim`` positions along the :math:`y`-axis. Let ``rx`` be the set of
possible :math:`x`-coordinates of the probe in ascending order, and
``ry`` be the set of possible :math:`y`-coordinates of the probe in
descending order, both in units of Å. In the case that
``force_2_col_shape==False`` and
``prismatique.load.scan_pattern_type(filename) == "rectangular grid"``,
``positions[ry_idx][rx_idx][0]=rx[rx_idx]`` and
``positions[ry_idx][rx_idx][1]=ry[ry_idx]``, where ``rx_idx`` and
``ry_idx`` are non-negative integers satisfying ``rx_idx<X_dim`` and
``ry_idx<Y_dim`` respectively.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
positions = _probe_positions(**kwargs)
return positions
def _probe_positions(filename, force_2_col_shape):
current_func_name = "_probe_positions"
try:
sim_params = \
prismatique.stem.sim.Params.load(filename)
sim_params_core_attrs = \
sim_params.get_core_attrs(deep_copy=False)
stem_system_model_params = \
sim_params_core_attrs["stem_system_model_params"]
stem_system_model_params_core_attrs = \
stem_system_model_params.get_core_attrs(deep_copy=False)
sample_specification = \
stem_system_model_params_core_attrs["sample_specification"]
scan_config = \
stem_system_model_params_core_attrs["scan_config"]
kwargs = \
{"sample_specification": sample_specification,
"scan_config": scan_config,
"filename": None}
positions = \
prismatique.scan.generate_probe_positions(**kwargs)
except:
try:
path_in_file = "/metadata/probe_positions"
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
positions = dataset[()]
dataset.file.close()
kwargs = {"obj": positions, "obj_name": "positions"}
czekitout.check.if_real_two_column_numpy_matrix(**kwargs)
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename,
current_func_name[1:])
raise IOError(err_msg)
if not force_2_col_shape:
if scan_pattern_type(filename) == "rectangular grid":
X_dim, Y_dim = grid_dims_in_units_of_probe_shifts(filename)
positions = np.reshape(positions, (X_dim, Y_dim, 2))
positions = np.transpose(positions, axes=[1, 0, 2])
positions = positions[::-1, :, :]
return positions
[docs]def output_layer_depths(filename,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given STEM simulation output file, load the output layer depths
stored or specified therein.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the output layer
depths. Any non-temporary file generated by the function
:func:`prismatique.stem.sim.run` that does not store potential slice or
S-matrix data, is a valid file. In other words, any file generated by a
STEM simulation with originally the basename
``"stem_sim_intensity_output.h5"``, ``"stem_sim_params.json"``, or a
basename of the form
``"stem_sim_wavefunction_output_of_subset_"+str(i)+".h5"``, where ``i``
is a nonnegative integer.
See the documentation for the class
:class:`prismatique.stem.output.Params` for a discussion on the layout
and structure of ``prismatique`` STEM simulation output files.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
layer_depths : `array_like` (`float`, ndim=1)
The output layer depths, in units of Å, in ascending order. Note that
for STEM simulations that use the PRISM algorithm, ``layer_depths`` is a
single-element array, where the single element is the the
:math:`z`-dimension of the sample's supercell in units of Å [see the
documentation for the class :class:`prismatique.discretization.Params`
for a discussion on the sample's supercell].
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
layer_depths = _output_layer_depths(**kwargs)
return layer_depths
def _output_layer_depths(filename):
current_func_name = "_output_layer_depths"
try:
sim_params = \
prismatique.stem.sim.Params.load(filename)
sim_params_core_attrs = \
sim_params.get_core_attrs(deep_copy=False)
stem_system_model_params = \
sim_params_core_attrs["stem_system_model_params"]
stem_system_model_params_core_attrs = \
stem_system_model_params.get_core_attrs(deep_copy=False)
output_params = \
sim_params_core_attrs["output_params"]
output_params_core_attrs = \
output_params.get_core_attrs(deep_copy=False)
kwargs = \
{"sample_specification": \
stem_system_model_params_core_attrs["sample_specification"],
"alg_specific_params": \
output_params_core_attrs["alg_specific_params"]}
layer_depths = \
prismatique.stem.output.layer_depths(**kwargs)
except:
try:
path_in_file = "/metadata/output_layer_depths"
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
layer_depths = dataset[()]
dataset.file.close()
kwargs = {"obj": layer_depths, "obj_name": "layer_depths"}
czekitout.check.if_real_numpy_array_1d(**kwargs)
layer_depths = tuple(layer_depths.tolist())
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename,
current_func_name[1:])
raise IOError(err_msg)
return layer_depths
[docs]def defocii(filename,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given ``prismatique`` output file, load the beam defocii stored
or specified therein that were used in the simulation.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the beam
defocii. Any non-temporary file generated by the function
:func:`prismatique.stem.sim.run` or :func:`prismatique.hrtem.sim.run`,
with originally the basename ``"stem_sim_params.json"``,
``"hrtem_sim_params.json"``, a basename of the form
``"stem_sim_wavefunction_output_of_subset_"+str(i)+".h5"``, or
``"hrtem_sim_wavefunction_output_of_subset_"+str(i)+".h5"``, where ``i``
is a nonnegative integer.
See the documentation for the classes
:class:`prismatique.stem.output.Params` and
:class:`prismatique.hrtem.output.Params` for discussions on the layout
and structure of ``prismatique`` STEM and HRTEM simulation output files
respectively.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
beam_defocii : `array_like` (`float`, ndim=1)
The beam defocii, in units of Å, in ascending order.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
beam_defocii = _defocii(**kwargs)
return beam_defocii
def _defocii(filename):
current_func_name = "_defocii"
try:
sim_params = prismatique.stem.sim.Params.load(filename)
beam_defocii = prismatique.stem.sim._defocii(sim_params)
except:
try:
sim_params = prismatique.hrtem.sim.Params.load(filename)
beam_defocii = prismatique.hrtem.sim._defocii(sim_params)
except:
try:
path_in_file = "/metadata/defocii"
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
beam_defocii = dataset[()]
dataset.file.close()
kwargs = {"obj": beam_defocii, "obj_name": "beam_defocii"}
czekitout.check.if_real_numpy_array_1d(**kwargs)
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename,
current_func_name[1:])
raise IOError(err_msg)
beam_defocii = tuple(beam_defocii.tolist())
return beam_defocii
[docs]def num_slices(filename,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given ``prismatique`` output file, load the number of slices used
to partition the sample, stored or specified therein.
See the documentation for the class
:class:`prismatique.discretization.Params` for a discussion on this slicing
scheme.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing or specifying the
number of slices used to partition the sample. Any non-temporary file
generated by the function
:func:`prismatique.sample.generate_potential_slices`,
:func:`prismatique.stem.sim.run`, or :func:`prismatique.hrtem.sim.run`
with originally the basename ``"sample_model_params.json"``, or a
basename of the form ``"potential_slices_of_subset_"+str(i)+".h5"`` is
valid, where ``i`` is nonnegative integer.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
num_sample_supercell_slices : `int`
The number of slices :math:`N_{\text{slices}}` used to partition the
sample.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
num_sample_supercell_slices = _num_slices(**kwargs)
return num_sample_supercell_slices
def _num_slices(filename):
current_func_name = "_num_slices"
try:
with open(filename, "r") as file_obj:
serializable_rep = json.load(file_obj)
de_pre_serialize_sample_specification = \
prismatique.stem.system._de_pre_serialize_sample_specification
sample_specification = \
de_pre_serialize_sample_specification(serializable_rep)
num_sample_supercell_slices = \
prismatique.sample.num_slices(sample_specification)
except:
try:
kwargs = \
{"filenames": (filename,)}
sample_specification = \
prismatique.sample.PotentialSliceSubsetIDs(**kwargs)
num_sample_supercell_slices = \
prismatique.sample.num_slices(sample_specification)
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename,
current_func_name[1:])
raise IOError(err_msg)
return num_sample_supercell_slices
[docs]def num_frozen_phonon_configs_in_subset(
filename,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given ``prismatique`` output file that stores data related to a
frozen phonon configuration subset, load the number of frozen phonon
configurations making up the subset.
See the documentation for the class :class:`prismatique.thermal.Params` for
a discussion on frozen phonon configurations and subsets thereof.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing ``prismatique``
output related to the frozen phonon configuration subset of interest.
Any non-temporary file generated by the function
:func:`prismatique.sample.generate_potential_slices`,
:func:`prismatique.sample.generate_S_matrices`,
:func:`prismatique.stem.sim.run`, or :func:`prismatique.hrtem.sim.run`
with originally a basename of the form
``"potential_slices_of_subset_"+str(i)+".h5"``,
``"S_matrices_of_subset_"+str(i)+".h5"``,
``"stem_sim_wavefunction_output_of_subset_"+str(i)+".h5"``, or
``"hrtem_sim_wavefunction_output_of_subset_"+str(i)+".h5"`` is valid,
where ``i`` is a nonnegative integer.
See the documentation for the classes
:class:`prismatique.stem.output.Params` and
:class:`prismatique.hrtem.output.Params` for discussions on the layout
and structure of ``prismatique`` STEM and HRTEM simulation output files
respectively.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
num_configs_in_subset : `int`
The number of frozen phonon configurations in the subset specified by
the output file.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
num_configs_in_subset = _num_frozen_phonon_configs_in_subset(**kwargs)
return num_configs_in_subset
def _num_frozen_phonon_configs_in_subset(filename):
current_func_name = "_num_frozen_phonon_configs_in_subset"
try:
path_in_file = "/data"
group_id = h5pywrappers.obj.ID(filename, path_in_file)
group = h5pywrappers.group.load(group_id, read_only=False)
key_1 = "4D_STEM/complex_valued_DPs"
key_2 = "image_wavefunctions"
if key_1 in group:
num_configs_in_subset = group[key_1].shape[1]
elif key_2 in group:
num_configs_in_subset = group[key_2].shape[0]
else:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename)
raise IOError(err_msg)
except:
kwargs = {"filenames": (filename,)}
func_alias = prismatique.sample.num_frozen_phonon_configs_in_subset
try:
sample_specification = \
prismatique.sample.PotentialSliceSubsetIDs(**kwargs)
num_configs_in_subset = \
func_alias(sample_specification, subset_idx=0)
except:
try:
sample_specification = \
prismatique.sample.SMatrixSubsetIDs(**kwargs)
num_configs_in_subset = \
func_alias(sample_specification, subset_idx=0)
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_2"]
err_msg = unformatted_err_msg.format(filename,
current_func_name[1:])
raise IOError(err_msg)
return num_configs_in_subset
[docs]def cbed_k_x_coords(filename,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given STEM simulation output file that stores CBED patterns, load
the :math:`k_x`-coordinates of said CBED patterns.
See also the documentation for the class :class:`prismatique.cbed.Params`
for a discussion on CBED patterns.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the CBED patterns
of interest. Any non-temporary file generated by the function
:func:`prismatique.stem.sim.run` with originally the basename
``"stem_sim_intensity_output.h5"``, or a basename of the form
``"stem_sim_wavefunction_output_of_subset_"+str(i)+".h5"`` is valid,
where ``i`` is a nonnegative integer. Note that CBED patterns stored in
the former are postprocessed, whereas those stored in the latter are
not, hence the :math:`k_x`-coordinates of the former set of CBED
patterns are generally different than the latter set.
See the documentation for the class
:class:`prismatique.stem.output.Params` for a discussion on the layout
and structuring of ``prismatique`` STEM simulation output files. See
also the documentation for the class :class:`prismatique.cbed.Params`
for a discussion on postprocessing CBED patterns.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
k_x : `array_like` (`float`, ndim=1)
The set of :math:`k_x`-coordinates, in ascending order, and in units of
1/Å.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
k_x = _cbed_k_x_coords(**kwargs)
return k_x
def _cbed_k_x_coords(filename):
current_func_name = "_cbed_k_x_coords"
try:
path_in_file = "/metadata/k_x"
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
k_x = dataset[()]
dataset.file.close()
kwargs = {"obj": k_x, "obj_name": "k_x"}
czekitout.check.if_real_numpy_array_1d(**kwargs)
k_x = tuple(k_x.tolist())
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename, current_func_name[1:])
raise IOError(err_msg)
return k_x
[docs]def cbed_k_y_coords(filename,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given STEM simulation output file that stores CBED patterns, load
the :math:`k_y`-coordinates of said CBED patterns.
See also the documentation for the class :class:`prismatique.cbed.Params`
for a discussion on CBED patterns.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the CBED patterns
of interest. Any non-temporary file generated by the function
:func:`prismatique.stem.sim.run` with originally the basename
``"stem_sim_intensity_output.h5"``, or a basename of the form
``"stem_sim_wavefunction_output_of_subset_"+str(i)+".h5"`` is valid,
where ``i`` is a nonnegative integer. Note that CBED patterns stored in
the former are postprocessed, whereas those stored in the latter are
not, hence the :math:`k_x`-coordinates of the former set of CBED
patterns are generally different than the latter set.
See the documentation for the class
:class:`prismatique.stem.output.Params` for a discussion on the layout
and structuring of ``prismatique`` STEM simulation output files. See
also the documentation for the class :class:`prismatique.cbed.Params`
for a discussion on postprocessing CBED patterns.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
k_y : `array_like` (`float`, ndim=1)
The set of :math:`k_y`-coordinates, in descending order, and in units of
1/Å.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
k_y = _cbed_k_y_coords(**kwargs)
return k_y
def _cbed_k_y_coords(filename):
current_func_name = "_cbed_k_y_coords"
try:
path_in_file = "/metadata/k_y"
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
k_y = dataset[()]
dataset.file.close()
kwargs = {"obj": k_y, "obj_name": "k_y"}
czekitout.check.if_real_numpy_array_1d(**kwargs)
k_y = tuple(k_y.tolist())
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename, current_func_name[1:])
raise IOError(err_msg)
return k_y
[docs]def k_xy_coords_of_3d_stem_output(filename,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given STEM simulation output file that stores 3D-STEM integrated
CBED intensity patterns, load the radial scattering momenta coordinates
:math:`k_{xy}` of said integrated CBED intensity patterns.
See the documentation for the class :class:`prismatique.stem.output.Params`
for a discussion on 3D-STEM integrated CBED intensity patterns.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the integrated CBED
intensity patterns of interest. Any non-temporary file generated by the
function :func:`prismatique.stem.sim.run` with originally the basename
``"stem_sim_intensity_output.h5"`` is valid.
See the documentation for the class
:class:`prismatique.stem.output.Params` for a discussion on the layout
and structuring of ``prismatique`` STEM simulation output files.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
k_xy : `array_like` (`float`, ndim=1)
The set of :math:`k_xy`-coordinates, in ascending order, and in units of
1/Å.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
k_xy = _k_xy_coords_of_3d_stem_output(**kwargs)
return k_xy
def _k_xy_coords_of_3d_stem_output(filename):
current_func_name = "_k_xy_coords_of_3d_stem_output"
try:
path_in_file = "/metadata/k_xy"
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
k_xy = dataset[()]
dataset.file.close()
kwargs = {"obj": k_xy, "obj_name": "k_xy"}
czekitout.check.if_real_numpy_array_1d(**kwargs)
k_xy = tuple(k_xy.tolist())
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename, current_func_name[1:])
raise IOError(err_msg)
return k_xy
[docs]def integration_limits_of_2d_stem_output(
filename,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given STEM simulation output file that stores 2D-STEM integrated
CBED intensity patterns, load the 2D-STEM integration limits.
See the documentation for the class :class:`prismatique.stem.output.Params`
for a discussion on 2D-STEM integrated CBED intensity patterns, i.e. STEM
intensity images.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the integrated CBED
intensity patterns of interest. Any non-temporary file generated by the
function :func:`prismatique.stem.sim.run` with originally the basename
``"stem_sim_intensity_output.h5"`` is valid.
See the documentation for the class
:class:`prismatique.stem.output.Params` for a discussion on the layout
and structuring of ``prismatique`` STEM simulation output files.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
integration_limits : `array_like` (`float`, shape=(``2``,))
``integration_limits[0]`` is the lower integration limit in mrads;
``integration_limits[1]`` is the upper integration limit in mrads.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
integration_limits = _integration_limits_of_2d_stem_output(**kwargs)
return integration_limits
def _integration_limits_of_2d_stem_output(filename):
current_func_name = "_integration_limits_of_2d_stem_output"
try:
attr_names = ("lower integration limit in mrads",
"upper integration limit in mrads")
path_in_file = "/data/2D_STEM/integrated_intensities"
obj_id = h5pywrappers.obj.ID(filename, path_in_file)
integration_limits = tuple()
for attr_name in attr_names:
attr_id = h5pywrappers.attr.ID(obj_id, attr_name)
integration_limits += (h5pywrappers.attr.load(attr_id),)
func_alias = czekitout.convert.to_tuple_of_nonnegative_floats
kwargs = {"obj": integration_limits, "obj_name": "integration_limits"}
integration_limits = func_alias(**kwargs)
if integration_limits[1] < integration_limits[0]:
err_msg = globals()[current_func_name+"_err_msg_1"]
raise ValueError(err_msg)
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_2"]
err_msg = unformatted_err_msg.format(filename, current_func_name[1:])
raise IOError(err_msg)
return integration_limits
[docs]def S_matrix_k_xy_vectors(filename,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given output file generated by a STEM simulation using the PRISM
algorithm, load the :math:`\mathbf{k}_{xy}`-momentum vectors of the plane
waves used to calculate the :math:`S`-matrices of said STEM simulation.
See the documentation for the subpackage :mod:`prismatique.stem` for a
discussion on :math:`S`-matrices, :math:`S_{m_{x},m_{y}}\left(x,y\right)`,
and the PRISM algorithm. As discussed therein, each pair :math:`\left(m_x,
m_y\right)` corresponds to a different :math:`\mathbf{k}_{xy}`-momentum
vector. In ``prismatic``, each pair :math:`\left(m_x, m_y\right)` is
essentially mapped to a unique integer :math:`i`.
Parameters
----------
filename : `str`
The relative or absolute path to the file specifying the
:math:`\mathbf{k}_{xy}`-momentum vectors. Any non-temporary file
generated by the function :func:`prismatique.stem.sim.run` using the
PRISM algorithm, with originally the basename ``"stem_sim_params.json"``
is valid.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
k_xy_vectors : `array_like` (`float`, shape=(``num_vectors``, ``2``))
If we let ``num_vectors`` be the number of
:math:`\mathbf{k}_{xy}`-momentum vectors, then ``k_xy_vectors[i][0]``
and ``k_xy_vectors[i][1]`` are the :math:`x`- and :math:`y`-components
of the ``i`` th :math:`\mathbf{k}_{xy}`-momentum vector in units of 1/Å,
where ``0<=i<num_vectors``.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
k_xy_vectors = _S_matrix_k_xy_vectors(**kwargs)
return k_xy_vectors
def _S_matrix_k_xy_vectors(filename):
current_func_name = "_S_matrix_k_xy_vectors"
try:
sim_params = \
prismatique.stem.sim.Params.load(filename)
sim_params_core_attrs = \
sim_params.get_core_attrs(deep_copy=False)
stem_system_model_params = \
sim_params_core_attrs["stem_system_model_params"]
stem_system_model_params_core_attrs = \
stem_system_model_params.get_core_attrs(deep_copy=False)
output_params = \
sim_params_core_attrs["output_params"]
output_params_core_attrs = \
output_params.get_core_attrs(deep_copy=False)
sample_specification = \
stem_system_model_params_core_attrs["sample_specification"]
probe_model_params = \
stem_system_model_params_core_attrs["probe_model_params"]
alg_specific_output_params = \
output_params_core_attrs["alg_specific_params"]
alg_specific_output_params_core_attrs = \
alg_specific_output_params.get_core_attrs(deep_copy=False)
if "z_start_output" in alg_specific_output_params_core_attrs:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename)
raise IOError(err_msg)
num_sample_supercell_slices = \
prismatique.sample.num_slices(sample_specification)
kwargs = {"sample_specification": sample_specification,
"probe_model_params": probe_model_params}
k_xy_vectors = prismatique.sample.S_matrix_k_xy_vectors(**kwargs)
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_2"]
err_msg = unformatted_err_msg.format(filename, current_func_name[1:])
raise IOError(err_msg)
return k_xy_vectors
[docs]def hrtem_beam_tilts(filename,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given HRTEM simulation output file, load the beam tilts stored
or specified therein that were used in the simulation.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the beam tilts. Any
non-temporary file generated by the function
:func:`prismatique.hrtem.sim.run`, with originally the basename
``"hrtem_sim_params.json"``, or a basename of the form
``"hrtem_sim_wavefunction_output_of_subset_"+str(i)+".h5"``, where ``i``
is a nonnegative integer.
See the documentation for the class
:class:`prismatique.hrtem.output.Params` for a discussion on the layout
and structure of ``prismatique`` HRTEM simulation output files
respectively.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
beam_tilts : `array_like` (`float`, shape=(``num_tilts``, ``2``))
If we let ``num_tilts`` be the number of beam tilts, then
``beam_tilts[i][0]`` and ``beam_tilts[i][1]`` are the :math:`x`- and
:math:`y`-components of the ``i`` th beam tilt in units of mrads, where
``0<=i<num_vectors``.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
beam_tilts = _hrtem_beam_tilts(**kwargs)
return beam_tilts
def _hrtem_beam_tilts(filename):
current_func_name = "_hrtem_beam_tilts"
try:
sim_params = prismatique.hrtem.sim.Params.load(filename)
beam_tilts = prismatique.hrtem.sim._tilt_series(sim_params)
except:
try:
path_in_file = "/metadata/tilts"
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
beam_tilts = dataset[()]
dataset.file.close()
kwargs = {"obj": beam_tilts, "obj_name": "beam_tilts"}
czekitout.check.if_real_two_column_numpy_matrix(**kwargs)
beam_tilts = tuple(tuple(beam_tilt) for beam_tilt in beam_tilts)
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename,
current_func_name[1:])
raise IOError(err_msg)
return beam_tilts
[docs]def hrtem_image_x_coords(filename,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given HRTEM simulation output file that stores HRTEM images, load
the :math:`x`-coordinates of said HRTEM images.
See also the documentation for the class
:class:`prismatique.hrtem.image.Params` for a discussion on HRTEM images.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the HRTEM images of
interest. Any non-temporary file generated by the function
:func:`prismatique.hrtem.sim.run` with originally the basename
``"hrtem_sim_intensity_output.h5"``, or a basename of the form
``"hrtem_sim_wavefunction_output_of_subset_"+str(i)+".h5"`` is valid,
where ``i`` is a nonnegative integer. Note that HRTEM images stored in
the former are postprocessed, whereas those stored in the latter are
not, hence the :math:`x`-coordinates of the former set of HRTEM images
are generally different than the latter set.
See the documentation for the class
:class:`prismatique.hrtem.output.Params` for a discussion on the layout
and structuring of ``prismatique`` HRTEM simulation output files. See
also the documentation for the class
:class:`prismatique.hrtem.image.Params` for a discussion on
postprocessing HRTEM images.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
r_x : `array_like` (`float`, ndim=1)
The set of :math:`x`-coordinates, in ascending order, and in units of Å.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
r_x = _hrtem_image_x_coords(**kwargs)
return r_x
def _hrtem_image_x_coords(filename):
current_func_name = "_hrtem_image_x_coords"
try:
path_in_file = "/metadata/r_x"
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
r_x = dataset[()]
dataset.file.close()
kwargs = {"obj": r_x, "obj_name": "r_x"}
czekitout.check.if_real_numpy_array_1d(**kwargs)
r_x = tuple(r_x.tolist())
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename, current_func_name[1:])
raise IOError(err_msg)
return r_x
[docs]def hrtem_image_y_coords(filename,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given HRTEM simulation output file that stores HRTEM images, load
the :math:`y`-coordinates of said HRTEM images.
See also the documentation for the class
:class:`prismatique.hrtem.image.Params` for a discussion on HRTEM images.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the HRTEM images of
interest. Any non-temporary file generated by the function
:func:`prismatique.hrtem.sim.run` with originally the basename
``"hrtem_sim_intensity_output.h5"``, or a basename of the form
``"hrtem_sim_wavefunction_output_of_subset_"+str(i)+".h5"`` is valid,
where ``i`` is a nonnegative integer. Note that HRTEM images stored in
the former are postprocessed, whereas those stored in the latter are
not, hence the :math:`y`-coordinates of the former set of HRTEM images
are generally different than the latter set.
See the documentation for the class
:class:`prismatique.hrtem.output.Params` for a discussion on the layout
and structuring of ``prismatique`` HRTEM simulation output files. See
also the documentation for the class
:class:`prismatique.hrtem.image.Params` for a discussion on
postprocessing HRTEM images.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
r_y : `array_like` (`float`, ndim=1)
The set of :math:`y`-coordinates, in ascending order, and in units of Å.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
r_y = _hrtem_image_y_coords(**kwargs)
return r_y
def _hrtem_image_y_coords(filename):
current_func_name = "_hrtem_image_y_coords"
try:
path_in_file = "/metadata/r_y"
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
r_y = dataset[()]
dataset.file.close()
kwargs = {"obj": r_y, "obj_name": "r_y"}
czekitout.check.if_real_numpy_array_1d(**kwargs)
r_y = tuple(r_y.tolist())
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename, current_func_name[1:])
raise IOError(err_msg)
return r_y
def _check_and_convert_multi_dim_slice(params):
obj_name = "multi_dim_slice"
module_alias = h5pywrappers.datasubset
cls_alias = module_alias.ID
func_alias = cls_alias.get_validation_and_conversion_funcs()[obj_name]
multi_dim_slice = func_alias(params)
return multi_dim_slice
def _check_and_convert_num_superslices(params):
obj_name = "num_superslices"
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
num_superslices = czekitout.convert.to_nonnegative_int(**kwargs)
return num_superslices
def _check_and_convert_average_thermally(params):
obj_name = "average_thermally"
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
average_thermally = czekitout.convert.to_bool(**kwargs)
return average_thermally
def _check_and_convert_average_laterally_in_space(params):
obj_name = "average_laterally_in_space"
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
average_laterally_in_space = czekitout.convert.to_bool(**kwargs)
return average_laterally_in_space
_default_multi_dim_slice = None
_default_num_superslices = 0
_default_average_thermally = False
_default_average_laterally_in_space = False
[docs]def potential_slices(filename,
multi_dim_slice=\
_default_multi_dim_slice,
num_superslices=\
_default_num_superslices,
average_thermally=\
_default_average_thermally,
average_laterally_in_space=\
_default_average_laterally_in_space,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given ``prismatique`` output file that stores potential slices of
a sample, load a specified subcollection of said potential slices into a
``hyperspy`` signal.
See also the documentation for the class
:class:`prismatique.discretization.Params` for a discussion on potential
slices.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the potential
slices of interest. Any non-temporary file generated by the function
:func:`prismatique.sample.generate_potential_slices`,
:func:`prismatique.stem.sim.run`, or :func:`prismatique.hrtem.sim.run`,
with originally a basename of the form
``"potential_slices_of_subset_"+str(i)+".h5"`` is valid, where ``i`` is
a nonnegative integer.
multi_dim_slice : `tuple` (`int` | `slice` | `list` (`int`)) | `None`, optional
The "multidimensional slice object", which specifies the subcollection
of potential slices to load from file. We define a multi-dimensional
slice object as a `tuple` of items which contains at most one item being
a `list` of integers, and the remaining items being `slice` and/or `int`
objects.
If ``multi_dim_slice`` is a `tuple` of length 2, then
``multi_dim_slice[0]`` specifies a set of atomic configuration indices,
where each atomic configuration index corresponds to a frozen phonon
configuration; and ``multi_dim_slice[1]`` specifies a set of sample
slice indices, where each sample slice index corresponds to a different
sample slice at a different :math:`z`-coordinate. In this case, the
current Python function will load the potential slices of the frozen
phonon configurations specified by ``multi_dim_slice[0]``, at the
:math:`z`-coordinates specified by ``multi_dim_slice[1]``. Note that a
sample slice index :math:`n` corresponds to a sample slice at the
:math:`z`-coordinate :math:`z_{n}`, where :math:`z_{n}` is given by
Eq. :eq:`z_n_in_potential_params`.
Otherwise, if ``multi_dim_slice`` is set to `None`, then all the
potential slices stored in the file are loaded.
num_superslices : `int`, optional
The number of potential superslices to form from the potential slices to
be loaded from file, per frozen phonon configuration. We define a
potential superslice as the sum of a group of potential slices. Let
``N`` be the total number of potential slices stored in the file per
frozen phonon configuration, ``M`` be the number of slices to be loaded
per frozen phonon configuration, ``n=int(np.ceil(M /
max(num_superslices, 1)))``, and ``single_dim_slice=multi_dim_slice[1]
if multi_dim_slice is not None else slice(None)``. If
``num_superslices`` is a positive `int`, then for each integer ``i``
satisfying ``0<=i<num_superslices-1``, the ``i`` th superslice is
calculated by summing the potential slices with the corresponding
indices ``np.arange(N)[single_dim_slice][i*n:(i+1)*n]``, and for
``i==num_superslices-1``, the ``i`` th superslice is calculated by
summing the potential slices with the corresponding indices
``np.arange(N)[single_dim_slice][i*n:M]``. Otherwise, if
``num_superslices==0``, then no potential superslices are formed.
average_thermally : `bool`, optional
If ``average_thermally`` is set to ``True``, then the potential slices
and superslices are averaged over the selected frozen phonon
configurations. Otherwise, the potential slices are not averaged over
the selected frozen phonon configurations.
average_laterally_in_space : `bool`, optional
If ``average_laterally_in_space`` is set to ``True``, then the potential
slices and superslices are averaged laterally in space. Otherwise, the
potential slices are not averaged laterally in space.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
potential_slice_signal : :class:`hyperspy.signal.BaseSignal` | :class:`hyperspy._signals.signal2d.Signal2D`
The subcollection of potential slices, stored in an instance of the
:class:`hyperspy._signals.signal2d.Signal2D` class if
``average_laterally_in_space`` is set to ``False``, otherwise it is
stored in an instance of the :class:`hyperspy.signal.BaseSignal`. See
the documentation and/or reference guide for the :mod:`hyperspy` package
for details on how to use instances of the
:class:`hyperspy._signals.signal2d.Signal2D` and
:class:`hyperspy.signal.BaseSignal` classes.
navigational_to_original_indices_map : `dict`
A dictionary that maps the navigational indices of the hyperspy signal
``potential_slice_signal`` to the original indices specified by
``multi_dim_slice``. For example, if the original atomic configuration
indices map to a set of corresponding navigational indices, then
``navigational_to_original_indices_map["atomic_config_indices"][i]``
yields the atomic configuration index specified in the expression
``single_dim_slice=multi_dim_slice[0] if multi_dim_slice is not None
else slice(None)`` that corresponds to the ``i`` th atomic configuration
index in the nagivation index space of ``potential_slice_signal``, where
``i`` is a nonnegative integer smaller than the total number of atomic
configuration indices specified in ``single_dim_slice``.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
result = _potential_slices(**kwargs)
potential_slice_signal, navigational_to_original_indices_map = result
return potential_slice_signal, navigational_to_original_indices_map
def _potential_slices(filename,
multi_dim_slice,
num_superslices,
average_thermally,
average_laterally_in_space):
current_func_params = locals()
kwargs = \
{**current_func_params,
"indices_map_keys": \
("atomic_config_indices", "slice_indices"),
"func_to_calc_sizes_of_navigational_space_axes": \
_sizes_of_navigational_space_axes_for_potential_slices,
"func_to_initialize_sample_specification_signal": \
_initialize_potential_slice_signal}
potential_slice_signal, navigational_to_original_indices_map = \
_sample_specification_signal_and_indices_map(**kwargs)
return potential_slice_signal, navigational_to_original_indices_map
def _sizes_of_navigational_space_axes_for_potential_slices(filename):
current_func_name = "_sizes_of_navigational_space_axes_for_potential_slices"
try:
cls_alias = prismatique.sample.PotentialSliceSubsetIDs
kwargs = {"filenames": (filename,)}
sample_specification = cls_alias(**kwargs)
func_alias = prismatique.sample._num_frozen_phonon_configs_in_subset
kwargs = {"sample_specification": sample_specification, "subset_idx": 0}
num_frozen_phonon_configs_in_subset = func_alias(**kwargs)
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename, current_func_name[38:])
raise IOError(err_msg)
kwargs = {"sample_specification": sample_specification}
num_sample_supercell_slices = prismatique.sample.num_slices(**kwargs)
sizes_of_navigational_space_axes = (num_frozen_phonon_configs_in_subset,
num_sample_supercell_slices)
return sizes_of_navigational_space_axes
def _initialize_potential_slice_signal(filename,
multi_dim_slice,
indices_map,
indices_map_keys,
extra_kwargs):
current_func_params = locals()
average_thermally = extra_kwargs["average_thermally"]
average_laterally_in_space = extra_kwargs["average_laterally_in_space"]
num_superslices = extra_kwargs["num_superslices"]
kwargs = {"filenames": (filename,)}
sample_specification = prismatique.sample.PotentialSliceSubsetIDs(**kwargs)
N_x, N_y = \
prismatique.sample._supercell_xy_dims_in_pixels(sample_specification)
Delta_x, Delta_y = \
prismatique.sample._supercell_lateral_pixel_size(sample_specification)
signal_title = \
((average_thermally*"Thermally ")
+ ((average_thermally and average_laterally_in_space)*"and ")
+ (average_laterally_in_space*"Spatially ")
+ ((average_thermally or average_laterally_in_space)*"Averaged ")
+ "Potential "
+ ((num_superslices>0)*"Superslice")
+ ((num_superslices==0)*"Slice"))
signal_space_sizes = (N_x, N_y)*(not average_laterally_in_space)
signal_space_scales = (Delta_x, -Delta_y)*(not average_laterally_in_space)
signal_space_offsets = ((-((N_x-1) * Delta_x) / 2, ((N_y-1) * Delta_y) / 2)
* (not average_laterally_in_space))
signal_space_units = ("Å", "Å")*(not average_laterally_in_space)
kwargs = {"signal_space_sizes": signal_space_sizes,
"signal_space_scales": signal_space_scales,
"signal_space_offsets": signal_space_offsets,
"signal_space_units": signal_space_units,
"signal_dtype": "float32",
"signal_title": signal_title,
"use_two_axes_to_map_probe_position_if_possible": False}
kwargs = {**kwargs, **current_func_params}
potential_slice_signal = _initialize_signal_for_generic_case(**kwargs)
return potential_slice_signal
def _initialize_signal_for_generic_case(
filename,
multi_dim_slice,
indices_map,
indices_map_keys,
signal_space_sizes,
signal_space_scales,
signal_space_offsets,
signal_space_units,
signal_dtype,
signal_title,
use_two_axes_to_map_probe_position_if_possible,
extra_kwargs):
current_func_params = locals()
kwargs = {key: current_func_params[key]
for key in current_func_params
if "signal" not in key}
navigational_space_dims = _navigational_space_dims(**kwargs)
navigational_space_rank = len(navigational_space_dims)
signal_data_shape = navigational_space_dims + signal_space_sizes[::-1]
if len(signal_data_shape) == 0:
signal_data_shape = [1]
signal_data = np.zeros(signal_data_shape, dtype=signal_dtype)
del kwargs["use_two_axes_to_map_probe_position_if_possible"]
kwargs["signal_title"] = signal_title
signal_metadata = _signal_metadata(**kwargs)
kwargs = current_func_params.copy()
del kwargs["signal_dtype"]
signal_axes = _signal_axes(**kwargs)
kwargs = {"signal_data": signal_data,
"signal_metadata": signal_metadata,
"signal_axes": signal_axes,
"navigational_space_rank": navigational_space_rank}
signal = _build_signal(**kwargs)
return signal
def _navigational_space_dims(filename,
multi_dim_slice,
indices_map,
indices_map_keys,
use_two_axes_to_map_probe_position_if_possible,
extra_kwargs):
navigational_space_dims = tuple()
zip_obj = zip(indices_map_keys, multi_dim_slice)
for indices_map_key, single_dim_slice in zip_obj:
M = len(indices_map[indices_map_key])
if indices_map_key == "probe_indices":
scan_pattern_type = prismatique.load.scan_pattern_type(filename)
if ((scan_pattern_type == "rectangular grid")
and (single_dim_slice == slice(None))
and (use_two_axes_to_map_probe_position_if_possible)):
grid_dims = grid_dims_in_units_of_probe_shifts(filename)
navigational_space_dims += (grid_dims[1],)
navigational_space_dims += (grid_dims[0],)
elif not isinstance(single_dim_slice, int):
navigational_space_dims += (M,)
elif indices_map_key == "slice_indices":
if extra_kwargs.get("num_superslices", 0) > 0:
navigational_space_dims += (extra_kwargs["num_superslices"],)
elif not isinstance(single_dim_slice, int):
navigational_space_dims += (M,)
elif indices_map_key == "atomic_config_indices":
if ((not extra_kwargs.get("average_thermally", False))
and (not isinstance(single_dim_slice, int))):
navigational_space_dims += (M,)
elif not isinstance(single_dim_slice, int):
navigational_space_dims += (M,)
return navigational_space_dims
def _signal_metadata(filename,
multi_dim_slice,
indices_map,
indices_map_keys,
signal_title,
extra_kwargs):
prismatique_metadata = _prismatique_metadata(filename,
multi_dim_slice,
indices_map,
indices_map_keys,
extra_kwargs)
if signal_title == "Center of Mass Momentum":
pixel_value_units = "1/Å"
elif "Potential" in signal_title:
pixel_value_units = "Å*V"
else:
pixel_value_units = "dimensionless"
signal_metadata = {"General": {"title": signal_title},
"Signal": {"pixel value units": pixel_value_units},
"Prismatique": prismatique_metadata}
return signal_metadata
def _prismatique_metadata(filename,
multi_dim_slice,
indices_map,
indices_map_keys,
extra_kwargs):
candidate_prismatique_metadata_keys = \
_candidate_prismatique_metadata_keys(indices_map_keys)
zip_obj = zip(indices_map_keys,
multi_dim_slice,
candidate_prismatique_metadata_keys)
func_alias = \
_prismatique_metadata_resulting_from_single_dim_slices_of_length_one
kwargs = \
{"filename": filename, "zip_obj": zip_obj}
prismatique_metadata = \
func_alias(filename, zip_obj)
if "atomic_config_indices" in indices_map_keys:
if extra_kwargs.get("average_thermally", False):
single_dim_slice = ([multi_dim_slice[0]]
if isinstance(multi_dim_slice[0], int)
else multi_dim_slice[0])
num_configs_in_subset = \
num_frozen_phonon_configs_in_subset(filename)
original_atomic_configuration_indices = \
np.arange(num_configs_in_subset)[single_dim_slice]
prismatique_metadata["original atomic configuration indices"] = \
tuple(original_atomic_configuration_indices.tolist())
if "slice_indices" in indices_map_keys:
prismatique_metadata["slice thickness"] = _slice_thickness(filename)
num_superslices = extra_kwargs.get("num_superslices", 0)
if num_superslices > 0:
kwargs = \
{"filename": filename,
"indices_map": indices_map,
"num_superslices": num_superslices}
prismatique_metadata["original slice indices of superslices"] = \
_original_slice_indices_of_superslices(**kwargs)
if indices_map_keys == ("output_layer_indices",):
prismatique_metadata["integration limits"] = \
integration_limits_of_2d_stem_output(filename)
return prismatique_metadata
def _candidate_prismatique_metadata_keys(indices_map_keys):
candidate_prismatique_metadata_keys = \
tuple()
for indices_map_key in indices_map_keys:
indices_map_key_to_prismatique_metadata_key_map = \
{"output_layer_indices": "original output layer index",
"atomic_config_indices": "original atomic configuration index",
"defocus_indices": "original defocus index",
"probe_indices": "original probe index",
"vector_component_indices": "original vector component index",
"tilt_indices": "original tilt index",
"k_xy_indices": r"original $k_{xy}$ index",
"k_xy_vector_indices": r"original $\mathbf{k}_{xy}$ index",
"slice_indices": "original slice index"}
prismatique_metadata_key = \
indices_map_key_to_prismatique_metadata_key_map.get(indices_map_key)
candidate_prismatique_metadata_keys += \
(prismatique_metadata_key != None)*(prismatique_metadata_key,)
return candidate_prismatique_metadata_keys
def _prismatique_metadata_resulting_from_single_dim_slices_of_length_one(
filename, zip_obj):
prismatique_metadata = dict()
for triplet in zip_obj:
indices_map_key = triplet[0]
single_dim_slice = triplet[1]
candidate_prismatique_metadata_key = triplet[2]
if isinstance(single_dim_slice, int):
original_idx = \
single_dim_slice
prismatique_metadata[candidate_prismatique_metadata_key] = \
original_idx
if indices_map_key == "output_layer_indices":
extra_key = "output layer depth"
extra_val = output_layer_depths(filename)[single_dim_slice]
elif indices_map_key == "defocus_indices":
extra_key = "defocus"
extra_val = defocii(filename)[single_dim_slice]
elif indices_map_key == "probe_indices":
extra_key = "probe position"
position = probe_positions(filename)[single_dim_slice]
extra_val = tuple(position.tolist())
elif indices_map_key == "slice_indices":
extra_key = r"$z$-coordinate"
extra_val = single_dim_slice * _slice_thickness(filename)
elif indices_map_key == "tilt_indices":
extra_key = "beam tilt"
extra_val = hrtem_beam_tilts(filename)[single_dim_slice]
elif indices_map_key == "vector_component_indices":
extra_key = "vector component"
extra_val = (r"$x$", r"$y$")[single_dim_slice]
else:
extra_key = "dummy"
extra_val = None
prismatique_metadata[extra_key] = extra_val
prismatique_metadata.pop("dummy", None)
return prismatique_metadata
def _slice_thickness(filename):
kwargs = \
{"filenames": (filename,)}
sample_specification = \
prismatique.sample.PotentialSliceSubsetIDs(**kwargs)
kwargs = \
{"sample_specification": sample_specification}
slice_thickness = \
prismatique.sample._supercell_slice_thickness(sample_specification)
return slice_thickness
def _original_slice_indices_of_superslices(filename,
indices_map,
num_superslices):
slice_idx_subcollection = indices_map["slice_indices"]
original_slice_indices_of_superslices = tuple()
if num_superslices == 0:
original_slice_indices_of_superslices += (slice_idx_subcollection,)
else:
N = _sizes_of_navigational_space_axes_for_potential_slices(filename)
M = len(slice_idx_subcollection)
n = int(np.ceil(M / num_superslices))
for idx in range(num_superslices):
beg = idx*n
end = (idx+1)*n if (idx < num_superslices-1) else M
indices_of_superslice = tuple(slice_idx_subcollection[beg:end])
original_slice_indices_of_superslices += (indices_of_superslice,)
original_slice_indices_of_superslices = \
tuple(original_slice_indices_of_superslices)
return original_slice_indices_of_superslices
def _signal_axes(filename,
multi_dim_slice,
indices_map,
indices_map_keys,
signal_space_sizes,
signal_space_scales,
signal_space_offsets,
signal_space_units,
signal_title,
use_two_axes_to_map_probe_position_if_possible,
extra_kwargs):
current_func_params = locals()
kwargs = {"filename": \
filename,
"signal_space_units": \
signal_space_units,
"signal_title": \
signal_title,
"use_two_axes_to_map_probe_position_if_possible": \
use_two_axes_to_map_probe_position_if_possible}
axes_labels = _signal_space_axes_labels(**kwargs)
func_alias = _add_navigational_space_axes_properties_to_current_sets
kwargs = current_func_params.copy()
kwargs["axes_labels"] = axes_labels
del kwargs["signal_title"]
key_set_1 = tuple(kwargs.keys())
for key_1 in key_set_1:
if "signal_space" in key_1:
key_2 = key_1.split("_")[-1]
kwargs[key_2] = tuple(kwargs[key_1])
del kwargs[key_1]
axes_labels, sizes, scales, offsets, units = func_alias(**kwargs)
tol = 1e-10
if (len(signal_space_units) == 2) and (abs(scales[-2]+scales[-1]) < tol):
scales = scales[:-2] + (scales[-2], -scales[-2])
signal_axes = tuple()
for axis_idx in range(len(units)):
axis = hyperspy.axes.UniformDataAxis(size=sizes[axis_idx],
scale=scales[axis_idx],
offset=offsets[axis_idx])
axis.name = axes_labels[axis_idx]
axis.units = units[axis_idx]
signal_axes += (axis,)
return signal_axes
def _signal_space_axes_labels(
filename,
signal_space_units,
signal_title,
use_two_axes_to_map_probe_position_if_possible):
if signal_title in ("STEM Intensity Image", "Center of Mass Momentum"):
scan_pattern_type = prismatique.load.scan_pattern_type(filename)
if ((scan_pattern_type == "rectangular grid")
and (use_two_axes_to_map_probe_position_if_possible)):
signal_space_axes_labels = (r"probe $x$-coordinate",
r"probe $y$-coordinate")
return signal_space_axes_labels
if len(signal_space_units) == 0:
signal_space_axes_labels = tuple()
else:
if signal_space_units[0] == "dimensionless":
signal_space_axes_labels = ("original probe index",)
elif signal_space_units[0] == "Å":
signal_space_axes_labels = (r"$x$", r"$y$")
else:
signal_space_axes_labels = ((r"$k_{xy}$",)
if (len(signal_space_units) == 1)
else (r"$k_x$", r"$k_y$"))
return signal_space_axes_labels
def _add_navigational_space_axes_properties_to_current_sets(
indices_map_keys,
extra_kwargs,
multi_dim_slice,
filename,
use_two_axes_to_map_probe_position_if_possible,
indices_map,
axes_labels,
sizes,
scales,
offsets,
units,):
kwargs = \
{"indices_map_keys": \
indices_map_keys,
"extra_kwargs": \
extra_kwargs,
"multi_dim_slice": \
multi_dim_slice,
"filename": \
filename,
"use_two_axes_to_map_probe_position_if_possible": \
use_two_axes_to_map_probe_position_if_possible,
"indices_map": \
indices_map}
navigational_space_axes_properties = \
_navigational_space_axes_properties(**kwargs)
axes_labels = \
navigational_space_axes_properties["axes_labels"] + axes_labels
sizes = \
navigational_space_axes_properties["sizes"] + sizes
scales = \
navigational_space_axes_properties["scales"] + scales
offsets = \
navigational_space_axes_properties["offsets"] + offsets
units = \
navigational_space_axes_properties["units"] + units
return axes_labels, sizes, scales, offsets, units
def _navigational_space_axes_properties(
indices_map_keys,
extra_kwargs,
multi_dim_slice,
filename,
use_two_axes_to_map_probe_position_if_possible,
indices_map):
current_func_params = locals()
axes_property_names = \
("axes_labels", "sizes", "scales", "offsets", "units")
navigational_space_axes_properties = \
{key: tuple() for key in axes_property_names}
kwargs = \
{"indices_map_keys": indices_map_keys,
"extra_kwargs": extra_kwargs}
candidate_navigational_space_axes_labels = \
_candidate_navigational_space_axes_labels(**kwargs)
zip_obj = zip(indices_map_keys,
multi_dim_slice,
candidate_navigational_space_axes_labels)
for indices_map_key, single_dim_slice, candidate_axis_label in zip_obj:
if ((indices_map_key == "slice_indices")
and ((extra_kwargs.get("num_superslices", 0) == 1)
or ((extra_kwargs.get("num_superslices", 0) == 0)
and isinstance(single_dim_slice, int)))):
continue
elif (isinstance(single_dim_slice, int)
or ((indices_map_key == "atomic_config_indices")
and extra_kwargs.get("average_thermally", False))):
continue
func_alias = _subset_of_navigational_space_axes_properties
kwargs = current_func_params.copy()
kwargs["indices_map_key"] = indices_map_key
kwargs["single_dim_slice"] = single_dim_slice
kwargs["candidate_axis_label"] = candidate_axis_label
del kwargs["indices_map_keys"]
del kwargs["multi_dim_slice"]
subset_of_navigational_space_axes_properties = func_alias(**kwargs)
for key in navigational_space_axes_properties:
navigational_space_axes_properties[key] = \
(subset_of_navigational_space_axes_properties[key]
+ navigational_space_axes_properties[key])
return navigational_space_axes_properties
def _candidate_navigational_space_axes_labels(indices_map_keys, extra_kwargs):
candidate_prismatique_metadata_keys = \
_candidate_prismatique_metadata_keys(indices_map_keys)
candidate_navigational_space_axes_labels = tuple()
for key in candidate_prismatique_metadata_keys:
axis_label = key.replace("original", "navigational")
if (("slice" in axis_label)
and (extra_kwargs.get("num_superslices", 0) > 0)):
axis_label = axis_label.replace("slice", "superslice")
candidate_navigational_space_axes_labels += (axis_label,)
return candidate_navigational_space_axes_labels
def _subset_of_navigational_space_axes_properties(
indices_map_key,
single_dim_slice,
use_two_axes_to_map_probe_position_if_possible,
filename,
candidate_axis_label,
extra_kwargs,
indices_map):
axes_property_names = \
("axes_labels", "sizes", "scales", "offsets", "units")
subset_of_navigational_space_axes_properties = \
{key: tuple() for key in axes_property_names}
subset_of_axes_properties = \
subset_of_navigational_space_axes_properties # Alias for brevity.
if indices_map_key == "probe_indices":
scan_pattern_type = prismatique.load.scan_pattern_type(filename)
if ((scan_pattern_type == "rectangular grid")
and (single_dim_slice == slice(None))
and (use_two_axes_to_map_probe_position_if_possible)):
positions = probe_positions(filename, force_2_col_shape=False)
Y_dim, X_dim, _ = positions.shape
d_r_x = positions[0, 1, 0]-positions[0, 0, 0] if (X_dim > 1) else 1
d_r_y = positions[0, 0, 1]-positions[1, 0, 1] if (Y_dim > 1) else 1
subset_of_axes_properties["axes_labels"] = (r"probe $x$-coordinate",
r"probe $y$-coordinate")
subset_of_axes_properties["sizes"] = (X_dim, Y_dim)
subset_of_axes_properties["scales"] = (d_r_x, -d_r_y)
subset_of_axes_properties["offsets"] = (positions[0, 0, 0],
positions[0, 0, 1])
subset_of_axes_properties["units"] = ("Å", "Å")
return subset_of_navigational_space_axes_properties
subset_of_axes_properties["axes_labels"] = (candidate_axis_label,)
if ((indices_map_key == "slice_indices")
and (extra_kwargs.get("num_superslices", 0) > 0)):
subset_of_axes_properties["sizes"] = (extra_kwargs["num_superslices"],)
else:
indices = indices_map[indices_map_key]
subset_of_axes_properties["sizes"] = (len(indices),)
subset_of_axes_properties["scales"] = (1,)
subset_of_axes_properties["offsets"] = (0,)
subset_of_axes_properties["units"] = ("dimensionless",)
return subset_of_navigational_space_axes_properties
def _build_signal(signal_data,
signal_metadata,
signal_axes,
navigational_space_rank):
signal_space_rank = len(signal_data.shape) - navigational_space_rank
current_signal_title = signal_metadata["General"]["title"]
if signal_data.dtype in ("complex64", "complex128"):
signal = hyperspy.signals.ComplexSignal2D(data=signal_data,
metadata=signal_metadata)
else:
if signal_space_rank == 2:
signal = hyperspy.signals.Signal2D(data=signal_data,
metadata=signal_metadata)
elif signal_space_rank == 1:
signal = hyperspy.signals.Signal1D(data=signal_data,
metadata=signal_metadata)
else:
signal = hyperspy.signals.BaseSignal(data=signal_data,
metadata=signal_metadata)
for idx, axis in enumerate(signal_axes):
signal.axes_manager[idx].update_from(axis)
signal.axes_manager[idx].name = axis.name
signal.axes_manager[idx].units = axis.units
return signal
def _sample_specification_signal_and_indices_map(
filename,
multi_dim_slice,
indices_map_keys,
average_thermally,
average_laterally_in_space,
num_superslices,
func_to_calc_sizes_of_navigational_space_axes,
func_to_initialize_sample_specification_signal):
extra_kwargs = {"average_thermally": average_thermally,
"average_laterally_in_space": average_laterally_in_space,
"num_superslices": num_superslices}
kwargs = \
{"filename": \
filename,
"multi_dim_slice": \
multi_dim_slice,
"indices_map_keys": \
indices_map_keys,
"func_to_calc_sizes_of_navigational_space_axes": \
func_to_calc_sizes_of_navigational_space_axes}
indices_map, multi_dim_slice = \
_indices_map_and_converted_multi_dim_slice(**kwargs)
func_alias = func_to_initialize_sample_specification_signal
kwargs["multi_dim_slice"] = multi_dim_slice
kwargs["indices_map"] = indices_map
kwargs["extra_kwargs"] = extra_kwargs
del kwargs["func_to_calc_sizes_of_navigational_space_axes"]
sample_specification_signal = func_alias(**kwargs)
kwargs = {"indices_map": indices_map,
"filename": filename,
"sample_specification_signal": sample_specification_signal,
"extra_kwargs": extra_kwargs}
_load_data_into_sample_specification_signal(**kwargs)
kwargs = {"indices_map": indices_map,
"multi_dim_slice": multi_dim_slice,
"extra_kwargs": extra_kwargs,
"filename": filename,
"use_two_axes_to_map_probe_position_if_possible": False}
_clean_indices_map(**kwargs)
return sample_specification_signal, indices_map
def _indices_map_and_converted_multi_dim_slice(
filename,
multi_dim_slice,
indices_map_keys,
func_to_calc_sizes_of_navigational_space_axes):
expected_length = len(indices_map_keys)
if multi_dim_slice is None:
multi_dim_slice = (slice(None),)*expected_length
else:
_check_multi_dim_slice_length(multi_dim_slice, expected_length)
sizes_of_navigational_axes = \
func_to_calc_sizes_of_navigational_space_axes(filename)
indices_map = dict()
zip_obj = zip(indices_map_keys, sizes_of_navigational_axes, multi_dim_slice)
for dim, triplet in enumerate(zip_obj):
key, axis_size, single_dim_slice = triplet
if isinstance(single_dim_slice, int):
single_dim_slice = [single_dim_slice]
all_indices_along_dim = np.arange(axis_size)
current_func_name = "_indices_map_and_converted_multi_dim_slice"
try:
selected_indices_along_dim = all_indices_along_dim[single_dim_slice]
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(dim, axis_size)
raise IndexError(err_msg)
selected_indices_along_dim = tuple(selected_indices_along_dim.tolist())
temp_idx_set = set(selected_indices_along_dim)
if len(selected_indices_along_dim) != len(temp_idx_set):
unformatted_err_msg = globals()[current_func_name+"_err_msg_2"]
err_msg = unformatted_err_msg.format(dim)
raise ValueError(err_msg)
indices_map[key] = selected_indices_along_dim
return indices_map, multi_dim_slice
def _check_multi_dim_slice_length(multi_dim_slice, expected_length):
current_func_name = "_check_multi_dim_slice_length"
if len(multi_dim_slice) != expected_length:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(expected_length)
raise ValueError(err_msg)
return None
def _load_data_into_sample_specification_signal(indices_map,
filename,
sample_specification_signal,
extra_kwargs):
kwargs = {"indices_map": indices_map,
"filename": filename,
"sample_specification_signal": sample_specification_signal}
if "Potential" in sample_specification_signal.metadata.General.title:
kwargs["extra_kwargs"] = extra_kwargs
_load_data_into_potential_slice_signal(**kwargs)
else:
_load_data_into_S_matrix_wavefunction_signal(**kwargs)
return None
def _load_data_into_potential_slice_signal(extra_kwargs,
indices_map,
filename,
sample_specification_signal):
average_thermally = extra_kwargs["average_thermally"]
average_laterally_in_space = extra_kwargs["average_laterally_in_space"]
num_superslices = extra_kwargs["num_superslices"]
original_idx_set_1 = indices_map["atomic_config_indices"]
slice_idx_subsets = _original_slice_indices_of_superslices(filename,
indices_map,
num_superslices)
for nav_idx_1, original_idx_1 in enumerate(original_idx_set_1):
path_in_file = ("4DSTEM_simulation/data/realslices/ppotential_fp"
+ str(original_idx_1).rjust(4, "0") + "/data")
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
for superslice_idx, slice_idx_subset in enumerate(slice_idx_subsets):
kwargs = {"dataset_id": \
dataset_id,
"multi_dim_slice": \
(slice(None), slice(None), slice_idx_subset)}
datasubset_id = h5pywrappers.datasubset.ID(**kwargs)
kwargs = \
{"datasubset_id": datasubset_id,
"average_thermally": average_thermally,
"original_idx_set_1": original_idx_set_1,
"nav_idx_1": nav_idx_1,
"num_superslices": num_superslices,
"slice_idx_subset": slice_idx_subset,
"superslice_idx": superslice_idx,
"average_laterally_in_space": average_laterally_in_space,
"sample_specification_signal": sample_specification_signal}
_ = \
_load_datasubset_into_potential_slice_signal(**kwargs)
if average_thermally:
sample_specification_signal.data /= len(original_idx_set_1)
return None
def _load_datasubset_into_potential_slice_signal(datasubset_id,
average_thermally,
original_idx_set_1,
nav_idx_1,
num_superslices,
slice_idx_subset,
superslice_idx,
average_laterally_in_space,
sample_specification_signal):
datasubset = h5pywrappers.datasubset.load(datasubset_id)
multi_dim_slice_1 = tuple()
if (not average_thermally) and (len(original_idx_set_1) > 1):
multi_dim_slice_1 += (nav_idx_1,)
if num_superslices == 0:
if len(slice_idx_subset) > 1:
multi_dim_slice_1 += (slice(None),)
elif num_superslices > 1:
multi_dim_slice_1 += (superslice_idx,)
if not average_laterally_in_space:
multi_dim_slice_1 += (slice(None), slice(None))
multi_dim_slice_1 += (len(multi_dim_slice_1) == 0)*(slice(None),)
if ((len(datasubset.shape) == 3)
and ((num_superslices > 0)
or ((num_superslices == 0) and (datasubset.shape[-1] == 1)))):
datasubset = np.sum(datasubset, axis=(2,))
if average_laterally_in_space:
datasubset = np.mean(datasubset, axis=(0, 1))
if len(datasubset.shape) != 1:
datasubset = np.array([datasubset])
axes = tuple(range(len(datasubset.shape)))[::-1]
multi_dim_slice_2 = (slice(None), slice(None, None, -1), slice(None))
multi_dim_slice_2 = multi_dim_slice_2[3-len(datasubset.shape):]
sample_specification_signal.data[multi_dim_slice_1] += \
np.transpose(datasubset, axes=axes)[multi_dim_slice_2]
return None
def _load_data_into_S_matrix_wavefunction_signal(indices_map,
filename,
sample_specification_signal):
original_idx_set_1 = indices_map["atomic_config_indices"]
original_idx_set_2 = (indices_map["k_xy_vector_indices"]
if (len(indices_map["k_xy_vector_indices"]) > 1)
else indices_map["k_xy_vector_indices"][0])
for nav_idx_1, original_idx_1 in enumerate(original_idx_set_1):
path_in_file = ("4DSTEM_simulation/data/realslices/smatrix_fp"
+ str(original_idx_1).rjust(4, "0") + "/data")
kwargs = {"dataset_id": \
h5pywrappers.obj.ID(filename, path_in_file),
"multi_dim_slice": \
(slice(None), slice(None), original_idx_set_2)}
datasubset_id = h5pywrappers.datasubset.ID(**kwargs)
datasubset = h5pywrappers.datasubset.load(datasubset_id)
multi_dim_slice_1 = (slice(None), slice(None))
if len(datasubset.shape) == 3:
multi_dim_slice_1 = (slice(None),) + multi_dim_slice_1
if len(original_idx_set_1) > 1:
multi_dim_slice_1 = (nav_idx_1,) + multi_dim_slice_1
multi_dim_slice_1 = tuple(multi_dim_slice_1)
axes = list(range(len(datasubset.shape)))[::-1]
multi_dim_slice_2 = (slice(None), slice(None, None, -1), slice(None))
multi_dim_slice_2 = multi_dim_slice_2[3-len(datasubset.shape):]
sample_specification_signal.data[multi_dim_slice_1] += \
np.transpose(datasubset, axes=axes)[multi_dim_slice_2]
return None
def _clean_indices_map(indices_map,
multi_dim_slice,
extra_kwargs,
filename,
use_two_axes_to_map_probe_position_if_possible):
indices_map_keys = tuple(indices_map.keys())
for dim, key in enumerate(indices_map_keys):
if ((key == "slice_indices")
and ((extra_kwargs.get("num_superslices", 0) > 0)
or ((extra_kwargs.get("num_superslices", 0) == 0)
and isinstance(multi_dim_slice[dim], int)))):
del indices_map[key]
elif (isinstance(multi_dim_slice[dim], int)
or ((key == "atomic_config_indices")
and extra_kwargs.get("average_thermally", False))):
del indices_map[key]
elif key == "probe_indices":
scan_pattern_type = prismatique.load.scan_pattern_type(filename)
if ((scan_pattern_type == "rectangular grid")
and (multi_dim_slice[-1] == slice(None))
and (use_two_axes_to_map_probe_position_if_possible)):
del indices_map[key]
return None
[docs]def S_matrix_wavefunctions(filename,
multi_dim_slice=\
_default_multi_dim_slice,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given ``prismatique`` output file that stores :math:`S`-matrices,
load a specified subcollection of said :math:`S`-matrices into a
``hyperspy`` signal.
See the documentation for the subpackage :mod:`prismatique.stem` for a
discussion on :math:`S`-matrices.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the
:math:`S`-matrices of interest. Any non-temporary file generated by the
function :func:`prismatique.sample.generate_S_matrices`, or
:func:`prismatique.stem.sim.run` using the PRISM algorithm, with
originally a basename of the form
``"S_matrices_of_subset_"+str(i)+".h5"`` is valid, where ``i`` is a
nonnegative integer.
multi_dim_slice : `tuple` (`int` | `slice` | `list` (`int`)) | `None`, optional
The "multidimensional slice object", which specifies the subcollection
of :math:`S`-matrices to load from file. We define a multi-dimensional
slice object as a `tuple` of items which contains at most one item being
a `list` of integers, and the remaining items being `slice` and/or `int`
objects.
If ``multi_dim_slice`` is a `tuple` of length 2, then
``multi_dim_slice[0]`` specifies a set of atomic configuration indices,
where each atomic configuration index corresponds to a frozen phonon
configuration; and ``multi_dim_slice[1]`` specifies a set of
:math:`\mathbf{k}_{xy}` indices, where each :math:`\mathbf{k}_{xy}`
index corresponds to a different :math:`\mathbf{k}_{xy}`-momentum vector
of a plane wave used to calculate a different :math:`S`-matrix. In this
case, the current Python function will load the :math:`S`-matrices of
the frozen phonon configurations specified by ``multi_dim_slice[0]``,
calculated using the plane waves with the
:math:`\mathbf{k}_{xy}`-momenta vectors specified by
``multi_dim_slice[1]``. Note that
``prismatique.sample.S_matrix_k_xy_vectors(sample_specification,
probe_model_params)[multi_dim_slice[1]]` yields the aforementioned
specified :math:`\mathbf{k}_{xy}`-momenta vectors, where
``sample_specification`` and ``probe_model_params`` are objects of the
appropriate types representing the sample model/model parameters and the
probe model parameters respectively, used to generate the
:math:`S`-matrices.
Otherwise, if ``multi_dim_slice`` is set to `None`, then all the
:math:`S`-matrices stored in the file are loaded.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
S_matrix_wavefunction_signal : :class:`hyperspy._signals.complex_signal2d.ComplexSignal2D`
The subcollection of :math:`S`-matrices, stored in an instance of the
:class:`hyperspy._signals.complex_signal2d.ComplexSignal2D` class. See
the documentation and/or reference guide for the :mod:`hyperspy` package
for details on how to use instances of the
:class:`hyperspy._signals.complex_signal2d.ComplexSignal2D` class.
navigational_to_original_indices_map : `dict`
A dictionary that maps the navigational indices of the hyperspy signal
``S_matrix_wavefunction_signal`` to the original indices specified by
``multi_dim_slice``. For example, if the original atomic configuration
indices map to a set of corresponding navigational indices, then
``navigational_to_original_indices_map["atomic_config_indices"][i]``
yields the atomic configuration index specified in the expression
``single_dim_slice=multi_dim_slice[0] if multi_dim_slice is not None
else slice(None)`` that corresponds to the ``i`` th atomic configuration
index in the nagivation index space of
``S_matrix_wavefunction_signal``, where ``i`` is a nonnegative integer
smaller than the total number of atomic configuration indices specified
in ``single_dim_slice``.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
result = _S_matrix_wavefunctions(**kwargs)
S_matrix_wavefunction_signal, navigational_to_original_indices_map = result
return S_matrix_wavefunction_signal, navigational_to_original_indices_map
def _S_matrix_wavefunctions(filename, multi_dim_slice):
current_func_params = locals()
kwargs = \
{**current_func_params,
"indices_map_keys": \
("atomic_config_indices", "k_xy_vector_indices"),
"average_thermally": \
False,
"average_laterally_in_space": \
False,
"num_superslices": \
0,
"func_to_calc_sizes_of_navigational_space_axes": \
_sizes_of_navigational_space_axes_for_S_matrix_wavefunctions,
"func_to_initialize_sample_specification_signal": \
_initialize_S_matrix_wavefunction_signal}
S_matrix_wavefunction_signal, navigational_to_original_indices_map = \
_sample_specification_signal_and_indices_map(**kwargs)
return S_matrix_wavefunction_signal, navigational_to_original_indices_map
def _sizes_of_navigational_space_axes_for_S_matrix_wavefunctions(filename):
current_func_name = \
"_sizes_of_navigational_space_axes_for_S_matrix_wavefunctions"
try:
kwargs = {"filenames": (filename,)}
sample_specification = prismatique.sample.SMatrixSubsetIDs(**kwargs)
params = {"sample_specification": sample_specification,
"accepted_types": (prismatique.sample.SMatrixSubsetIDs,)}
accepted_types = (prismatique.sample.SMatrixSubsetIDs,)
prismatique.sample._check_and_convert_sample_specification(params)
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename, current_func_name[38:])
raise IOError(err_msg)
kwargs = \
{"sample_specification": sample_specification, "subset_idx": 0}
num_frozen_phonon_configs_in_subset = \
prismatique.sample._num_frozen_phonon_configs_in_subset(**kwargs)
path_in_file = "4DSTEM_simulation/data/realslices/smatrix_fp0000/data"
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id)
num_S_matrix_k_xy_vectors = dataset.shape[2]
dataset.file.close()
sizes_of_navigational_space_axes = (num_frozen_phonon_configs_in_subset,
num_S_matrix_k_xy_vectors)
return sizes_of_navigational_space_axes
def _initialize_S_matrix_wavefunction_signal(filename,
multi_dim_slice,
indices_map,
indices_map_keys,
extra_kwargs):
kwargs = {"filenames": (filename,)}
sample_specification = prismatique.sample.SMatrixSubsetIDs(**kwargs)
N_x, N_y = \
prismatique.sample._supercell_xy_dims_in_pixels(sample_specification)
interpolation_factors_from_sample_specification = \
prismatique.sample._interpolation_factors_from_sample_specification
f_x, f_y = \
interpolation_factors_from_sample_specification(sample_specification)
Delta_x, Delta_y = \
prismatique.sample._supercell_lateral_pixel_size(sample_specification)
signal_space_offsets = (-Delta_x * (N_x // 2 // f_x),
Delta_y * (N_y // 2 // f_y))
kwargs = {"filename": filename,
"multi_dim_slice": multi_dim_slice,
"indices_map": indices_map,
"indices_map_keys": indices_map_keys,
"signal_space_sizes": (N_x // 2 // f_x, N_y // 2 // f_y),
"signal_space_scales": (2 * Delta_x, -2 * Delta_y),
"signal_space_offsets": signal_space_offsets,
"signal_space_units": ("Å", "Å"),
"signal_dtype": "complex64",
"signal_title": "S-Matrix Wavefunction",
"use_two_axes_to_map_probe_position_if_possible": False,
"extra_kwargs": extra_kwargs}
S_matrix_wavefunction_signal = \
_initialize_signal_for_generic_case(**kwargs)
return S_matrix_wavefunction_signal
def _check_and_convert_use_two_axes_to_map_probe_position_if_possible(params):
obj_name = \
"use_two_axes_to_map_probe_position_if_possible"
kwargs = \
{"obj": params[obj_name], "obj_name": obj_name}
use_two_axes_to_map_probe_position_if_possible = \
czekitout.convert.to_bool(**kwargs)
return use_two_axes_to_map_probe_position_if_possible
_default_use_two_axes_to_map_probe_position_if_possible = False
[docs]def cbed_wavefunctions(filename,
multi_dim_slice=\
_default_multi_dim_slice,
use_two_axes_to_map_probe_position_if_possible=\
_default_use_two_axes_to_map_probe_position_if_possible,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given STEM simulation output file that stores CBED wavefunction
patterns, load a specified subcollection of said CBED wavefunction patterns
into a ``hyperspy`` signal.
See the documentation for the class :class:`prismatique.cbed.Params` for a
discussion on CBED wavefunction patterns, which we denote as
:math:`\left|\psi_{t}\left(\delta_{f};\mathbf{u}_{1}, \ldots,\mathbf{u}_{N};
\boldsymbol{\delta}_{\beta}\right)\right\rangle` throughout the
documentation. See also the documentation for the class
:class:`prismatique.thermal.Params` for additional information and context
on said patterns.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the CBED
wavefunction patterns. Any non-temporary file generated by the function
:func:`prismatique.stem.sim.run`, with originally a basename of the form
``"stem_sim_wavefunction_output_of_subset_"+str(i)+".h5"`` is valid,
where ``i`` is a nonnegative integer. See the documentation for the
class :class:`prismatique.stem.output.Params` for a discussion on the
layout and structuring of ``prismatique`` STEM simulation output files.
multi_dim_slice : `tuple` (`int` | `slice` | `list` (`int`)) | `None`, optional
The "multidimensional slice object", which specifies the subcollection
of CBED wavefunction patterns to load from file. We define a
multi-dimensional slice object as a `tuple` of items which contains at
most one item being a `list` of integers, and the remaining items being
`slice` and/or `int` objects.
If ``multi_dim_slice`` is a `tuple` of length 4, then then
``multi_dim_slice[0]`` specifies a set of output layer indices, where
each output layer index corresponds to a different output layer;
``multi_dim_slice[1]`` specifies a set of atomic configuration indices,
where each atomic configuration index corresponds to a frozen phonon
configuration; ``multi_dim_slice[2]`` specifies a set of defocus
indices, where each defocus index corresponds to a different beam
defocus; and ``multi_dim_slice[3]`` specifies a set of probe indices,
where each probe index corresponds to a different probe position. In
this case, the current Python function will load the CBED wavefunction
patterns of the frozen phonon configurations specified by
``multi_dim_slice[1]``, generated by electrons exiting from the output
layers specified by ``multi_dim_slice[0]``, the electrons of which
coming from probes operating at the defocii specified by
``multi_dim_slice[2]`` and centered at the positions specified by
``multi_dim_slice[3]``. Note that
``prismatique.load.output_layer_depths(filename)[multi_dim_slice[0]]``
yields the depths of the specified output layers;
``prismatique.load.defocii(filename)[multi_dim_slice[2]]`` yields the
specified defocii; and ``prismatique.load.probe_positions(filename,
force_2_col_shape=True)[multi_dim_slice[3]]`` yields the specified probe
positions.
Otherwise, if ``multi_dim_slice`` is set to `None`, then all the CBED
wavefunction patterns stored in the file are loaded.
use_two_axes_to_map_probe_position_if_possible : `bool`, optional
If ``use_two_axes_to_map_probe_position_if_possible`` is set to
``True``, ``prismatique.load.scan_pattern_type(filename) ==
"rectangular grid"``, and ``multi_dim_slice`` satisfies either
``multi_dim_slice[-1]==slice(None)`` or ``multi_dim_slice is None``,
then two ``hyperspy`` axes are used rather than one to map the probe
position. In this case, the two axes have the same dimensions as the
rectangular grid on which the probe positions lie. Otherwise, one
``hyperspy`` axis is used to map the probe positions.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
cbed_wavefunction_signal : :class:`hyperspy._signals.complex_signal2d.ComplexSignal2D`
The subcollection of CBED wavefunction patterns, stored in an instance
of the :class:`hyperspy._signals.complex_signal2d.ComplexSignal2D`
class. See the documentation and/or reference guide for the
:mod:`hyperspy` package for details on how to use instances of the
:class:`hyperspy._signals.complex_signal2d.ComplexSignal2D` class.
navigational_to_original_indices_map : `dict`
A dictionary that maps the navigational indices of the hyperspy signal
``cbed_wavefunction_signal`` to the original indices specified by
``multi_dim_slice``. For example, if the original atomic configuration
indices map to a set of corresponding navigational indices, then
``navigational_to_original_indices_map["atomic_config_indices"][i]``
yields the atomic configuration index specified in the expression
``single_dim_slice=multi_dim_slice[1] if multi_dim_slice is not None
else slice(None)`` that corresponds to the ``i`` th atomic configuration
index in the nagivation index space of ``cbed_wavefunction_signal``,
where ``i`` is a nonnegative integer smaller than the total number of
atomic configuration indices specified in ``single_dim_slice``.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
result = _cbed_wavefunctions(**kwargs)
cbed_wavefunction_signal, navigational_to_original_indices_map = result
return cbed_wavefunction_signal, navigational_to_original_indices_map
def _cbed_wavefunctions(filename,
multi_dim_slice,
use_two_axes_to_map_probe_position_if_possible):
current_func_params = locals()
indices_map_keys = ("output_layer_indices",
"atomic_config_indices",
"defocus_indices",
"probe_indices")
kwargs = \
{**current_func_params,
"indices_map_keys": \
indices_map_keys,
"func_to_calc_sizes_of_navigational_space_axes": \
_sizes_of_navigational_space_axes_for_cbed_wavefunctions,
"func_to_initialize_cbed_related_signal": \
_initialize_cbed_wavefunction_signal}
cbed_wavefunction_signal, navigational_to_original_indices_map = \
_cbed_related_signal_and_indices_map(**kwargs)
return cbed_wavefunction_signal, navigational_to_original_indices_map
def _sizes_of_navigational_space_axes_for_cbed_wavefunctions(filename):
current_func_name = ("_sizes_of_navigational_space_axes"
"_for_cbed_wavefunctions")
try:
path_in_file = "/data/4D_STEM/complex_valued_DPs"
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
sizes_of_navigational_space_axes = dataset.shape[:4]
dataset.file.close()
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename, current_func_name[38:])
raise IOError(err_msg)
return sizes_of_navigational_space_axes
def _initialize_cbed_wavefunction_signal(
filename,
multi_dim_slice,
indices_map,
indices_map_keys,
use_two_axes_to_map_probe_position_if_possible):
current_func_params = locals()
k_x = cbed_k_x_coords(filename)
k_y = cbed_k_y_coords(filename)
kwargs = {**current_func_params,
"signal_space_sizes": (len(k_x), len(k_y)),
"signal_space_scales": (k_x[1]-k_x[0], k_y[1]-k_y[0]),
"signal_space_offsets": (k_x[0], k_y[0]),
"signal_space_units": ("1/Å", "1/Å"),
"signal_dtype": "complex64",
"signal_title": "CBED Wavefunction",
"extra_kwargs": dict()}
cbed_wavefunction_signal = _initialize_signal_for_generic_case(**kwargs)
return cbed_wavefunction_signal
def _cbed_related_signal_and_indices_map(
filename,
multi_dim_slice,
indices_map_keys,
use_two_axes_to_map_probe_position_if_possible,
func_to_calc_sizes_of_navigational_space_axes,
func_to_initialize_cbed_related_signal):
kwargs = \
{"filename": \
filename,
"multi_dim_slice": \
multi_dim_slice,
"indices_map_keys": \
indices_map_keys,
"func_to_calc_sizes_of_navigational_space_axes": \
func_to_calc_sizes_of_navigational_space_axes}
indices_map, multi_dim_slice = \
_indices_map_and_converted_multi_dim_slice(**kwargs)
kwargs = {"filename": \
filename,
"multi_dim_slice": \
multi_dim_slice,
"indices_map": \
indices_map,
"indices_map_keys": \
indices_map_keys,
"use_two_axes_to_map_probe_position_if_possible": \
use_two_axes_to_map_probe_position_if_possible}
cbed_related_signal = func_to_initialize_cbed_related_signal(**kwargs)
kwargs["cbed_related_signal"] = cbed_related_signal
del kwargs["indices_map"]
del kwargs["indices_map_keys"]
_load_data_into_cbed_related_signal(**kwargs)
kwargs["indices_map"] = indices_map
kwargs["extra_kwargs"] = dict()
del kwargs["cbed_related_signal"]
_clean_indices_map(**kwargs)
return cbed_related_signal, indices_map
def _load_data_into_cbed_related_signal(
filename,
multi_dim_slice,
cbed_related_signal,
use_two_axes_to_map_probe_position_if_possible):
path_in_file = _path_in_file_to_cbed_related_data(cbed_related_signal)
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
dataset_rank = len(dataset.shape)
dataset.file.close()
kwargs = \
{"dataset_id": \
dataset_id,
"multi_dim_slice": \
multi_dim_slice + (slice(None),)*(dataset_rank-len(multi_dim_slice))}
datasubset_id = \
h5pywrappers.datasubset.ID(**kwargs)
current_func_name = "_load_data_into_cbed_related_signal"
try:
cbed_related_data = h5pywrappers.datasubset.load(datasubset_id)
kwargs = \
{"filename": \
filename,
"multi_dim_slice": \
multi_dim_slice,
"cbed_related_signal": \
cbed_related_signal,
"use_two_axes_to_map_probe_position_if_possible": \
use_two_axes_to_map_probe_position_if_possible}
cbed_related_data_is_to_be_transformed = \
_cbed_related_data_is_to_be_transformed(**kwargs)
if cbed_related_data_is_to_be_transformed:
kwargs = {"cbed_related_data": cbed_related_data,
"filename": filename,
"cbed_related_signal": cbed_related_signal}
cbed_related_data = _transform_cbed_related_data(**kwargs)
cbed_related_signal.data = cbed_related_data
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(path_in_file, filename)
raise IOError(err_msg)
return None
def _path_in_file_to_cbed_related_data(cbed_related_signal):
signal_title = cbed_related_signal.metadata.General.title
signal_title_to_path_in_file_map = \
{"CBED Wavefunction": "/data/4D_STEM/complex_valued_DPs",
"CBED Intensity Pattern": "/data/4D_STEM/intensity_DPs",
"Center of Mass Momentum": "/data/center_of_mass_momentum",
"STEM Intensity Image": "/data/2D_STEM/integrated_intensities"}
default_path_in_file = \
"/data/3D_STEM/integrated_intensities"
path_in_file = \
signal_title_to_path_in_file_map.get(signal_title, default_path_in_file)
return path_in_file
def _cbed_related_data_is_to_be_transformed(
filename,
multi_dim_slice,
cbed_related_signal,
use_two_axes_to_map_probe_position_if_possible):
scan_pattern_type = prismatique.load.scan_pattern_type(filename)
signal_title = cbed_related_signal.metadata.General.title
if ((signal_title in ("STEM Intensity Image", "Center of Mass Momentum"))
or (multi_dim_slice[-1] == slice(None))):
data_for_all_probe_positions_is_to_be_loaded = True
else:
data_for_all_probe_positions_is_to_be_loaded = False
if ((scan_pattern_type == "rectangular grid")
and use_two_axes_to_map_probe_position_if_possible
and data_for_all_probe_positions_is_to_be_loaded):
cbed_related_data_is_to_be_transformed = True
else:
cbed_related_data_is_to_be_transformed = False
return cbed_related_data_is_to_be_transformed
def _transform_cbed_related_data(cbed_related_data,
filename,
cbed_related_signal):
signal_title = cbed_related_signal.metadata.General.title
if signal_title in ("STEM Intensity Image", "Center of Mass Momentum"):
idx = 0
else:
idx = cbed_related_signal.axes_manager.signal_dimension
new_shape = (cbed_related_data.shape[:len(cbed_related_data.shape)-(idx+1)]
+ grid_dims_in_units_of_probe_shifts(filename)
+ cbed_related_data.shape[len(cbed_related_data.shape)-idx:])
new_axes_order = np.arange(len(new_shape))
new_axes_order[[-(idx+1), -(idx+2)]] = new_axes_order[[-(idx+2), -(idx+1)]]
new_axes_order = tuple(new_axes_order.tolist())
multi_dim_slice = [slice(None)]*len(new_shape)
multi_dim_slice[-(idx+2)] = slice(None, None, -1)
multi_dim_slice = tuple(multi_dim_slice)
cbed_related_data = np.transpose(cbed_related_data.reshape(new_shape),
axes=new_axes_order)[multi_dim_slice]
return cbed_related_data
[docs]def cbed_intensity_patterns(
filename,
multi_dim_slice=\
_default_multi_dim_slice,
use_two_axes_to_map_probe_position_if_possible=\
_default_use_two_axes_to_map_probe_position_if_possible,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given STEM simulation output file that stores CBED intensity
patterns, load a specified subcollection of said CBED intensity patterns
into a ``hyperspy`` signal.
See the documentation for the class :class:`prismatique.cbed.Params` for a
discussion on CBED intensity patterns
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the CBED intensity
patterns. Any non-temporary file generated by the function
:func:`prismatique.stem.sim.run`, with originally the basename
``"stem_sim_intensity_output.h5"`` is valid. See the documentation for
the class :class:`prismatique.stem.output.Params` for a discussion on
the layout and structuring of ``prismatique`` STEM simulation output
files.
multi_dim_slice : `tuple` (`int` | `slice` | `list` (`int`)) | `None`, optional
The "multidimensional slice object", which specifies the subcollection
of CBED intensity patterns to load from file. We define a
multi-dimensional slice object as a `tuple` of items which contains at
most one item being a `list` of integers, and the remaining items being
`slice` and/or `int` objects.
If ``multi_dim_slice`` is a `tuple` of length 2, then then
``multi_dim_slice[0]`` specifies a set of output layer indices, where
each output layer index corresponds to a different output layer; and
``multi_dim_slice[1]`` specifies a set of probe indices, where each
probe index corresponds to a different probe position. In this case, the
current Python function will load the CBED intensity patterns generated
by electrons exiting from the output layers specified by
``multi_dim_slice[0]``, the electrons of which coming from probes
centered at the positions specified by ``multi_dim_slice[1]``. Note that
``prismatique.load.output_layer_depths(filename)[multi_dim_slice[0]]``
yields the depths of the specified output layers; and
``prismatique.load.probe_positions(filename)[multi_dim_slice[1]]``
yields the specified probe positions.
Otherwise, if ``multi_dim_slice`` is set to `None`, then all the CBED
intensity patterns stored in the file are loaded.
use_two_axes_to_map_probe_position_if_possible : `bool`, optional
If ``use_two_axes_to_map_probe_position_if_possible`` is set to
``True``, ``prismatique.load.scan_pattern_type(filename) ==
"rectangular grid"``, and ``multi_dim_slice`` satisfies either
``multi_dim_slice[-1]==slice(None)`` or ``multi_dim_slice is None``,
then two ``hyperspy`` axes are used rather than one to map the probe
position. In this case, the two axes have the same dimensions as the
rectangular grid on which the probe positions lie. Otherwise, one
``hyperspy`` axis is used to map the probe positions.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
cbed_intensity_pattern_signal : :class:`hyperspy._signals.signal2d.Signal2D`
The subcollection of CBED intensity patterns, stored in an instance of
the :class:`hyperspy._signals.signal2d.Signal2D` class. See the
documentation and/or reference guide for the :mod:`hyperspy` package for
details on how to use instances of the
:class:`hyperspy._signals.signal2d.Signal2D` class.
navigational_to_original_indices_map : `dict`
A dictionary that maps the navigational indices of the hyperspy signal
``cbed_intensity_pattern_signal`` to the original indices specified by
``multi_dim_slice``. For example, if the original output layer indices
indices map to a set of corresponding navigational indices, then
``navigational_to_original_indices_map["output_layer_indices"][i]``
yields the output layer index specified in the expression
``single_dim_slice=multi_dim_slice[0] if multi_dim_slice is not None
else slice(None)`` that corresponds to the ``i`` th atomic configuration
index in the nagivation index space of
``cbed_intensity_pattern_signal``, where ``i`` is a nonnegative integer
smaller than the total number of output layers specified in
``single_dim_slice``.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
result = _cbed_intensity_patterns(**kwargs)
cbed_intensity_pattern_signal, navigational_to_original_indices_map = result
return cbed_intensity_pattern_signal, navigational_to_original_indices_map
def _cbed_intensity_patterns(filename,
multi_dim_slice,
use_two_axes_to_map_probe_position_if_possible):
current_func_params = locals()
kwargs = \
{**current_func_params,
"indices_map_keys": \
("output_layer_indices", "probe_indices"),
"func_to_calc_sizes_of_navigational_space_axes": \
_sizes_of_navigational_space_axes_for_cbed_intensity_patterns,
"func_to_initialize_cbed_related_signal": \
_initialize_cbed_intensity_pattern_signal}
cbed_intensity_pattern_signal, navigational_to_original_indices_map = \
_cbed_related_signal_and_indices_map(**kwargs)
return cbed_intensity_pattern_signal, navigational_to_original_indices_map
def _sizes_of_navigational_space_axes_for_cbed_intensity_patterns(filename):
current_func_name = ("_sizes_of_navigational_space_axes"
"_for_cbed_intensity_patterns")
try:
path_in_file = "/data/4D_STEM/intensity_DPs"
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
sizes_of_navigational_space_axes = dataset.shape[:2]
dataset.file.close()
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename, current_func_name[38:])
raise IOError(err_msg)
return sizes_of_navigational_space_axes
def _initialize_cbed_intensity_pattern_signal(
filename,
multi_dim_slice,
indices_map,
indices_map_keys,
use_two_axes_to_map_probe_position_if_possible):
current_func_params = locals()
k_x = cbed_k_x_coords(filename)
k_y = cbed_k_y_coords(filename)
kwargs = \
{**current_func_params,
"signal_space_sizes": (len(k_x), len(k_y)),
"signal_space_scales": (k_x[1]-k_x[0], k_y[1]-k_y[0]),
"signal_space_offsets": (k_x[0], k_y[0]),
"signal_space_units": ("1/Å", "1/Å"),
"signal_dtype": "float32",
"signal_title": "CBED Intensity Pattern",
"extra_kwargs": dict()}
cbed_intensity_pattern_signal = \
_initialize_signal_for_generic_case(**kwargs)
return cbed_intensity_pattern_signal
[docs]def com_momenta(filename,
multi_dim_slice=\
_default_multi_dim_slice,
use_two_axes_to_map_probe_position_if_possible=\
_default_use_two_axes_to_map_probe_position_if_possible,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given STEM simulation output file that stores center of mass
(COM) momentum patterns, load a specified subcollection of said COM momentum
patterns into a ``hyperspy`` signal.
See the documentation for the class :class:`prismatique.stem.output.Params`
for a discussion on COM momentum patterns.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the COM momentum
patterns. Any non-temporary file generated by the function
:func:`prismatique.stem.sim.run`, with originally the basename
``"stem_sim_intensity_output.h5"`` is valid. See the documentation for
the class :class:`prismatique.stem.output.Params` for a discussion on
the layout and structuring of ``prismatique`` STEM simulation output
files.
multi_dim_slice : `tuple` (`int` | `slice` | `list` (`int`)) | `None`, optional
The "multidimensional slice object", which specifies the subcollection
of COM momentum patterns to load from file. We define a
multi-dimensional slice object as a `tuple` of items which contains at
most one item being a `list` of integers, and the remaining items being
`slice` and/or `int` objects.
If ``multi_dim_slice`` is a `tuple` of length 2, then then
``multi_dim_slice[0]`` specifies a set of output layer indices, where
each output layer index corresponds to a different output layer; and
``multi_dim_slice[1]`` specifies the vector component indices, where the
indices ``0`` and ``1`` would correspond to the :math:`x`- and
:math:`y`-components of the COM momentum respectively. In this case, the
current Python function will load the COM momentum patterns generated by
electrons exiting from the output layers specified by
``multi_dim_slice[0]``, for the vector components specified by
``multi_dim_slice[1]``. Note that
``prismatique.load.output_layer_depths(filename)[multi_dim_slice[0]]``
yields the depths of the specified output layers.
Otherwise, if ``multi_dim_slice`` is set to `None`, then all the COM
momentum patterns stored in the file are loaded.
use_two_axes_to_map_probe_position_if_possible : `bool`, optional
If ``use_two_axes_to_map_probe_position_if_possible`` is set to
``True``, and ``prismatique.load.scan_pattern_type(filename) ==
"rectangular grid"``, then two ``hyperspy`` axes are used rather than
one to map the probe position. In this case, the two axes have the same
dimensions as the rectangular grid on which the probe positions
lie. Otherwise, one ``hyperspy`` axis is used to map the probe
positions.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
com_momentum_signal : :class:`hyperspy._signals.signal2d.Signal2D`
The subcollection of COM momentum patterns, stored in a ``hyperspy``
signal: If two axes are used to map the probe position, then the STEM
intensity images are stored in an instance of the
:class:`hyperspy._signals.signal2d.Signal2D` class; otherwise they are
stored in an instance of the
:class:`hyperspy._signals.signal1d.Signal1D` class. See the
documentation and/or reference guide for the :mod:`hyperspy` package for
details on how to use instances of the
:class:`hyperspy._signals.signal1d.Signal1D` and
:class:`hyperspy._signals.signal2d.Signal2D` classes.
navigational_to_original_indices_map : `dict`
A dictionary that maps the navigational indices of the hyperspy signal
``com_momentum_signal`` to the original indices specified by
``multi_dim_slice``. For example, if the original output layer indices
indices map to a set of corresponding navigational indices, then
``navigational_to_original_indices_map["output_layer_indices"][i]``
yields the output layer index specified in the expression
``single_dim_slice=multi_dim_slice[0] if multi_dim_slice is not None
else slice(None)`` that corresponds to the ``i`` th atomic configuration
index in the nagivation index space of ``com_momentum_signal``, where
``i`` is a nonnegative integer smaller than the total number of output
layers specified in ``single_dim_slice``.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
result = _com_momenta(**kwargs)
com_momentum_signal, navigational_to_original_indices_map = result
return com_momentum_signal, navigational_to_original_indices_map
def _com_momenta(filename,
multi_dim_slice,
use_two_axes_to_map_probe_position_if_possible):
current_func_params = locals()
kwargs = \
{**current_func_params,
"indices_map_keys": \
("output_layer_indices", "vector_component_indices"),
"func_to_calc_sizes_of_navigational_space_axes": \
_sizes_of_navigational_space_axes_for_com_momenta,
"func_to_initialize_cbed_related_signal": \
_initialize_com_momentum_signal}
com_momentum_signal, navigational_to_original_indices_map = \
_cbed_related_signal_and_indices_map(**kwargs)
return com_momentum_signal, navigational_to_original_indices_map
def _sizes_of_navigational_space_axes_for_com_momenta(filename):
current_func_name = "_sizes_of_navigational_space_axes_for_com_momenta"
try:
path_in_file = "/data/center_of_mass_momentum"
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
sizes_of_navigational_space_axes = dataset.shape[:2]
dataset.file.close()
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename, current_func_name[38:])
raise IOError(err_msg)
return sizes_of_navigational_space_axes
def _initialize_com_momentum_signal(
filename,
multi_dim_slice,
indices_map,
indices_map_keys,
use_two_axes_to_map_probe_position_if_possible):
kwargs = \
{"filename": \
filename,
"use_two_axes_to_map_probe_position_if_possible": \
use_two_axes_to_map_probe_position_if_possible}
signal_space_axes_properties = \
_signal_space_axes_properties_for_com_momenta_or_stem_images(**kwargs)
kwargs.update(signal_space_axes_properties)
kwargs["multi_dim_slice"] = multi_dim_slice
kwargs["indices_map"] = indices_map
kwargs["indices_map_keys"] = indices_map_keys
kwargs["signal_dtype"] = "float32"
kwargs["signal_title"] = "Center of Mass Momentum"
kwargs["extra_kwargs"] = dict()
com_momentum_signal = _initialize_signal_for_generic_case(**kwargs)
return com_momentum_signal
def _signal_space_axes_properties_for_com_momenta_or_stem_images(
filename, use_two_axes_to_map_probe_position_if_possible):
scan_pattern_type = prismatique.load.scan_pattern_type(filename)
if ((scan_pattern_type == "rectangular grid")
and (use_two_axes_to_map_probe_position_if_possible)):
positions = probe_positions(filename, force_2_col_shape=False)
Y_dim, X_dim, _ = positions.shape
d_r_x = positions[0, 1, 0]-positions[0, 0, 0] if (X_dim > 1) else 1
d_r_y = positions[0, 0, 1]-positions[1, 0, 1] if (Y_dim > 1) else 1
signal_space_sizes = (X_dim, Y_dim)
signal_space_scales = (d_r_x, -d_r_y)
signal_space_offsets = (positions[0, 0, 0],
positions[0, 0, 1])
signal_space_units = ("Å", "Å")
else:
positions = probe_positions(filename, force_2_col_shape=True)
signal_space_sizes = (len(positions),)
signal_space_scales = (1,)
signal_space_offsets = (0,)
signal_space_units = ("dimensionless",)
signal_space_axes_properties = \
{"signal_space_sizes": signal_space_sizes,
"signal_space_scales": signal_space_scales,
"signal_space_offsets": signal_space_offsets,
"signal_space_units": signal_space_units}
return signal_space_axes_properties
[docs]def stem_intensity_images(
filename,
multi_dim_slice=\
_default_multi_dim_slice,
use_two_axes_to_map_probe_position_if_possible=\
_default_use_two_axes_to_map_probe_position_if_possible,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given STEM simulation output file that stores STEM intensity
images, load a specified subcollection of said STEM intensity images into a
``hyperspy`` signal.
See the documentation for the class :class:`prismatique.stem.output.Params`
for a discussion on STEM intensity images, i.e. 2D-STEM output.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the STEM intensity
images. Any non-temporary file generated by the function
:func:`prismatique.stem.sim.run`, with originally the basename
``"stem_sim_intensity_output.h5"`` is valid. See the documentation for
the class :class:`prismatique.stem.output.Params` for a discussion on
the layout and structuring of ``prismatique`` STEM simulation output
files.
multi_dim_slice : `tuple` (`int` | `slice` | `list` (`int`)) | `None`, optional
The "multidimensional slice object", which specifies the subcollection
of STEM intensity images to load from file. We define a
multi-dimensional slice object as a `tuple` of items which contains at
most one item being a `list` of integers, and the remaining items being
`slice` and/or `int` objects.
If ``multi_dim_slice`` is a `tuple` of length 1, then then
``multi_dim_slice[0]`` specifies a set of output layer indices, where
each output layer index corresponds to a different output layer. In this
case, the current Python function will load the STEM intensity images
generated by electrons exiting from the output layers specified by
``multi_dim_slice[0]``. Note that
``prismatique.load.output_layer_depths(filename)[multi_dim_slice[0]]``
yields the depths of the specified output layers.
Otherwise, if ``multi_dim_slice`` is set to `None`, then all the STEM
intensity images stored in the file are loaded.
use_two_axes_to_map_probe_position_if_possible : `bool`, optional
If ``use_two_axes_to_map_probe_position_if_possible`` is set to
``True``, and ``prismatique.load.scan_pattern_type(filename) ==
"rectangular grid"``, then two ``hyperspy`` axes are used rather than
one to map the probe position. In this case, the two axes have the same
dimensions as the rectangular grid on which the probe positions
lie. Otherwise, one ``hyperspy`` axis is used to map the probe
positions.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
stem_image_signal : :class:`hyperspy._signals.signal1d.Signal1D` | :class:`hyperspy._signals.signal2d.Signal2D`
The subcollection of STEM intensity images, stored in a ``hyperspy``
signal: If two axes are used to map the probe position, then the STEM
intensity images are stored in an instance of the
:class:`hyperspy._signals.signal2d.Signal2D` class; otherwise they are
stored in an instance of the
:class:`hyperspy._signals.signal1d.Signal1D` class. See the
documentation and/or reference guide for the :mod:`hyperspy` package for
details on how to use instances of the
:class:`hyperspy._signals.signal1d.Signal1D` and
:class:`hyperspy._signals.signal2d.Signal2D` classes.
navigational_to_original_indices_map : `dict`
A dictionary that maps the navigational indices of the hyperspy signal
``stem_image_signal`` to the original indices specified by
``multi_dim_slice``. For example, if the original output layer indices
map to a set of corresponding navigational indices, then
``navigational_to_original_indices_map["output_layer_indices"][i]``
yields the output layer index specified in the expression
``single_dim_slice=multi_dim_slice[0] if multi_dim_slice is not None
else slice(None)`` that corresponds to the ``i`` th atomic configuration
index in the nagivation index space of ``stem_image_signal``, where
``i`` is a nonnegative integer smaller than the total number of output
layer indices specified in ``single_dim_slice``.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
result = _stem_intensity_images(**kwargs)
stem_image_signal, navigational_to_original_indices_map = result
return stem_image_signal, navigational_to_original_indices_map
def _stem_intensity_images(filename,
multi_dim_slice,
use_two_axes_to_map_probe_position_if_possible):
current_func_params = locals()
kwargs = \
{**current_func_params,
"indices_map_keys": \
("output_layer_indices",),
"func_to_calc_sizes_of_navigational_space_axes": \
_sizes_of_navigational_space_axes_for_stem_intensity_images,
"func_to_initialize_cbed_related_signal": \
_initialize_stem_image_signal}
stem_image_signal, navigational_to_original_indices_map = \
_cbed_related_signal_and_indices_map(**kwargs)
return stem_image_signal, navigational_to_original_indices_map
def _sizes_of_navigational_space_axes_for_stem_intensity_images(filename):
current_func_name = ("_sizes_of_navigational_space_axes"
"_for_stem_intensity_images")
try:
path_in_file = "/data/2D_STEM/integrated_intensities"
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
sizes_of_navigational_space_axes = dataset.shape[:1]
dataset.file.close()
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename, current_func_name[38:])
raise IOError(err_msg)
return sizes_of_navigational_space_axes
def _initialize_stem_image_signal(
filename,
use_two_axes_to_map_probe_position_if_possible,
multi_dim_slice,
indices_map,
indices_map_keys):
kwargs = \
{"filename": \
filename,
"use_two_axes_to_map_probe_position_if_possible": \
use_two_axes_to_map_probe_position_if_possible}
signal_space_axes_properties = \
_signal_space_axes_properties_for_com_momenta_or_stem_images(**kwargs)
kwargs.update(signal_space_axes_properties)
kwargs["multi_dim_slice"] = multi_dim_slice
kwargs["indices_map"] = indices_map
kwargs["indices_map_keys"] = indices_map_keys
kwargs["signal_dtype"] = "float32"
kwargs["signal_title"] = "STEM Intensity Image"
kwargs["extra_kwargs"] = dict()
stem_image_signal = _initialize_signal_for_generic_case(**kwargs)
return stem_image_signal
[docs]def azimuthally_integrated_cbed_intensity_patterns(
filename,
multi_dim_slice=\
_default_multi_dim_slice,
use_two_axes_to_map_probe_position_if_possible=\
_default_use_two_axes_to_map_probe_position_if_possible,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given STEM simulation output file that stores azimuthally
integrated CBED intensity patterns, load a specified subcollection of said
integrated intensity patterns into a ``hyperspy`` signal.
See the documentation for the class :class:`prismatique.stem.output.Params`
for a discussion on azimuthally integrated CBED intensity patterns, i.e.
3D-STEM output.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the azimuthally
integrated CBED intensity patterns. Any non-temporary file generated by
the function :func:`prismatique.stem.sim.run`, with originally the
basename ``"stem_sim_intensity_output.h5"`` is valid. See the
documentation for the class :class:`prismatique.stem.output.Params` for
a discussion on the layout and structuring of ``prismatique`` STEM
simulation output files.
multi_dim_slice : `tuple` (`int` | `slice` | `list` (`int`)) | `None`, optional
The "multidimensional slice object", which specifies the subcollection
of azimuthally integrated CBED intensity patterns to load from file. We
define a multi-dimensional slice object as a `tuple` of items which
contains at most one item being a `list` of integers, and the remaining
items being `slice` and/or `int` objects.
If ``multi_dim_slice`` is a `tuple` of length 2, then then
``multi_dim_slice[0]`` specifies a set of output layer indices, where
each output layer index corresponds to a different output layer; and
``multi_dim_slice[1]`` specifies a set of probe indices, where each
probe index corresponds to a different probe position. In this case, the
current Python function will load the azimuthally integrated CBED
intensity patterns generated by electrons exiting from the output layers
specified by ``multi_dim_slice[0]``, the electrons of which coming from
probes centered at the positions specified by
``multi_dim_slice[1]``. Note that
``prismatique.load.output_layer_depths(filename)[multi_dim_slice[0]]``
yields the depths of the specified output layers; and
``prismatique.load.probe_positions(filename)[multi_dim_slice[1]]``
yields the specified probe positions.
Otherwise, if ``multi_dim_slice`` is set to `None`, then all the
azimuthally integrated CBED intensity patterns stored in the file are
loaded.
use_two_axes_to_map_probe_position_if_possible : `bool`, optional
If ``use_two_axes_to_map_probe_position_if_possible`` is set to
``True``, and ``prismatique.load.scan_pattern_type(filename) ==
"rectangular grid"``, then two ``hyperspy`` axes are used rather than
one to map the probe position. In this case, the two axes have the same
dimensions as the rectangular grid on which the probe positions
lie. Otherwise, one ``hyperspy`` axis is used to map the probe
positions.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
intensity_3d_stem_signal : :class:`hyperspy._signals.signal2d.Signal2D`
The subcollection of azimuthally integrated CBED intensity patterns,
stored in an instance of the
:class:`hyperspy._signals.signal2d.Signal2D` class. See the
documentation and/or reference guide for the :mod:`hyperspy` package for
details on how to use instances of the
:class:`hyperspy._signals.signal2d.Signal2D` class.
navigational_to_original_indices_map : `dict`
A dictionary that maps the navigational indices of the hyperspy signal
``intensity_3d_stem_signal`` to the original indices specified by
``multi_dim_slice``. For example, if the original output layer indices
map to a set of corresponding navigational indices, then
``navigational_to_original_indices_map["output_layer_indices"][i]``
yields the output layer index specified in the expression
``single_dim_slice=multi_dim_slice[0] if multi_dim_slice is not None
else slice(None)`` that corresponds to the ``i`` th atomic configuration
index in the nagivation index space of ``intensity_3d_stem_signal``,
where ``i`` is a nonnegative integer smaller than the total number of
output layer indices specified in ``single_dim_slice``.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
result = _azimuthally_integrated_cbed_intensity_patterns(**kwargs)
intensity_3d_stem_signal, navigational_to_original_indices_map = result
return intensity_3d_stem_signal, navigational_to_original_indices_map
def _azimuthally_integrated_cbed_intensity_patterns(
filename,
multi_dim_slice,
use_two_axes_to_map_probe_position_if_possible):
current_func_params = locals()
kwargs = \
{**current_func_params,
"indices_map_keys": \
("output_layer_indices", "probe_indices"),
"func_to_calc_sizes_of_navigational_space_axes": \
_sizes_of_navigational_space_axes_for_intensity_3d_stem,
"func_to_initialize_cbed_related_signal": \
_initialize_intensity_3d_stem_signal}
intensity_3d_stem_signal, navigational_to_original_indices_map = \
_cbed_related_signal_and_indices_map(**kwargs)
return intensity_3d_stem_signal, navigational_to_original_indices_map
def _sizes_of_navigational_space_axes_for_intensity_3d_stem(filename):
current_func_name = ("_sizes_of_navigational_space_axes"
"_for_intensity_3d_stem")
try:
path_in_file = "/data/3D_STEM/integrated_intensities"
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
sizes_of_navigational_space_axes = dataset.shape[:2]
dataset.file.close()
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
args = (filename, "azimuthally_integrated_cbed_intensity_patterns")
err_msg = unformatted_err_msg.format(*args)
raise IOError(err_msg)
return sizes_of_navigational_space_axes
def _initialize_intensity_3d_stem_signal(
filename,
multi_dim_slice,
indices_map,
indices_map_keys,
use_two_axes_to_map_probe_position_if_possible):
current_func_params = locals()
k_xy = k_xy_coords_of_3d_stem_output(filename)
kwargs = {**current_func_params,
"signal_space_sizes": (len(k_xy),),
"signal_space_scales": (k_xy[1]-k_xy[0],),
"signal_space_offsets": (0,),
"signal_space_units": ("1/Å",),
"signal_dtype": "float32",
"signal_title": "Azimuthally Integrated CBED Intensity Pattern",
"extra_kwargs": dict()}
intensity_3d_stem_signal = _initialize_signal_for_generic_case(**kwargs)
return intensity_3d_stem_signal
[docs]def hrtem_image_wavefunctions(filename,
multi_dim_slice=\
_default_multi_dim_slice,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given HRTEM simulation output file that stores HRTEM image
wavefunctions, load a specified subcollection of said HRTEM wavefunctions
into a ``hyperspy`` signal.
See the documentation for the class :class:`prismatique.hrtem.image.Params`
for a discussion on HRTEM image wavefunctions, which we denote as
:math:`\left|\psi_{t}\left(\delta_{f};\mathbf{u}_{1}, \ldots,\mathbf{u}_{N};
\boldsymbol{\delta}_{\beta}\right)\right\rangle` throughout the
documentation. See also the documentation for the class
:class:`prismatique.thermal.Params` for additional information and context
on said patterns.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the HRTEM image
wavefunctions. Any non-temporary file generated by the function
:func:`prismatique.stem.sim.run`, with originally a basename of the form
``"hrtem_sim_wavefunction_output_of_subset_"+str(i)+".h5"`` is valid,
where ``i`` is a nonnegative integer. See the documentation for the
class :class:`prismatique.hrtem.output.Params` for a discussion on the
layout and structuring of ``prismatique`` HRTEM simulation output files.
multi_dim_slice : `tuple` (`int` | `slice` | `list` (`int`)) | `None`, optional
The "multidimensional slice object", which specifies the subcollection
of HRTEM image wavefunctions to load from file. We define a
multi-dimensional slice object as a `tuple` of items which contains at
most one item being a `list` of integers, and the remaining items being
`slice` and/or `int` objects.
If ``multi_dim_slice`` is a `tuple` of length 3, then then
``multi_dim_slice[0]`` specifies a set of atomic configuration indices,
where each atomic configuration index corresponds to a frozen phonon
configuration; ``multi_dim_slice[1]`` specifies a set of defocus
indices, where each defocus index corresponds to a different beam
defocus; and ``multi_dim_slice[2]`` specifies a set of tilt indices,
where each tilt index corresponds to a different beam tilt. In this
case, the current Python function will load the HRTEM image
wavefunctions of the frozen phonon configurations specified by
``multi_dim_slice[0]``, generated by electron beams operating at the
defocii specified by ``multi_dim_slice[1]``, and beam tilts specified by
``multi_dim_slice[2]``. Note that
``prismatique.load.defocii(filename)[multi_dim_slice[1]]`` yields the
specified defocii; and
``prismatique.load.hrtem_beam_tilts(filename)[multi_dim_slice[2]]``
yields the specified tilts.
Otherwise, if ``multi_dim_slice`` is set to `None`, then all the HRTEM
image wavefunctions stored in the file are loaded.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
hrtem_image_wavefunction_signal : :class:`hyperspy._signals.complex_signal2d.ComplexSignal2D`
The subcollection of HRTEM image wavefunctions, stored in an instance of
the :class:`hyperspy._signals.complex_signal2d.ComplexSignal2D` class.
See the documentation and/or reference guide for the :hyperspy:`hyperspy
<>` package for details on how to use instances of the
:class:`hyperspy._signals.complex_signal2d.ComplexSignal2D` class.
navigational_to_original_indices_map : `dict`
A dictionary that maps the navigational indices of the hyperspy signal
``hrtem_image_wavefunction_signal`` to the original indices specified by
``multi_dim_slice``. For example, if the original atomic configuration
indices map to a set of corresponding navigational indices, then
``navigational_to_original_indices_map["output_layer_indices"][i]``
yields the atomic configuration index specified in the expression
``single_dim_slice=multi_dim_slice[0] if multi_dim_slice is not None
else slice(None)`` that corresponds to the ``i`` th atomic configuration
index in the nagivation index space of
``hrtem_image_wavefunction_signal``, where ``i`` is a nonnegative
integer smaller than the total number of atomic configuration indices
specified in ``single_dim_slice``.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
result = _hrtem_image_wavefunctions(**kwargs)
hrtem_image_wavefunction_signal = result[0]
navigational_to_original_indices_map = result[1]
return hrtem_image_wavefunction_signal, navigational_to_original_indices_map
def _hrtem_image_wavefunctions(filename, multi_dim_slice):
current_func_params = locals()
indices_map_keys = ("atomic_config_indices",
"defocus_indices",
"tilt_indices")
kwargs = \
{**current_func_params,
"indices_map_keys": \
indices_map_keys,
"func_to_calc_sizes_of_navigational_space_axes": \
_sizes_of_navigational_space_axes_for_hrtem_image_wavefunctions,
"func_to_initialize_hrtem_related_signal": \
_initialize_hrtem_image_wavefunction_signal}
hrtem_image_wavefunction_signal, navigational_to_original_indices_map = \
_hrtem_related_signal_and_indices_map(**kwargs)
return hrtem_image_wavefunction_signal, navigational_to_original_indices_map
def _sizes_of_navigational_space_axes_for_hrtem_image_wavefunctions(filename):
current_func_name = ("_sizes_of_navigational_space_axes"
"_for_hrtem_wavefunctions")
try:
path_in_file = "/data/image_wavefunctions"
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
sizes_of_navigational_space_axes = dataset.shape[:3]
dataset.file.close()
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename, current_func_name[38:])
raise IOError(err_msg)
return sizes_of_navigational_space_axes
def _initialize_hrtem_image_wavefunction_signal(filename,
multi_dim_slice,
indices_map,
indices_map_keys):
current_func_params = locals()
r_x = hrtem_image_x_coords(filename)
r_y = hrtem_image_y_coords(filename)
kwargs = \
{**current_func_params,
"signal_space_sizes": (len(r_x), len(r_y)),
"signal_space_scales": (r_x[1]-r_x[0], r_y[1]-r_y[0]),
"signal_space_offsets": (r_x[0], r_y[0]),
"signal_space_units": ("Å", "Å"),
"signal_dtype": "complex64",
"signal_title": "HRTEM Image Wavefunction",
"use_two_axes_to_map_probe_position_if_possible": False,
"extra_kwargs": dict()}
hrtem_image_wavefunction_signal = \
_initialize_signal_for_generic_case(**kwargs)
return hrtem_image_wavefunction_signal
def _hrtem_related_signal_and_indices_map(
filename,
multi_dim_slice,
indices_map_keys,
func_to_calc_sizes_of_navigational_space_axes,
func_to_initialize_hrtem_related_signal):
kwargs = \
{"filename": \
filename,
"multi_dim_slice": \
multi_dim_slice,
"indices_map_keys": \
indices_map_keys,
"func_to_calc_sizes_of_navigational_space_axes": \
func_to_calc_sizes_of_navigational_space_axes}
indices_map, multi_dim_slice = \
_indices_map_and_converted_multi_dim_slice(**kwargs)
kwargs = {"filename": filename,
"multi_dim_slice": multi_dim_slice,
"indices_map": indices_map,
"indices_map_keys": indices_map_keys}
hrtem_related_signal = func_to_initialize_hrtem_related_signal(**kwargs)
kwargs = {"filename": filename,
"multi_dim_slice": multi_dim_slice,
"hrtem_related_signal": hrtem_related_signal}
_load_data_into_hrtem_related_signal(**kwargs)
kwargs = {"indices_map": indices_map,
"filename": filename,
"multi_dim_slice": multi_dim_slice,
"use_two_axes_to_map_probe_position_if_possible": False,
"extra_kwargs": dict()}
_clean_indices_map(**kwargs)
return hrtem_related_signal, indices_map
def _load_data_into_hrtem_related_signal(filename,
multi_dim_slice,
hrtem_related_signal):
path_in_file = _path_in_file_to_hrtem_related_data(hrtem_related_signal)
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
dataset_rank = len(dataset.shape)
dataset.file.close()
kwargs = \
{"dataset_id": \
dataset_id,
"multi_dim_slice": \
multi_dim_slice + (slice(None),)*(dataset_rank-len(multi_dim_slice))}
datasubset_id = \
h5pywrappers.datasubset.ID(**kwargs)
current_func_name = "_load_data_into_hrtem_related_signal"
try:
datasubset = h5pywrappers.datasubset.load(datasubset_id)
hrtem_related_signal.data[:] = datasubset[:]
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(path_in_file, filename)
raise IOError(err_msg)
return None
def _path_in_file_to_hrtem_related_data(cbed_related_signal):
signal_title = cbed_related_signal.metadata.General.title
path_in_file = ("/data/image_wavefunctions"
if (signal_title == "HRTEM Image Wavefunction")
else "/data/intensity_image")
return path_in_file
[docs]def hrtem_intensity_image(filename,
skip_validation_and_conversion=\
_default_skip_validation_and_conversion):
r"""From a given HRTEM simulation output file that stores an HRTEM intensity
image, load said HRTEM intensity image into a ``hyperspy`` signal.
See the documentation for the class :class:`prismatique.hrtem.image.Params`
for a discussion on HRTEM intensity images.
Parameters
----------
filename : `str`
The relative or absolute path to the file containing the HRTEM intensity
image. Any non-temporary file generated by the function
:func:`prismatique.stem.sim.run`, with originally the basename
``"hrtem_sim_intensity_output.h5"`` is valid, where ``i`` is a
nonnegative integer. See the documentation for the class
:class:`prismatique.hrtem.output.Params` for a discussion on the layout
and structuring of ``prismatique`` HRTEM simulation output files.
skip_validation_and_conversion : `bool`, optional
If ``skip_validation_and_conversion`` is set to ``False``, then
validations and conversions are performed on the above
parameters.
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, no
validations and conversions are performed on the above parameters. This
option is desired primarily when the user wants to avoid potentially
expensive validation and/or conversion operations.
Returns
-------
hrtem_intensity_image_signal : :class:`hyperspy._signals.signal2d.Signal2D`
The HRTEM intensity image, stored in an instance of the
:class:`hyperspy._signals.signal2d.Signal2D` class. See the
documentation and/or reference guide for the :mod:`hyperspy` package for
details on how to use instances of the
:class:`hyperspy._signals.signal2d.Signal2D` class.
"""
params = locals()
func_alias = _check_and_convert_skip_validation_and_conversion
skip_validation_and_conversion = func_alias(params)
if (skip_validation_and_conversion == False):
global_symbol_table = globals()
for param_name in params:
if param_name == "skip_validation_and_conversion":
continue
func_name = "_check_and_convert_" + param_name
func_alias = global_symbol_table[func_name]
params[param_name] = func_alias(params)
kwargs = params
del kwargs["skip_validation_and_conversion"]
hrtem_intensity_image_signal = _hrtem_intensity_image(**kwargs)
return hrtem_intensity_image_signal
def _hrtem_intensity_image(filename):
kwargs = \
{"filename": \
filename,
"multi_dim_slice": \
tuple(),
"indices_map_keys": \
tuple(),
"func_to_calc_sizes_of_navigational_space_axes": \
_sizes_of_navigational_space_axes_for_hrtem_intensity_image,
"func_to_initialize_hrtem_related_signal": \
_initialize_hrtem_intensity_image_signal}
hrtem_intensity_image_signal, _ = \
_hrtem_related_signal_and_indices_map(**kwargs)
return hrtem_intensity_image_signal
def _sizes_of_navigational_space_axes_for_hrtem_intensity_image(filename):
current_func_name = ("_sizes_of_navigational_space_axes"
"_for_hrtem_intensity_image")
try:
path_in_file = "/data/intensity_image"
dataset_id = h5pywrappers.obj.ID(filename, path_in_file)
dataset = h5pywrappers.dataset.load(dataset_id, read_only=True)
sizes_of_navigational_space_axes = dataset.shape[:0]
dataset.file.close()
except:
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
err_msg = unformatted_err_msg.format(filename, current_func_name[38:])
raise IOError(err_msg)
return sizes_of_navigational_space_axes
def _initialize_hrtem_intensity_image_signal(filename,
multi_dim_slice,
indices_map,
indices_map_keys):
r_x = hrtem_image_x_coords(filename)
r_y = hrtem_image_y_coords(filename)
kwargs = {"filename": filename,
"multi_dim_slice": multi_dim_slice,
"indices_map": indices_map,
"indices_map_keys": indices_map_keys,
"signal_space_sizes": (len(r_x), len(r_y)),
"signal_space_scales": (r_x[1]-r_x[0], r_y[1]-r_y[0]),
"signal_space_offsets": (r_x[0], r_y[0]),
"signal_space_units": ("Å", "Å"),
"signal_dtype": "float32",
"signal_title": "HRTEM Intensity Image",
"use_two_axes_to_map_probe_position_if_possible": False,
"extra_kwargs": dict()}
hrtem_intensity_image_signal = _initialize_signal_for_generic_case(**kwargs)
return hrtem_intensity_image_signal
###########################
## Define error messages ##
###########################
_scan_pattern_type_err_msg_1 = \
("The object ``filename``, which stores the string ``'{}'``, specifies an "
"invalid filename: see the traceback for details and consult the "
"documentation for the function `prismatique.load.{}`.")
_grid_dims_in_units_of_probe_shifts_err_msg_1 = \
_scan_pattern_type_err_msg_1
_probe_positions_err_msg_1 = \
_scan_pattern_type_err_msg_1
_output_layer_depths_err_msg_1 = \
_scan_pattern_type_err_msg_1
_defocii_err_msg_1 = \
_scan_pattern_type_err_msg_1
_num_slices_err_msg_1 = \
_scan_pattern_type_err_msg_1
_num_frozen_phonon_configs_in_subset_err_msg_1 = \
("The HDF5 file ``'{}'`` does not store a dataset with the appropriate "
"dimensions at the HDF5 path '/data/4D_STEM/complex_valued_DPs', nor the "
"HDF5 path '/data/image_wavefunctions'.")
_num_frozen_phonon_configs_in_subset_err_msg_2 = \
_scan_pattern_type_err_msg_1
_cbed_k_x_coords_err_msg_1 = \
_scan_pattern_type_err_msg_1
_cbed_k_y_coords_err_msg_1 = \
_scan_pattern_type_err_msg_1
_k_xy_coords_of_3d_stem_output_err_msg_1 = \
_scan_pattern_type_err_msg_1
_integration_limits_of_2d_stem_output_err_msg_1 = \
("The loaded integration limits ``integration_limits`` must satisfy "
"``integration_limits[0] <= integration_limits[1]``.")
_integration_limits_of_2d_stem_output_err_msg_2 = \
_scan_pattern_type_err_msg_1
_S_matrix_k_xy_vectors_err_msg_1 = \
("The file ``'{}'`` stores the parameters for a STEM simulation using the "
"multislice algorithm, rather than the PRISM algorithm.")
_S_matrix_k_xy_vectors_err_msg_2 = \
_scan_pattern_type_err_msg_1
_hrtem_beam_tilts_err_msg_1 = \
_scan_pattern_type_err_msg_1
_hrtem_image_x_coords_err_msg_1 = \
_scan_pattern_type_err_msg_1
_hrtem_image_y_coords_err_msg_1 = \
_scan_pattern_type_err_msg_1
_sizes_of_navigational_space_axes_for_potential_slices_err_msg_1 = \
_scan_pattern_type_err_msg_1
_indices_map_and_converted_multi_dim_slice_err_msg_1 = \
("The object ``multi_dim_slice[{}]`` specifies a single-dimensional slice "
"that contains at least one index that is out of bounds: the "
"corresponding data axis is of size {}.")
_indices_map_and_converted_multi_dim_slice_err_msg_2 = \
("The object ``multi_dim_slice[{}]`` specifies a single-dimensional slice "
"that contains repeating indices after converting any negative indices to "
"their functionally equivalent positive values.")
_check_multi_dim_slice_length_err_msg_1 = \
("The object ``multi_dim_slice`` must be of type `NoneType` or a "
"'multidimensional slice object' represented as a sequence that satisfies "
"``len(multi_dim_slice)=={}``.")
_sizes_of_navigational_space_axes_for_S_matrix_wavefunctions_err_msg_1 = \
_scan_pattern_type_err_msg_1
_sizes_of_navigational_space_axes_for_cbed_wavefunctions_err_msg_1 = \
_scan_pattern_type_err_msg_1
_load_data_into_cbed_related_signal_err_msg_1 = \
("The HDF5 dataset at the HDF5 path ``'{}'`` of the HDF5 file at the file "
"path ``'{}'``, from which to load the desired ``hyperspy`` signal data, "
"does not have the expected/correct shape.")
_sizes_of_navigational_space_axes_for_cbed_intensity_patterns_err_msg_1 = \
_scan_pattern_type_err_msg_1
_sizes_of_navigational_space_axes_for_com_momenta_err_msg_1 = \
_scan_pattern_type_err_msg_1
_sizes_of_navigational_space_axes_for_stem_intensity_images_err_msg_1 = \
_scan_pattern_type_err_msg_1
_sizes_of_navigational_space_axes_for_intensity_3d_stem_err_msg_1 = \
_scan_pattern_type_err_msg_1
_sizes_of_navigational_space_axes_for_hrtem_wavefunctions_err_msg_1 = \
_scan_pattern_type_err_msg_1
_load_data_into_hrtem_related_signal_err_msg_1 = \
_load_data_into_cbed_related_signal_err_msg_1
_sizes_of_navigational_space_axes_for_hrtem_intensity_image_err_msg_1 = \
_scan_pattern_type_err_msg_1