From bea9b1c82d3d3e880284a1ac03c6825489cae213 Mon Sep 17 00:00:00 2001 From: Patrick Avery Date: Mon, 8 Jan 2024 19:34:44 -0600 Subject: [PATCH] Lazily compute structure factor Instead of computing the structure factor every time a property of the material changes, flag the structure factor as invalid, and only re-compute it if it is requested. This significantly speeds up interaction with the lattice parameter, such as with the PT sliders. Signed-off-by: Patrick Avery --- hexrd/material/crystallography.py | 24 ++++++++++++++++++++++++ hexrd/material/material.py | 14 ++++++-------- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/hexrd/material/crystallography.py b/hexrd/material/crystallography.py index ee186b364..d3d5c63c4 100644 --- a/hexrd/material/crystallography.py +++ b/hexrd/material/crystallography.py @@ -700,6 +700,9 @@ def __init__(self, raise RuntimeError('have unparsed keyword arguments with keys: ' + str(list(kwargs.keys()))) + # This is only used to calculate the structure factor if invalidated + self.__unitcell = None + self.__calc() return @@ -935,7 +938,27 @@ def set_wavelength(self, wavelength): wavelength = property(get_wavelength, set_wavelength, None) + def invalidate_structure_factor(self, unitcell): + # It can be expensive to compute the structure factor, so provide the + # option to just invalidate it, while providing a unit cell, so that + # it can be lazily computed from the unit cell. + self.__structFact = None + self._powder_intensity = None + self.__unitcell = unitcell + + def _compute_sf_if_needed(self): + any_invalid = ( + self.__structFact is None or + self._powder_intensity is None + ) + if any_invalid and self.__unitcell is not None: + # Compute the structure factor first. + # This can be expensive to do, so we lazily compute it when needed. + hkls = self.getHKLs(allHKLs=True) + self.set_structFact(self.__unitcell.CalcXRSF(hkls)) + def get_structFact(self): + self._compute_sf_if_needed() return self.__structFact[~self.exclusions] def set_structFact(self, structFact): @@ -953,6 +976,7 @@ def set_structFact(self, structFact): @property def powder_intensity(self): + self._compute_sf_if_needed() return self._powder_intensity[~self.exclusions] @staticmethod diff --git a/hexrd/material/material.py b/hexrd/material/material.py index abb139f08..ffd775248 100644 --- a/hexrd/material/material.py +++ b/hexrd/material/material.py @@ -226,7 +226,7 @@ def __init__( self.reset_v0() self._newPdata() - self.update_structure_factor() + self.invalidate_structure_factor() def __str__(self): """String representation""" @@ -291,7 +291,7 @@ def _newUnitcell(self): def _hkls_changed(self): # Call this when something happens that changes the hkls... self._newPdata() - self.update_structure_factor() + self.invalidate_structure_factor() def _newPdata(self): """Create a new plane data instance if the hkls have changed""" @@ -405,10 +405,8 @@ def enable_hkls_below_tth(self, tth_threshold=90.0): self._pData.exclusions = dflt_excl - def update_structure_factor(self): - hkls = self.planeData.getHKLs(allHKLs=True) - sf = self.unitcell.CalcXRSF(hkls) - self.planeData.set_structFact(sf) + def invalidate_structure_factor(self): + self.planeData.invalidate_structure_factor(self.unitcell) def compute_powder_overlay( self, ttharray=np.linspace(0, 80, 2000), fwhm=0.25, scale=1.0 @@ -1268,7 +1266,7 @@ def charge(self, vals): self._charge = vals # self._newUnitcell() - # self.update_structure_factor() + # self.invalidate_structure_factor() @property def absorption_length(self): @@ -1390,7 +1388,7 @@ def _set_atomdata(self, atomtype, atominfo, U, charge): self.charge = charge self._newUnitcell() - self.update_structure_factor() + self.invalidate_structure_factor() # # ========== Methods