Source code for prismatique.load

# -*- 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