From c1bbb9103e0b621a0e2928a5f3ba5543f8099bce Mon Sep 17 00:00:00 2001 From: Steven Zeltmann Date: Wed, 1 Nov 2023 15:15:12 -0400 Subject: [PATCH 01/11] add robustness back to fit_origin --- py4DSTEM/braggvectors/braggvector_methods.py | 21 ++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/py4DSTEM/braggvectors/braggvector_methods.py b/py4DSTEM/braggvectors/braggvector_methods.py index 267f81e5f..932056608 100644 --- a/py4DSTEM/braggvectors/braggvector_methods.py +++ b/py4DSTEM/braggvectors/braggvector_methods.py @@ -552,14 +552,19 @@ def fit_origin( from py4DSTEM.process.calibration import fit_origin if mask_check_data is True: - # TODO - replace this bad hack for the mask for the origin fit - mask = np.logical_not(q_meas[0] == 0) - qx0_fit, qy0_fit, qx0_residuals, qy0_residuals = fit_origin( - tuple(q_meas), - mask=mask, - ) - else: - qx0_fit, qy0_fit, qx0_residuals, qy0_residuals = fit_origin(tuple(q_meas)) + data_mask = np.logical_not(q_meas[0] == 0) + if mask is None: + mask = data_mask + else: + mask = np.logical_and(mask, data_mask) + + qx0_fit, qy0_fit, qx0_residuals, qy0_residuals = fit_origin( + tuple(q_meas), + mask=mask, + robust=robust, + robust_steps=robust_steps, + robust_thresh=robust_thresh, + ) # try to add to calibration try: From fac36a7f99378713e9af691bb9f2991aa1c0db09 Mon Sep 17 00:00:00 2001 From: Steven Zeltmann Date: Wed, 1 Nov 2023 15:19:24 -0400 Subject: [PATCH 02/11] add fit function option back --- py4DSTEM/braggvectors/braggvector_methods.py | 1 + 1 file changed, 1 insertion(+) diff --git a/py4DSTEM/braggvectors/braggvector_methods.py b/py4DSTEM/braggvectors/braggvector_methods.py index 932056608..a47a242c5 100644 --- a/py4DSTEM/braggvectors/braggvector_methods.py +++ b/py4DSTEM/braggvectors/braggvector_methods.py @@ -561,6 +561,7 @@ def fit_origin( qx0_fit, qy0_fit, qx0_residuals, qy0_residuals = fit_origin( tuple(q_meas), mask=mask, + fitfunction=fitfunction, robust=robust, robust_steps=robust_steps, robust_thresh=robust_thresh, From 9f82c20fb4a44c158270c286c092d48fb220053e Mon Sep 17 00:00:00 2001 From: Stephanie Ribet Date: Wed, 1 Nov 2023 14:00:54 -0700 Subject: [PATCH 03/11] real space mask for positions to ignore --- py4DSTEM/process/phase/iterative_base_class.py | 15 ++++++++++++++- ...terative_mixedstate_multislice_ptychography.py | 6 +++++- .../phase/iterative_mixedstate_ptychography.py | 6 +++++- .../phase/iterative_multislice_ptychography.py | 6 +++++- .../iterative_overlap_magnetic_tomography.py | 6 +++++- .../process/phase/iterative_overlap_tomography.py | 6 +++++- .../phase/iterative_simultaneous_ptychography.py | 6 +++++- .../phase/iterative_singleslice_ptychography.py | 6 +++++- 8 files changed, 49 insertions(+), 8 deletions(-) diff --git a/py4DSTEM/process/phase/iterative_base_class.py b/py4DSTEM/process/phase/iterative_base_class.py index 13c64d79d..476216f79 100644 --- a/py4DSTEM/process/phase/iterative_base_class.py +++ b/py4DSTEM/process/phase/iterative_base_class.py @@ -1535,7 +1535,9 @@ def _set_polar_parameters(self, parameters: dict): else: raise ValueError("{} not a recognized parameter".format(symbol)) - def _calculate_scan_positions_in_pixels(self, positions: np.ndarray): + def _calculate_scan_positions_in_pixels( + self, positions: np.ndarray, positions_mask + ): """ Method to compute the initial guess of scan positions in pixels. @@ -1544,6 +1546,8 @@ def _calculate_scan_positions_in_pixels(self, positions: np.ndarray): positions: (J,2) np.ndarray or None Input probe positions in Å. If None, a raster scan using experimental parameters is constructed. + positions_mask: np.ndarray, optional + Boolean real space mask to select positions in datacube to skip for reconstruction Returns ------- @@ -1592,6 +1596,15 @@ def _calculate_scan_positions_in_pixels(self, positions: np.ndarray): positions = np.array([x.ravel(), y.ravel()]).T positions -= np.min(positions, axis=0) + if positions_mask is not None: + if positions_mask.dtype != "bool": + warnings.warn( + ("`positions_mask` converged to `bool` array"), + UserWarning, + ) + positions_mask = np.asarray(positions_mask, dtype="bool") + positions = positions[positions_mask.ravel()] + if self._object_padding_px is None: float_padding = self._region_of_interest_shape / 2 self._object_padding_px = (float_padding, float_padding) diff --git a/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py b/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py index 82155219a..98967ba89 100644 --- a/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py +++ b/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py @@ -85,6 +85,8 @@ class MixedstateMultislicePtychographicReconstruction(PtychographicReconstructio object_type: str, optional The object can be reconstructed as a real potential ('potential') or a complex object ('complex') + positions_mask: np.ndarray, optional + Boolean real space mask to select positions in datacube to skip for reconstruction verbose: bool, optional If True, class methods will inherit this and print additional information device: str, optional @@ -115,6 +117,7 @@ def __init__( initial_probe_guess: np.ndarray = None, initial_scan_positions: np.ndarray = None, object_type: str = "complex", + positions_mask: np.ndarray = None, verbose: bool = True, device: str = "cpu", name: str = "multi-slice_ptychographic_reconstruction", @@ -201,6 +204,7 @@ def __init__( self._semiangle_cutoff_pixels = semiangle_cutoff_pixels self._rolloff = rolloff self._object_type = object_type + self._positions_mask = positions_mask self._object_padding_px = object_padding_px self._verbose = verbose self._device = device @@ -454,7 +458,7 @@ def preprocess( del self._intensities self._positions_px = self._calculate_scan_positions_in_pixels( - self._scan_positions + self._scan_positions, self._positions_mask ) # handle semiangle specified in pixels diff --git a/py4DSTEM/process/phase/iterative_mixedstate_ptychography.py b/py4DSTEM/process/phase/iterative_mixedstate_ptychography.py index 25bee346c..195dace86 100644 --- a/py4DSTEM/process/phase/iterative_mixedstate_ptychography.py +++ b/py4DSTEM/process/phase/iterative_mixedstate_ptychography.py @@ -74,6 +74,8 @@ class MixedstatePtychographicReconstruction(PtychographicReconstruction): initial_scan_positions: np.ndarray, optional Probe positions in Å for each diffraction intensity If None, initialized to a grid scan + positions_mask: np.ndarray, optional + Boolean real space mask to select positions in datacube to skip for reconstruction verbose: bool, optional If True, class methods will inherit this and print additional information device: str, optional @@ -102,6 +104,7 @@ def __init__( initial_probe_guess: np.ndarray = None, initial_scan_positions: np.ndarray = None, object_type: str = "complex", + positions_mask: np.ndarray = None, verbose: bool = True, device: str = "cpu", name: str = "mixed-state_ptychographic_reconstruction", @@ -178,6 +181,7 @@ def __init__( self._rolloff = rolloff self._object_type = object_type self._object_padding_px = object_padding_px + self._positions_mask = positions_mask self._verbose = verbose self._device = device self._preprocessed = False @@ -358,7 +362,7 @@ def preprocess( del self._intensities self._positions_px = self._calculate_scan_positions_in_pixels( - self._scan_positions + self._scan_positions, self._positions_mask ) # handle semiangle specified in pixels diff --git a/py4DSTEM/process/phase/iterative_multislice_ptychography.py b/py4DSTEM/process/phase/iterative_multislice_ptychography.py index 6bcacd934..a137bbeb9 100644 --- a/py4DSTEM/process/phase/iterative_multislice_ptychography.py +++ b/py4DSTEM/process/phase/iterative_multislice_ptychography.py @@ -89,6 +89,8 @@ class MultislicePtychographicReconstruction(PtychographicReconstruction): object_type: str, optional The object can be reconstructed as a real potential ('potential') or a complex object ('complex') + positions_mask: np.ndarray, optional + Boolean real space mask to select positions in datacube to skip for reconstruction verbose: bool, optional If True, class methods will inherit this and print additional information device: str, optional @@ -121,6 +123,7 @@ def __init__( theta_y: float = 0, middle_focus: bool = False, object_type: str = "complex", + positions_mask: np.ndarray = None, verbose: bool = True, device: str = "cpu", name: str = "multi-slice_ptychographic_reconstruction", @@ -211,6 +214,7 @@ def __init__( self._semiangle_cutoff_pixels = semiangle_cutoff_pixels self._rolloff = rolloff self._object_type = object_type + self._positions_mask = positions_mask self._object_padding_px = object_padding_px self._verbose = verbose self._device = device @@ -481,7 +485,7 @@ def preprocess( del self._intensities self._positions_px = self._calculate_scan_positions_in_pixels( - self._scan_positions + self._scan_positions, self._positions_mask ) # handle semiangle specified in pixels diff --git a/py4DSTEM/process/phase/iterative_overlap_magnetic_tomography.py b/py4DSTEM/process/phase/iterative_overlap_magnetic_tomography.py index cde84907c..b4501d012 100644 --- a/py4DSTEM/process/phase/iterative_overlap_magnetic_tomography.py +++ b/py4DSTEM/process/phase/iterative_overlap_magnetic_tomography.py @@ -93,6 +93,8 @@ class OverlapMagneticTomographicReconstruction(PtychographicReconstruction): object_type: str, optional The object can be reconstructed as a real potential ('potential') or a complex object ('complex') + positions_mask: np.ndarray, optional + Boolean real space mask to select positions in datacube to skip for reconstruction name: str, optional Class name kwargs: @@ -115,6 +117,7 @@ def __init__( polar_parameters: Mapping[str, float] = None, object_padding_px: Tuple[int, int] = None, object_type: str = "potential", + positions_mask: np.ndarray = None, initial_object_guess: np.ndarray = None, initial_probe_guess: np.ndarray = None, initial_scan_positions: Sequence[np.ndarray] = None, @@ -179,6 +182,7 @@ def __init__( self._rolloff = rolloff self._object_type = object_type self._object_padding_px = object_padding_px + self._positions_mask = positions_mask self._verbose = verbose self._device = device self._preprocessed = False @@ -615,7 +619,7 @@ def preprocess( tilt_index + 1 ] ] = self._calculate_scan_positions_in_pixels( - self._scan_positions[tilt_index] + self._scan_positions[tilt_index], self._positions_mask[tilt_index] ) # handle semiangle specified in pixels diff --git a/py4DSTEM/process/phase/iterative_overlap_tomography.py b/py4DSTEM/process/phase/iterative_overlap_tomography.py index e92211301..759b12602 100644 --- a/py4DSTEM/process/phase/iterative_overlap_tomography.py +++ b/py4DSTEM/process/phase/iterative_overlap_tomography.py @@ -88,6 +88,8 @@ class OverlapTomographicReconstruction(PtychographicReconstruction): object_type: str, optional The object can be reconstructed as a real potential ('potential') or a complex object ('complex') + positions_mask: np.ndarray, optional + Boolean real space mask to select positions to ignore in reconstruction name: str, optional Class name kwargs: @@ -111,6 +113,7 @@ def __init__( polar_parameters: Mapping[str, float] = None, object_padding_px: Tuple[int, int] = None, object_type: str = "potential", + positions_mask: np.ndarray = None, initial_object_guess: np.ndarray = None, initial_probe_guess: np.ndarray = None, initial_scan_positions: Sequence[np.ndarray] = None, @@ -188,6 +191,7 @@ def __init__( self._rolloff = rolloff self._object_type = object_type self._object_padding_px = object_padding_px + self._positions_mask = positions_mask self._verbose = verbose self._device = device self._preprocessed = False @@ -555,7 +559,7 @@ def preprocess( tilt_index + 1 ] ] = self._calculate_scan_positions_in_pixels( - self._scan_positions[tilt_index] + self._scan_positions[tilt_index], self._positions_mask[tilt_index] ) # handle semiangle specified in pixels diff --git a/py4DSTEM/process/phase/iterative_simultaneous_ptychography.py b/py4DSTEM/process/phase/iterative_simultaneous_ptychography.py index 757b2ffae..35b2bb9ef 100644 --- a/py4DSTEM/process/phase/iterative_simultaneous_ptychography.py +++ b/py4DSTEM/process/phase/iterative_simultaneous_ptychography.py @@ -66,6 +66,8 @@ class SimultaneousPtychographicReconstruction(PtychographicReconstruction): object_padding_px: Tuple[int,int], optional Pixel dimensions to pad objects with If None, the padding is set to half the probe ROI dimensions + positions_mask: np.ndarray, optional + Boolean real space mask to select positions in datacube to skip for reconstruction initial_object_guess: np.ndarray, optional Initial guess for complex-valued object of dimensions (Px,Py) If None, initialized to 1.0j @@ -102,6 +104,7 @@ def __init__( vacuum_probe_intensity: np.ndarray = None, polar_parameters: Mapping[str, float] = None, object_padding_px: Tuple[int, int] = None, + positions_mask: np.ndarray = None, initial_object_guess: np.ndarray = None, initial_probe_guess: np.ndarray = None, initial_scan_positions: np.ndarray = None, @@ -167,6 +170,7 @@ def __init__( self._rolloff = rolloff self._object_type = object_type self._object_padding_px = object_padding_px + self._positions_mask = positions_mask self._verbose = verbose self._device = device self._preprocessed = False @@ -607,7 +611,7 @@ def preprocess( self._region_of_interest_shape = np.array(self._amplitudes[0].shape[-2:]) self._positions_px = self._calculate_scan_positions_in_pixels( - self._scan_positions + self._scan_positions, self._positions_mask ) # handle semiangle specified in pixels diff --git a/py4DSTEM/process/phase/iterative_singleslice_ptychography.py b/py4DSTEM/process/phase/iterative_singleslice_ptychography.py index 5dd19d7bd..8e66639b2 100644 --- a/py4DSTEM/process/phase/iterative_singleslice_ptychography.py +++ b/py4DSTEM/process/phase/iterative_singleslice_ptychography.py @@ -79,6 +79,8 @@ class SingleslicePtychographicReconstruction(PtychographicReconstruction): object_type: str, optional The object can be reconstructed as a real potential ('potential') or a complex object ('complex') + positions_mask: np.ndarray, optional + Boolean real space mask to select positions in datacube to skip for reconstruction name: str, optional Class name kwargs: @@ -102,6 +104,7 @@ def __init__( initial_scan_positions: np.ndarray = None, object_padding_px: Tuple[int, int] = None, object_type: str = "complex", + positions_mask: np.ndarray = None, verbose: bool = True, device: str = "cpu", name: str = "ptychographic_reconstruction", @@ -163,6 +166,7 @@ def __init__( self._rolloff = rolloff self._object_type = object_type self._object_padding_px = object_padding_px + self._positions_mask = positions_mask self._verbose = verbose self._device = device self._preprocessed = False @@ -342,7 +346,7 @@ def preprocess( del self._intensities self._positions_px = self._calculate_scan_positions_in_pixels( - self._scan_positions + self._scan_positions, self._positions_mask ) # handle semiangle specified in pixels From 67e15e7002234c0540cc9a69d8a6c60ff0d4c471 Mon Sep 17 00:00:00 2001 From: Stephanie Ribet Date: Wed, 1 Nov 2023 14:40:00 -0700 Subject: [PATCH 04/11] amplitudes update for real space mask --- .../process/phase/iterative_base_class.py | 12 +++++----- ...tive_mixedstate_multislice_ptychography.py | 13 +++++++++- .../iterative_mixedstate_ptychography.py | 12 +++++++++- .../iterative_multislice_ptychography.py | 12 +++++++++- .../iterative_overlap_magnetic_tomography.py | 13 +++++++++- .../phase/iterative_overlap_tomography.py | 13 +++++++++- .../iterative_simultaneous_ptychography.py | 24 ++++++++++++++++--- .../iterative_singleslice_ptychography.py | 13 +++++++++- 8 files changed, 97 insertions(+), 15 deletions(-) diff --git a/py4DSTEM/process/phase/iterative_base_class.py b/py4DSTEM/process/phase/iterative_base_class.py index 476216f79..73021d8a9 100644 --- a/py4DSTEM/process/phase/iterative_base_class.py +++ b/py4DSTEM/process/phase/iterative_base_class.py @@ -1132,6 +1132,7 @@ def _normalize_diffraction_intensities( com_fitted_x, com_fitted_y, crop_patterns, + positions_mask, ): """ Fix diffraction intensities CoM, shift to origin, and take square root @@ -1147,6 +1148,8 @@ def _normalize_diffraction_intensities( crop_patterns: bool if True, crop patterns to avoid wrap around of patterns when centering + positions_mask: np.ndarray, optional + Boolean real space mask to select positions in datacube to skip for reconstruction Returns ------- @@ -1220,6 +1223,9 @@ def _normalize_diffraction_intensities( amplitudes = xp.reshape(amplitudes, (-1,) + region_of_interest_shape) amplitudes = xp.asarray(amplitudes) + if positions_mask is not None: + amplitudes = amplitudes[positions_mask.ravel()] + mean_intensity /= amplitudes.shape[0] return amplitudes, mean_intensity @@ -1597,12 +1603,6 @@ def _calculate_scan_positions_in_pixels( positions -= np.min(positions, axis=0) if positions_mask is not None: - if positions_mask.dtype != "bool": - warnings.warn( - ("`positions_mask` converged to `bool` array"), - UserWarning, - ) - positions_mask = np.asarray(positions_mask, dtype="bool") positions = positions[positions_mask.ravel()] if self._object_padding_px is None: diff --git a/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py b/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py index 98967ba89..2915acccb 100644 --- a/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py +++ b/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py @@ -189,6 +189,13 @@ def __init__( f"object_type must be either 'potential' or 'complex', not {object_type}" ) + if positions_mask.dtype != "bool": + warnings.warn( + ("`positions_mask` converted to `bool` array"), + UserWarning, + ) + positions_mask = np.asarray(positions_mask, dtype="bool") + self.set_save_defaults() # Data @@ -449,7 +456,11 @@ def preprocess( self._amplitudes, self._mean_diffraction_intensity, ) = self._normalize_diffraction_intensities( - self._intensities, self._com_fitted_x, self._com_fitted_y, crop_patterns + self._intensities, + self._com_fitted_x, + self._com_fitted_y, + crop_patterns, + self._positions_mask, ) # explicitly delete namespace diff --git a/py4DSTEM/process/phase/iterative_mixedstate_ptychography.py b/py4DSTEM/process/phase/iterative_mixedstate_ptychography.py index 195dace86..01d70bf71 100644 --- a/py4DSTEM/process/phase/iterative_mixedstate_ptychography.py +++ b/py4DSTEM/process/phase/iterative_mixedstate_ptychography.py @@ -164,6 +164,12 @@ def __init__( raise ValueError( f"object_type must be either 'potential' or 'complex', not {object_type}" ) + if positions_mask.dtype != "bool": + warnings.warn( + ("`positions_mask` converted to `bool` array"), + UserWarning, + ) + positions_mask = np.asarray(positions_mask, dtype="bool") self.set_save_defaults() @@ -353,7 +359,11 @@ def preprocess( self._amplitudes, self._mean_diffraction_intensity, ) = self._normalize_diffraction_intensities( - self._intensities, self._com_fitted_x, self._com_fitted_y, crop_patterns + self._intensities, + self._com_fitted_x, + self._com_fitted_y, + crop_patterns, + self._positions_mask, ) # explicitly delete namespace diff --git a/py4DSTEM/process/phase/iterative_multislice_ptychography.py b/py4DSTEM/process/phase/iterative_multislice_ptychography.py index a137bbeb9..be24f067d 100644 --- a/py4DSTEM/process/phase/iterative_multislice_ptychography.py +++ b/py4DSTEM/process/phase/iterative_multislice_ptychography.py @@ -198,6 +198,12 @@ def __init__( raise ValueError( f"object_type must be either 'potential' or 'complex', not {object_type}" ) + if positions_mask.dtype != "bool": + warnings.warn( + ("`positions_mask` converted to `bool` array"), + UserWarning, + ) + positions_mask = np.asarray(positions_mask, dtype="bool") self.set_save_defaults() @@ -476,7 +482,11 @@ def preprocess( self._amplitudes, self._mean_diffraction_intensity, ) = self._normalize_diffraction_intensities( - self._intensities, self._com_fitted_x, self._com_fitted_y, crop_patterns + self._intensities, + self._com_fitted_x, + self._com_fitted_y, + crop_patterns, + self._positions_mask, ) # explicitly delete namespace diff --git a/py4DSTEM/process/phase/iterative_overlap_magnetic_tomography.py b/py4DSTEM/process/phase/iterative_overlap_magnetic_tomography.py index b4501d012..810352ce8 100644 --- a/py4DSTEM/process/phase/iterative_overlap_magnetic_tomography.py +++ b/py4DSTEM/process/phase/iterative_overlap_magnetic_tomography.py @@ -166,6 +166,13 @@ def __init__( if object_type != "potential": raise NotImplementedError() + if positions_mask.dtype != "bool": + warnings.warn( + ("`positions_mask` converted to `bool` array"), + UserWarning, + ) + positions_mask = np.asarray(positions_mask, dtype="bool") + self.set_save_defaults() # Data @@ -599,7 +606,11 @@ def preprocess( ], mean_diffraction_intensity_temp, ) = self._normalize_diffraction_intensities( - intensities, com_fitted_x, com_fitted_y, crop_patterns + intensities, + com_fitted_x, + com_fitted_y, + crop_patterns, + self._positions_mask[tilt_index], ) self._mean_diffraction_intensity.append(mean_diffraction_intensity_temp) diff --git a/py4DSTEM/process/phase/iterative_overlap_tomography.py b/py4DSTEM/process/phase/iterative_overlap_tomography.py index 759b12602..701267e81 100644 --- a/py4DSTEM/process/phase/iterative_overlap_tomography.py +++ b/py4DSTEM/process/phase/iterative_overlap_tomography.py @@ -175,6 +175,13 @@ def __init__( if object_type != "potential": raise NotImplementedError() + if positions_mask.dtype != "bool": + warnings.warn( + ("`positions_mask` converted to `bool` array"), + UserWarning, + ) + positions_mask = np.asarray(positions_mask, dtype="bool") + self.set_save_defaults() # Data @@ -539,7 +546,11 @@ def preprocess( ], mean_diffraction_intensity_temp, ) = self._normalize_diffraction_intensities( - intensities, com_fitted_x, com_fitted_y, crop_patterns + intensities, + com_fitted_x, + com_fitted_y, + crop_patterns, + self._positions_mask[tilt_index], ) self._mean_diffraction_intensity.append(mean_diffraction_intensity_temp) diff --git a/py4DSTEM/process/phase/iterative_simultaneous_ptychography.py b/py4DSTEM/process/phase/iterative_simultaneous_ptychography.py index 35b2bb9ef..ae1a3ecac 100644 --- a/py4DSTEM/process/phase/iterative_simultaneous_ptychography.py +++ b/py4DSTEM/process/phase/iterative_simultaneous_ptychography.py @@ -153,6 +153,12 @@ def __init__( raise ValueError( f"object_type must be either 'potential' or 'complex', not {object_type}" ) + if positions_mask.dtype != "bool": + warnings.warn( + ("`positions_mask` converted to `bool` array"), + UserWarning, + ) + positions_mask = np.asarray(positions_mask, dtype="bool") self.set_save_defaults() @@ -408,7 +414,11 @@ def preprocess( amplitudes_0, mean_diffraction_intensity_0, ) = self._normalize_diffraction_intensities( - intensities_0, com_fitted_x_0, com_fitted_y_0, crop_patterns + intensities_0, + com_fitted_x_0, + com_fitted_y_0, + crop_patterns, + self._positions_mask, ) # explicitly delete namescapes @@ -489,7 +499,11 @@ def preprocess( amplitudes_1, mean_diffraction_intensity_1, ) = self._normalize_diffraction_intensities( - intensities_1, com_fitted_x_1, com_fitted_y_1, crop_patterns + intensities_1, + com_fitted_x_1, + com_fitted_y_1, + crop_patterns, + self._positions_mask, ) # explicitly delete namescapes @@ -571,7 +585,11 @@ def preprocess( amplitudes_2, mean_diffraction_intensity_2, ) = self._normalize_diffraction_intensities( - intensities_2, com_fitted_x_2, com_fitted_y_2, crop_patterns + intensities_2, + com_fitted_x_2, + com_fitted_y_2, + crop_patterns, + self._positions_mask, ) # explicitly delete namescapes diff --git a/py4DSTEM/process/phase/iterative_singleslice_ptychography.py b/py4DSTEM/process/phase/iterative_singleslice_ptychography.py index 8e66639b2..ab16330da 100644 --- a/py4DSTEM/process/phase/iterative_singleslice_ptychography.py +++ b/py4DSTEM/process/phase/iterative_singleslice_ptychography.py @@ -150,6 +150,13 @@ def __init__( f"object_type must be either 'potential' or 'complex', not {object_type}" ) + if positions_mask.dtype != "bool": + warnings.warn( + ("`positions_mask` converted to `bool` array"), + UserWarning, + ) + positions_mask = np.asarray(positions_mask, dtype="bool") + self.set_save_defaults() # Data @@ -337,7 +344,11 @@ def preprocess( self._amplitudes, self._mean_diffraction_intensity, ) = self._normalize_diffraction_intensities( - self._intensities, self._com_fitted_x, self._com_fitted_y, crop_patterns + self._intensities, + self._com_fitted_x, + self._com_fitted_y, + crop_patterns, + self._positions_mask, ) # explicitly delete namespace From 2d48616c7e5a0e83ad2f038c97c35fb6d4ddad24 Mon Sep 17 00:00:00 2001 From: Stephanie Ribet Date: Wed, 1 Nov 2023 15:51:05 -0700 Subject: [PATCH 05/11] Thnks fr th Mmr(s) --- .../process/phase/iterative_base_class.py | 24 ++++++++++++------- ...tive_mixedstate_multislice_ptychography.py | 2 +- .../iterative_mixedstate_ptychography.py | 2 +- .../iterative_multislice_ptychography.py | 2 +- .../iterative_overlap_magnetic_tomography.py | 2 +- .../phase/iterative_overlap_tomography.py | 2 +- .../iterative_simultaneous_ptychography.py | 2 +- .../iterative_singleslice_ptychography.py | 2 +- 8 files changed, 23 insertions(+), 15 deletions(-) diff --git a/py4DSTEM/process/phase/iterative_base_class.py b/py4DSTEM/process/phase/iterative_base_class.py index 73021d8a9..497c7ae1c 100644 --- a/py4DSTEM/process/phase/iterative_base_class.py +++ b/py4DSTEM/process/phase/iterative_base_class.py @@ -1163,6 +1163,12 @@ def _normalize_diffraction_intensities( mean_intensity = 0 diffraction_intensities = self._asnumpy(diffraction_intensities) + if positions_mask is not None: + number_of_patterns = np.count_nonzero(self._positions_mask.ravel()) + sx, sy = np.where(~self._positions_mask) + else: + number_of_patterns = np.prod(diffraction_intensities.shape[:2]) + if crop_patterns: crop_x = int( np.minimum( @@ -1181,8 +1187,7 @@ def _normalize_diffraction_intensities( region_of_interest_shape = (crop_w * 2, crop_w * 2) amplitudes = np.zeros( ( - diffraction_intensities.shape[0], - diffraction_intensities.shape[1], + number_of_patterns, crop_w * 2, crop_w * 2, ), @@ -1198,13 +1203,19 @@ def _normalize_diffraction_intensities( else: region_of_interest_shape = diffraction_intensities.shape[-2:] - amplitudes = np.zeros(diffraction_intensities.shape, dtype=np.float32) + amplitudes = np.zeros( + (number_of_patterns,) + region_of_interest_shape, dtype=np.float32 + ) com_fitted_x = self._asnumpy(com_fitted_x) com_fitted_y = self._asnumpy(com_fitted_y) + counter = 0 for rx in range(diffraction_intensities.shape[0]): for ry in range(diffraction_intensities.shape[1]): + if positions_mask is not None: + if rx in sx and ry in sy: + continue intensities = get_shifted_ar( diffraction_intensities[rx, ry], -com_fitted_x[rx, ry], @@ -1219,13 +1230,10 @@ def _normalize_diffraction_intensities( ) mean_intensity += np.sum(intensities) - amplitudes[rx, ry] = np.sqrt(np.maximum(intensities, 0)) + amplitudes[counter] = np.sqrt(np.maximum(intensities, 0)) + counter += 1 - amplitudes = xp.reshape(amplitudes, (-1,) + region_of_interest_shape) amplitudes = xp.asarray(amplitudes) - if positions_mask is not None: - amplitudes = amplitudes[positions_mask.ravel()] - mean_intensity /= amplitudes.shape[0] return amplitudes, mean_intensity diff --git a/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py b/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py index 2915acccb..26b0d8cff 100644 --- a/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py +++ b/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py @@ -189,7 +189,7 @@ def __init__( f"object_type must be either 'potential' or 'complex', not {object_type}" ) - if positions_mask.dtype != "bool": + if positions_mask is not None and positions_mask.dtype != "bool": warnings.warn( ("`positions_mask` converted to `bool` array"), UserWarning, diff --git a/py4DSTEM/process/phase/iterative_mixedstate_ptychography.py b/py4DSTEM/process/phase/iterative_mixedstate_ptychography.py index 01d70bf71..ebc40928d 100644 --- a/py4DSTEM/process/phase/iterative_mixedstate_ptychography.py +++ b/py4DSTEM/process/phase/iterative_mixedstate_ptychography.py @@ -164,7 +164,7 @@ def __init__( raise ValueError( f"object_type must be either 'potential' or 'complex', not {object_type}" ) - if positions_mask.dtype != "bool": + if positions_mask is not None and positions_mask.dtype != "bool": warnings.warn( ("`positions_mask` converted to `bool` array"), UserWarning, diff --git a/py4DSTEM/process/phase/iterative_multislice_ptychography.py b/py4DSTEM/process/phase/iterative_multislice_ptychography.py index be24f067d..73f83558e 100644 --- a/py4DSTEM/process/phase/iterative_multislice_ptychography.py +++ b/py4DSTEM/process/phase/iterative_multislice_ptychography.py @@ -198,7 +198,7 @@ def __init__( raise ValueError( f"object_type must be either 'potential' or 'complex', not {object_type}" ) - if positions_mask.dtype != "bool": + if positions_mask is not None and positions_mask.dtype != "bool": warnings.warn( ("`positions_mask` converted to `bool` array"), UserWarning, diff --git a/py4DSTEM/process/phase/iterative_overlap_magnetic_tomography.py b/py4DSTEM/process/phase/iterative_overlap_magnetic_tomography.py index 810352ce8..582eea772 100644 --- a/py4DSTEM/process/phase/iterative_overlap_magnetic_tomography.py +++ b/py4DSTEM/process/phase/iterative_overlap_magnetic_tomography.py @@ -166,7 +166,7 @@ def __init__( if object_type != "potential": raise NotImplementedError() - if positions_mask.dtype != "bool": + if positions_mask is not None and positions_mask.dtype != "bool": warnings.warn( ("`positions_mask` converted to `bool` array"), UserWarning, diff --git a/py4DSTEM/process/phase/iterative_overlap_tomography.py b/py4DSTEM/process/phase/iterative_overlap_tomography.py index 701267e81..f4dfe5022 100644 --- a/py4DSTEM/process/phase/iterative_overlap_tomography.py +++ b/py4DSTEM/process/phase/iterative_overlap_tomography.py @@ -175,7 +175,7 @@ def __init__( if object_type != "potential": raise NotImplementedError() - if positions_mask.dtype != "bool": + if positions_mask is not None and positions_mask.dtype != "bool": warnings.warn( ("`positions_mask` converted to `bool` array"), UserWarning, diff --git a/py4DSTEM/process/phase/iterative_simultaneous_ptychography.py b/py4DSTEM/process/phase/iterative_simultaneous_ptychography.py index ae1a3ecac..866ff0a89 100644 --- a/py4DSTEM/process/phase/iterative_simultaneous_ptychography.py +++ b/py4DSTEM/process/phase/iterative_simultaneous_ptychography.py @@ -153,7 +153,7 @@ def __init__( raise ValueError( f"object_type must be either 'potential' or 'complex', not {object_type}" ) - if positions_mask.dtype != "bool": + if positions_mask is not None and positions_mask.dtype != "bool": warnings.warn( ("`positions_mask` converted to `bool` array"), UserWarning, diff --git a/py4DSTEM/process/phase/iterative_singleslice_ptychography.py b/py4DSTEM/process/phase/iterative_singleslice_ptychography.py index ab16330da..350d0a3cb 100644 --- a/py4DSTEM/process/phase/iterative_singleslice_ptychography.py +++ b/py4DSTEM/process/phase/iterative_singleslice_ptychography.py @@ -150,7 +150,7 @@ def __init__( f"object_type must be either 'potential' or 'complex', not {object_type}" ) - if positions_mask.dtype != "bool": + if positions_mask is not None and positions_mask.dtype != "bool": warnings.warn( ("`positions_mask` converted to `bool` array"), UserWarning, From 3da6fdc3cda11baba1289abbd167ffa2d42627e5 Mon Sep 17 00:00:00 2001 From: Stephanie Ribet Date: Wed, 1 Nov 2023 16:32:24 -0700 Subject: [PATCH 06/11] one more bug --- py4DSTEM/process/phase/iterative_base_class.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/py4DSTEM/process/phase/iterative_base_class.py b/py4DSTEM/process/phase/iterative_base_class.py index 497c7ae1c..1aa03559a 100644 --- a/py4DSTEM/process/phase/iterative_base_class.py +++ b/py4DSTEM/process/phase/iterative_base_class.py @@ -1165,7 +1165,6 @@ def _normalize_diffraction_intensities( diffraction_intensities = self._asnumpy(diffraction_intensities) if positions_mask is not None: number_of_patterns = np.count_nonzero(self._positions_mask.ravel()) - sx, sy = np.where(~self._positions_mask) else: number_of_patterns = np.prod(diffraction_intensities.shape[:2]) @@ -1214,7 +1213,7 @@ def _normalize_diffraction_intensities( for rx in range(diffraction_intensities.shape[0]): for ry in range(diffraction_intensities.shape[1]): if positions_mask is not None: - if rx in sx and ry in sy: + if not self._positions_mask[rx,ry]: continue intensities = get_shifted_ar( diffraction_intensities[rx, ry], From 7a4e7a43e926c48aa0643b60bb1d0202e8aa65ea Mon Sep 17 00:00:00 2001 From: Stephanie Ribet Date: Wed, 1 Nov 2023 16:34:08 -0700 Subject: [PATCH 07/11] black format --- py4DSTEM/process/phase/iterative_base_class.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py4DSTEM/process/phase/iterative_base_class.py b/py4DSTEM/process/phase/iterative_base_class.py index 1aa03559a..7437679a2 100644 --- a/py4DSTEM/process/phase/iterative_base_class.py +++ b/py4DSTEM/process/phase/iterative_base_class.py @@ -1213,7 +1213,7 @@ def _normalize_diffraction_intensities( for rx in range(diffraction_intensities.shape[0]): for ry in range(diffraction_intensities.shape[1]): if positions_mask is not None: - if not self._positions_mask[rx,ry]: + if not self._positions_mask[rx, ry]: continue intensities = get_shifted_ar( diffraction_intensities[rx, ry], From eabd74257d553caf633e7dae2ecd1bd535e9c84f Mon Sep 17 00:00:00 2001 From: Stephanie Ribet Date: Thu, 2 Nov 2023 11:39:43 -0700 Subject: [PATCH 08/11] I've been plotting to update this function --- .../process/phase/iterative_base_class.py | 6 -- ...tive_mixedstate_multislice_ptychography.py | 70 ++++++++++++++++++- .../iterative_multislice_ptychography.py | 34 +++++++-- .../iterative_overlap_magnetic_tomography.py | 6 -- .../phase/iterative_overlap_tomography.py | 6 -- 5 files changed, 96 insertions(+), 26 deletions(-) diff --git a/py4DSTEM/process/phase/iterative_base_class.py b/py4DSTEM/process/phase/iterative_base_class.py index 7437679a2..56be2784a 100644 --- a/py4DSTEM/process/phase/iterative_base_class.py +++ b/py4DSTEM/process/phase/iterative_base_class.py @@ -2306,22 +2306,16 @@ def show_object_fft(self, obj=None, **kwargs): figsize = kwargs.pop("figsize", (6, 6)) cmap = kwargs.pop("cmap", "magma") - vmin = kwargs.pop("vmin", 0) - vmax = kwargs.pop("vmax", 1) - power = kwargs.pop("power", 0.2) pixelsize = 1 / (object_fft.shape[1] * self.sampling[1]) show( object_fft, figsize=figsize, cmap=cmap, - vmin=vmin, - vmax=vmax, scalebar=True, pixelsize=pixelsize, ticks=False, pixelunits=r"$\AA^{-1}$", - power=power, **kwargs, ) diff --git a/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py b/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py index 26b0d8cff..6cbdca19e 100644 --- a/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py +++ b/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py @@ -82,6 +82,12 @@ class MixedstateMultislicePtychographicReconstruction(PtychographicReconstructio initial_scan_positions: np.ndarray, optional Probe positions in Å for each diffraction intensity If None, initialized to a grid scan + theta_x: float + x tilt of propagator (in degrees) + theta_y: float + y tilt of propagator (in degrees) + middle_focus: bool + if True, adds half the sample thickness to the defocus object_type: str, optional The object can be reconstructed as a real potential ('potential') or a complex object ('complex') @@ -116,6 +122,9 @@ def __init__( initial_object_guess: np.ndarray = None, initial_probe_guess: np.ndarray = None, initial_scan_positions: np.ndarray = None, + theta_x: float = 0, + theta_y: float = 0, + middle_focus: bool = False, object_type: str = "complex", positions_mask: np.ndarray = None, verbose: bool = True, @@ -165,6 +174,25 @@ def __init__( if (key not in polar_symbols) and (key not in polar_aliases.keys()): raise ValueError("{} not a recognized parameter".format(key)) + if np.isscalar(slice_thicknesses): + mean_slice_thickness = slice_thicknesses + else: + mean_slice_thickness = np.mean(slice_thicknesses) + + if middle_focus: + if "defocus" in kwargs: + kwargs["defocus"] += mean_slice_thickness * num_slices / 2 + elif "C10" in kwargs: + kwargs["C10"] -= mean_slice_thickness * num_slices / 2 + elif polar_parameters is not None and "defocus" in polar_parameters: + polar_parameters["defocus"] = ( + polar_parameters["defocus"] + mean_slice_thickness * num_slices / 2 + ) + elif polar_parameters is not None and "C10" in polar_parameters: + polar_parameters["C10"] = ( + polar_parameters["C10"] - mean_slice_thickness * num_slices / 2 + ) + self._polar_parameters = dict(zip(polar_symbols, [0.0] * len(polar_symbols))) if polar_parameters is None: @@ -221,6 +249,8 @@ def __init__( self._num_probes = num_probes self._num_slices = num_slices self._slice_thicknesses = slice_thicknesses + self._theta_x = theta_x + self._theta_y = theta_y def _precompute_propagator_arrays( self, @@ -243,6 +273,10 @@ def _precompute_propagator_arrays( The electron energy of the wave functions in eV slice_thicknesses: Sequence[float] Array of slice thicknesses in A + theta_x: float + x tilt of propagator (in degrees) + theta_y: float + y tilt of propagator (in degrees) Returns ------- @@ -262,6 +296,10 @@ def _precompute_propagator_arrays( propagators = xp.empty( (num_slices, kx.shape[0], ky.shape[0]), dtype=xp.complex64 ) + + theta_x = np.deg2rad(theta_x) + theta_y = np.deg2rad(theta_y) + for i, dz in enumerate(slice_thicknesses): propagators[i] = xp.exp( 1.0j * (-(kx**2)[:, None] * np.pi * wavelength * dz) @@ -269,6 +307,12 @@ def _precompute_propagator_arrays( propagators[i] *= xp.exp( 1.0j * (-(ky**2)[None] * np.pi * wavelength * dz) ) + propagators[i] *= xp.exp( + 1.0j * (2 * kx[:, None] * np.pi * dz * np.tan(theta_x)) + ) + propagators[i] *= xp.exp( + 1.0j * (2 * ky[None] * np.pi * dz * np.tan(theta_y)) + ) return propagators @@ -3075,6 +3119,7 @@ def show_slices( common_color_scale: bool = True, padding: int = 0, num_cols: int = 3, + show_fft: bool = False, **kwargs, ): """ @@ -3090,12 +3135,20 @@ def show_slices( Padding to leave uncropped num_cols: int, optional Number of GridSpec columns + show_fft: bool, optional + if True, plots fft of object slices """ if ms_object is None: ms_object = self._object rotated_object = self._crop_rotate_object_fov(ms_object, padding=padding) + if show_fft: + rotated_object = np.abs( + np.fft.fftshift( + np.fft.fft2(rotated_object, axes=(-2, -1)), axes=(-2, -1) + ) + ) rotated_shape = rotated_object.shape if np.iscomplexobj(rotated_object): @@ -3113,8 +3166,21 @@ def show_slices( axsize = kwargs.pop("axsize", (3, 3)) cmap = kwargs.pop("cmap", "magma") - vmin = np.min(rotated_object) if common_color_scale else None - vmax = np.max(rotated_object) if common_color_scale else None + + if common_color_scale: + vals = np.sort(rotated_object.ravel()) + ind_vmin = np.round((vals.shape[0] - 1) * 0.02).astype("int") + ind_vmax = np.round((vals.shape[0] - 1) * 0.98).astype("int") + ind_vmin = np.max([0, ind_vmin]) + ind_vmax = np.min([len(vals) - 1, ind_vmax]) + vmin = vals[ind_vmin] + vmax = vals[ind_vmax] + if vmax == vmin: + vmin = vals[0] + vmax = vals[-1] + else: + vmax = None + vmin = None vmin = kwargs.pop("vmin", vmin) vmax = kwargs.pop("vmax", vmax) diff --git a/py4DSTEM/process/phase/iterative_multislice_ptychography.py b/py4DSTEM/process/phase/iterative_multislice_ptychography.py index 73f83558e..4b0d6881c 100644 --- a/py4DSTEM/process/phase/iterative_multislice_ptychography.py +++ b/py4DSTEM/process/phase/iterative_multislice_ptychography.py @@ -81,9 +81,9 @@ class MultislicePtychographicReconstruction(PtychographicReconstruction): Probe positions in Å for each diffraction intensity If None, initialized to a grid scan theta_x: float - x tilt of propagator (in angles) + x tilt of propagator (in degrees) theta_y: float - y tilt of propagator (in angles) + y tilt of propagator (in degrees) middle_focus: bool if True, adds half the sample thickness to the defocus object_type: str, optional @@ -256,9 +256,9 @@ def _precompute_propagator_arrays( slice_thicknesses: Sequence[float] Array of slice thicknesses in A theta_x: float - x tilt of propagator (in angles) + x tilt of propagator (in degrees) theta_y: float - y tilt of propagator (in angles) + y tilt of propagator (in degrees) Returns ------- @@ -2955,6 +2955,7 @@ def show_slices( common_color_scale: bool = True, padding: int = 0, num_cols: int = 3, + show_fft: bool = False, **kwargs, ): """ @@ -2970,12 +2971,20 @@ def show_slices( Padding to leave uncropped num_cols: int, optional Number of GridSpec columns + show_fft: bool, optional + if True, plots fft of object slices """ if ms_object is None: ms_object = self._object rotated_object = self._crop_rotate_object_fov(ms_object, padding=padding) + if show_fft: + rotated_object = np.abs( + np.fft.fftshift( + np.fft.fft2(rotated_object, axes=(-2, -1)), axes=(-2, -1) + ) + ) rotated_shape = rotated_object.shape if np.iscomplexobj(rotated_object): @@ -2993,8 +3002,21 @@ def show_slices( axsize = kwargs.pop("axsize", (3, 3)) cmap = kwargs.pop("cmap", "magma") - vmin = np.min(rotated_object) if common_color_scale else None - vmax = np.max(rotated_object) if common_color_scale else None + + if common_color_scale: + vals = np.sort(rotated_object.mean(0).ravel()) + ind_vmin = np.round((vals.shape[0] - 1) * 0.02).astype("int") + ind_vmax = np.round((vals.shape[0] - 1) * 0.98).astype("int") + ind_vmin = np.max([0, ind_vmin]) + ind_vmax = np.min([len(vals) - 1, ind_vmax]) + vmin = vals[ind_vmin] + vmax = vals[ind_vmax] + if vmax == vmin: + vmin = vals[0] + vmax = vals[-1] + else: + vmax = None + vmin = None vmin = kwargs.pop("vmin", vmin) vmax = kwargs.pop("vmax", vmax) diff --git a/py4DSTEM/process/phase/iterative_overlap_magnetic_tomography.py b/py4DSTEM/process/phase/iterative_overlap_magnetic_tomography.py index 582eea772..7c96cb34c 100644 --- a/py4DSTEM/process/phase/iterative_overlap_magnetic_tomography.py +++ b/py4DSTEM/process/phase/iterative_overlap_magnetic_tomography.py @@ -3303,22 +3303,16 @@ def show_object_fft( figsize = kwargs.pop("figsize", (6, 6)) cmap = kwargs.pop("cmap", "magma") - vmin = kwargs.pop("vmin", 0) - vmax = kwargs.pop("vmax", 1) - power = kwargs.pop("power", 0.2) pixelsize = 1 / (object_fft.shape[1] * self.sampling[1]) show( object_fft, figsize=figsize, cmap=cmap, - vmin=vmin, - vmax=vmax, scalebar=True, pixelsize=pixelsize, ticks=False, pixelunits=r"$\AA^{-1}$", - power=power, **kwargs, ) diff --git a/py4DSTEM/process/phase/iterative_overlap_tomography.py b/py4DSTEM/process/phase/iterative_overlap_tomography.py index f4dfe5022..54b94010a 100644 --- a/py4DSTEM/process/phase/iterative_overlap_tomography.py +++ b/py4DSTEM/process/phase/iterative_overlap_tomography.py @@ -3183,22 +3183,16 @@ def show_object_fft( figsize = kwargs.pop("figsize", (6, 6)) cmap = kwargs.pop("cmap", "magma") - vmin = kwargs.pop("vmin", 0) - vmax = kwargs.pop("vmax", 1) - power = kwargs.pop("power", 0.2) pixelsize = 1 / (object_fft.shape[1] * self.sampling[1]) show( object_fft, figsize=figsize, cmap=cmap, - vmin=vmin, - vmax=vmax, scalebar=True, pixelsize=pixelsize, ticks=False, pixelunits=r"$\AA^{-1}$", - power=power, **kwargs, ) From 3a6ee5a80c70cd9cf4c9d94b662c01bb82df8ce7 Mon Sep 17 00:00:00 2001 From: Stephanie Ribet Date: Thu, 2 Nov 2023 11:46:23 -0700 Subject: [PATCH 09/11] correct propagation of arguments --- .../phase/iterative_mixedstate_multislice_ptychography.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py b/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py index 6cbdca19e..6cd74828e 100644 --- a/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py +++ b/py4DSTEM/process/phase/iterative_mixedstate_multislice_ptychography.py @@ -258,6 +258,8 @@ def _precompute_propagator_arrays( sampling: Tuple[float, float], energy: float, slice_thicknesses: Sequence[float], + theta_x: float, + theta_y: float, ): """ Precomputes propagator arrays complex wave-function will be convolved by, @@ -656,6 +658,8 @@ def preprocess( self.sampling, self._energy, self._slice_thicknesses, + self._theta_x, + self._theta_y, ) # overlaps From a51594c80b9c22344760ab287b3d8f2a36492cb0 Mon Sep 17 00:00:00 2001 From: Stephanie Ribet Date: Thu, 2 Nov 2023 11:56:39 -0700 Subject: [PATCH 10/11] one more bug fix --- py4DSTEM/process/phase/iterative_multislice_ptychography.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py4DSTEM/process/phase/iterative_multislice_ptychography.py b/py4DSTEM/process/phase/iterative_multislice_ptychography.py index 4b0d6881c..764f0b4a0 100644 --- a/py4DSTEM/process/phase/iterative_multislice_ptychography.py +++ b/py4DSTEM/process/phase/iterative_multislice_ptychography.py @@ -3004,7 +3004,7 @@ def show_slices( cmap = kwargs.pop("cmap", "magma") if common_color_scale: - vals = np.sort(rotated_object.mean(0).ravel()) + vals = np.sort(rotated_object.ravel()) ind_vmin = np.round((vals.shape[0] - 1) * 0.02).astype("int") ind_vmax = np.round((vals.shape[0] - 1) * 0.98).astype("int") ind_vmin = np.max([0, ind_vmin]) From 8323bc8f30d35a98de32e7521bfc3616a0d95706 Mon Sep 17 00:00:00 2001 From: Stephanie Ribet Date: Thu, 2 Nov 2023 17:51:30 -0700 Subject: [PATCH 11/11] fft hanning window --- py4DSTEM/visualize/show.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/py4DSTEM/visualize/show.py b/py4DSTEM/visualize/show.py index 4e99c0de5..b6077c412 100644 --- a/py4DSTEM/visualize/show.py +++ b/py4DSTEM/visualize/show.py @@ -366,7 +366,9 @@ def show( from py4DSTEM.visualize import show if show_fft: - ar = np.abs(np.fft.fftshift(np.fft.fft2(ar.copy()))) + n0 = ar.shape + w0 = np.hanning(n0[1]) * np.hanning(n0[0])[:, None] + ar = np.abs(np.fft.fftshift(np.fft.fft2(w0 * ar.copy()))) for a0 in range(num_images): im = show( ar[a0],