Skip to content

Commit 221d2fd

Browse files
steps-reshyuep
andauthored
fix(diffraction): correct reciprocal lattice cross terms in get_interplanar_angle (#4685)
get_interplanar_angle computes each plane's reciprocal vector norm and their dot product using the standard triclinic metric formulas. The k*l cross term in both r1_norm and r2_norm used cos_gamma_star instead of cos_alpha_star, and the h*l cross term in the dot product indexed p1[1] instead of p1[2]. Since a, b, c* worth of terms cancel out whenever two of the lattice angles are 90 degrees, this was invisible on the existing cubic/tetragonal/hexagonal test fixtures and only shows up for genuinely triclinic or low-symmetry monoclinic structures, where it silently returns the wrong angle. Added a regression test against an independently computed reference using the crystallographic reciprocal lattice directly on a triclinic cell with three distinct angles. Signed-off-by: Mike German <mike@stepsventures.com> Co-authored-by: Shyue Ping Ong <shyuep@users.noreply.github.com>
1 parent 777b616 commit 221d2fd

2 files changed

Lines changed: 27 additions & 3 deletions

File tree

src/pymatgen/analysis/diffraction/tem.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -408,22 +408,22 @@ def get_interplanar_angle(structure: Structure, p1: tuple[int, int, int], p2: tu
408408
+ p1[2] ** 2 * c_star**2
409409
+ 2 * p1[0] * p1[1] * a_star * b_star * cos_gamma_star
410410
+ 2 * p1[0] * p1[2] * a_star * c_star * cos_beta_star
411-
+ 2 * p1[1] * p1[2] * b_star * c_star * cos_gamma_star
411+
+ 2 * p1[1] * p1[2] * b_star * c_star * cos_alpha_star
412412
)
413413
r2_norm = np.sqrt(
414414
p2[0] ** 2 * a_star**2
415415
+ p2[1] ** 2 * b_star**2
416416
+ p2[2] ** 2 * c_star**2
417417
+ 2 * p2[0] * p2[1] * a_star * b_star * cos_gamma_star
418418
+ 2 * p2[0] * p2[2] * a_star * c_star * cos_beta_star
419-
+ 2 * p2[1] * p2[2] * b_star * c_star * cos_gamma_star
419+
+ 2 * p2[1] * p2[2] * b_star * c_star * cos_alpha_star
420420
)
421421
r1_dot_r2 = (
422422
p1[0] * p2[0] * a_star**2
423423
+ p1[1] * p2[1] * b_star**2
424424
+ p1[2] * p2[2] * c_star**2
425425
+ (p1[0] * p2[1] + p2[0] * p1[1]) * a_star * b_star * cos_gamma_star
426-
+ (p1[0] * p2[2] + p2[0] * p1[1]) * a_star * c_star * cos_beta_star
426+
+ (p1[0] * p2[2] + p2[0] * p1[2]) * a_star * c_star * cos_beta_star
427427
+ (p1[1] * p2[2] + p2[1] * p1[2]) * b_star * c_star * cos_alpha_star
428428
)
429429
phi = np.arccos(r1_dot_r2 / (r1_norm * r2_norm))

tests/analysis/diffraction/test_tem.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,30 @@ def test_interplanar_angle(self):
217217
phi = tem_calc.get_interplanar_angle(hexagonal, (0, 0, 1), (1, 0, 6))
218218
assert phi == approx(21.0517, rel=1e-4)
219219

220+
def test_interplanar_angle_triclinic(self):
221+
# Regression test: get_interplanar_angle's reciprocal-lattice-vector
222+
# norm and dot-product formulas had copy-paste errors (cos_gamma_star
223+
# used instead of cos_alpha_star in the k*l cross term of each norm,
224+
# and p1[1] used instead of p1[2] in the h*l cross term of the dot
225+
# product). These only show up when alpha/beta/gamma are all
226+
# different from 90 degrees and from each other, which none of the
227+
# cubic/tetragonal/hexagonal cases above exercise (their cos_alpha_star
228+
# / cos_beta_star terms are zero, hiding the bug). Compare against an
229+
# independent calculation using the crystallographic reciprocal
230+
# lattice directly.
231+
tem_calc = TEMCalculator()
232+
lattice = Lattice.from_parameters(4.0, 5.0, 6.0, 80, 95, 70)
233+
struct = Structure(lattice, ["Si"], [[0, 0, 0]])
234+
p1, p2 = (1, 1, 1), (1, 0, 2)
235+
236+
recip = lattice.reciprocal_lattice_crystallographic
237+
v1, v2 = recip.get_cartesian_coords(p1), recip.get_cartesian_coords(p2)
238+
cos_phi_expected = np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))
239+
phi_expected = np.rad2deg(np.arccos(cos_phi_expected))
240+
241+
phi = tem_calc.get_interplanar_angle(struct, p1, p2)
242+
assert phi == approx(phi_expected, rel=1e-6)
243+
220244
def test_get_plot_coeffs(self):
221245
# Test if x * p1 + y * p2 yields p3.
222246
tem_calc = TEMCalculator()

0 commit comments

Comments
 (0)