class WulffSHT:
def __init__(self, facet_normals, facet_energies, labels=None,
l_max=10, sht_object=None, scale=1):
from chmpy.shape.sht import SHT
self.facet_normals = facet_normals
self.facet_energies = facet_energies
self.labels = labels
self.l_max = l_max
if self.labels is None:
self.labels = np.arange(len(facet_normals))
if sht_object is None:
sht = SHT(l_max)
else:
sht = sht_object
self.grid = np.array(sht.grid_cartesian)
dps = np.einsum('ijk,li->jkl', self.grid, self.facet_normals)
mask = (dps > 0)
intersections = np.inf * np.ones_like(dps)
intersections[mask] = 1.0 / dps[mask]
intersections *= scale * self.facet_energies
facet_idx = np.argmin(intersections, axis=2, keepdims=True)
self.distances = np.take_along_axis(intersections, facet_idx, axis=2).squeeze()
self.facet_idx = facet_idx.squeeze()
self.coeffs = sht.analysis(self.distances)
def invariants(self, **kwargs):
from chmpy.shape.shape_descriptors import expand_coeffs_to_full, make_invariants
coeff4inv = expand_coeffs_to_full(self.l_max, self.coeffs)
invariants = make_invariants(
self.l_max, coeff4inv, kinds=kwargs.get("kinds", "NP")
)
return invariants
def invariants_kazhdan(self, sht_object=None, **kwargs):
from chmpy.shape.sht import SHT
if sht_object is None:
sht = SHT(self.l_max)
else:
sht = sht_object
return sht.invariants_pure_python(self.coeffs)
def power_spectrum(self, sht_object=None, **kwargs):
from chmpy.shape.sht import SHT
if sht_object is None:
sht = SHT(self.l_max)
else:
sht = sht_object
return sht.power_spectrum(self.coeffs)
@classmethod
def from_gmf_and_crystal(cls, gmf, crystal, **kwargs):
hkl, energies = expand_symmetry_related_planes(gmf.hkl, gmf.energies, crystal.space_group)
facet_normals = hkl @ crystal.uc.reciprocal_lattice
facet_normals /= np.linalg.norm(facet_normals, axis=1)[:, None]
return cls(facet_normals, energies, labels=energies, **kwargs)
def __repr__(self):
return f"WulffSHT<l={self.l_max}>"