Source code for prismatique.stem.output.multislice

# -*- 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 specifying output parameters that are applicable only to the multislice
implementation of STEM simulations.

"""



#####################################
## Load libraries/packages/modules ##
#####################################

# For validating and converting objects.
import czekitout.check
import czekitout.convert

# For defining classes that support enforced validation, updatability,
# pre-serialization, and de-serialization.
import fancytypes



# For recycling helper functions and/or constants.
import prismatique.sample



##################################
## Define classes and functions ##
##################################

# List of public objects in objects.
__all__ = ["Params"]



def _check_and_convert_num_slices_per_output(params):
    obj_name = "num_slices_per_output"
    kwargs = {"obj": params[obj_name], "obj_name": obj_name}
    num_slices_per_output = czekitout.convert.to_positive_int(**kwargs)
    
    return num_slices_per_output



def _pre_serialize_num_slices_per_output(num_slices_per_output):
    obj_to_pre_serialize = num_slices_per_output
    serializable_rep = obj_to_pre_serialize

    return serializable_rep



def _de_pre_serialize_num_slices_per_output(serializable_rep):
    num_slices_per_output = serializable_rep

    return num_slices_per_output



def _check_and_convert_z_start_output(params):
    obj_name = "z_start_output"
    kwargs = {"obj": params[obj_name], "obj_name": obj_name}
    z_start_output = czekitout.convert.to_nonnegative_float(**kwargs)
    
    return z_start_output



def _pre_serialize_z_start_output(z_start_output):
    obj_to_pre_serialize = z_start_output
    serializable_rep = obj_to_pre_serialize

    return serializable_rep



def _de_pre_serialize_z_start_output(serializable_rep):
    z_start_output = serializable_rep

    return z_start_output



_module_alias = \
    prismatique.sample
_default_num_slices_per_output = \
    1
_default_z_start_output = \
    float("inf")
_default_skip_validation_and_conversion = \
    _module_alias._default_skip_validation_and_conversion



[docs]class Params(fancytypes.PreSerializableAndUpdatable): r"""The output parameters that are applicable only to the multislice implementation of STEM simulations. For a general discussion on the possible output that can be generated from STEM simulations, see the documentation for the class :class:`prismatique.stem.output.Params`. That discussion provides important context to the description of the parameter set below. Parameters ---------- num_slices_per_output : `int`, optional In the multislice algorithm, the sample is partitioned into slices along the :math:`z`-axis [i.e. the optic axis]. One can optionally save output for exit waves that emerge immediately after select intermediate slices, in addition to the output for the final exit wave. The output generated after a given slice is saved into an "output layer", i.e. a given output layer will contain the output for an exit wave that emerged from an intermediate slice or the final slice. Note that the exit wave emerging from the :math:`n^{\text{th}}` slice, where :math:`n=0` is the first slice, emerges at a depth of :math:`\left(n+1\right)\delta z`, where :math:`\delta z` is the slice thickness, given by Eq. :eq:`slice_thickness_in_potential_params`. ``num_slices_per_output`` specifies the number of slices between intermediate outputs. For example, if ``num_slices_per_output`` is set to ``1``, then the output from each slice is saved; if ``num_slices_per_output`` is set to ``2``, then the output from every second slice is saved; and so on. Note that the output for the exit wave that emerges from the final slice is always saved to the last output layer, irrespective of the value of ``num_slices_per_output``. Moreover, note that ``num_slices_per_output`` must be a positive `int`. z_start_output : `float`, optional Continuing from above, ``z_start_output`` specifies the depth in angstroms along the :math:`z`-axis at which intermediate output collection begins. Let ``m = int(math.ceil(z_start_output / (num_slices_per_output*slice_thickness))`` and ``n = max(1, m)*slices_per_output - 1``, where ``slice_thickness`` is the slice thickness :math:`\delta z` in angstroms. If ``n<=N_slice-1``, where ``N_slice`` is the total number of slices used to partition the sample, then intermediate output is stored with the output from the exit wave that emerges from the ``n`` th slice being saved into the first output layer. Note that the output for the exit wave that emerges from the final slice is always saved to the last output layer, irrespective of the value of ``z_start_output``. Note that ``z_start_output`` must be a nonnegative `float`. skip_validation_and_conversion : `bool`, optional Let ``validation_and_conversion_funcs`` and ``core_attrs`` denote the attributes :attr:`~fancytypes.Checkable.validation_and_conversion_funcs` and :attr:`~fancytypes.Checkable.core_attrs` respectively, both of which being `dict` objects. Let ``params_to_be_mapped_to_core_attrs`` denote the `dict` representation of the constructor parameters excluding the parameter ``skip_validation_and_conversion``, where each `dict` key ``key`` is a different constructor parameter name, excluding the name ``"skip_validation_and_conversion"``, and ``params_to_be_mapped_to_core_attrs[key]`` would yield the value of the constructor parameter with the name given by ``key``. If ``skip_validation_and_conversion`` is set to ``False``, then for each key ``key`` in ``params_to_be_mapped_to_core_attrs``, ``core_attrs[key]`` is set to ``validation_and_conversion_funcs[key] (params_to_be_mapped_to_core_attrs)``. Otherwise, if ``skip_validation_and_conversion`` is set to ``True``, then ``core_attrs`` is set to ``params_to_be_mapped_to_core_attrs.copy()``. This option is desired primarily when the user wants to avoid potentially expensive deep copies and/or conversions of the `dict` values of ``params_to_be_mapped_to_core_attrs``, as it is guaranteed that no copies or conversions are made in this case. """ ctor_param_names = ("num_slices_per_output", "z_start_output") kwargs = {"namespace_as_dict": globals(), "ctor_param_names": ctor_param_names} _validation_and_conversion_funcs_ = \ fancytypes.return_validation_and_conversion_funcs(**kwargs) _pre_serialization_funcs_ = \ fancytypes.return_pre_serialization_funcs(**kwargs) _de_pre_serialization_funcs_ = \ fancytypes.return_de_pre_serialization_funcs(**kwargs) del ctor_param_names, kwargs def __init__(self, num_slices_per_output=\ _default_num_slices_per_output, z_start_output=\ _default_z_start_output, skip_validation_and_conversion=\ _default_skip_validation_and_conversion): ctor_params = {key: val for key, val in locals().items() if (key not in ("self", "__class__"))} kwargs = ctor_params kwargs["skip_cls_tests"] = True fancytypes.PreSerializableAndUpdatable.__init__(self, **kwargs) return None
[docs] @classmethod def get_validation_and_conversion_funcs(cls): validation_and_conversion_funcs = \ cls._validation_and_conversion_funcs_.copy() return validation_and_conversion_funcs
[docs] @classmethod def get_pre_serialization_funcs(cls): pre_serialization_funcs = \ cls._pre_serialization_funcs_.copy() return pre_serialization_funcs
[docs] @classmethod def get_de_pre_serialization_funcs(cls): de_pre_serialization_funcs = \ cls._de_pre_serialization_funcs_.copy() return de_pre_serialization_funcs
def _check_and_convert_multislice_output_params(params): obj_name = "multislice_output_params" obj = params[obj_name] accepted_types = (Params, type(None)) if isinstance(obj, accepted_types[-1]): multislice_output_params = accepted_types[0]() else: kwargs = {"obj": obj, "obj_name": obj_name, "accepted_types": accepted_types} czekitout.check.if_instance_of_any_accepted_types(**kwargs) kwargs = obj.get_core_attrs(deep_copy=False) multislice_output_params = accepted_types[0](**kwargs) return multislice_output_params def _pre_serialize_multislice_output_params(multislice_output_params): obj_to_pre_serialize = multislice_output_params serializable_rep = obj_to_pre_serialize.pre_serialize() return serializable_rep def _de_pre_serialize_multislice_output_params(serializable_rep): multislice_output_params = Params.de_pre_serialize(serializable_rep) return multislice_output_params _default_multislice_output_params = None ########################### ## Define error messages ## ###########################