Skip to content

Commit 757af92

Browse files
authored
Monoclinic Symmetry Handling Fix (#4404)
* Fix monoclinic symmetry handling (with alpha = 90 degrees), and reduce redundant code * Add test for monoclinic symmetry handling with alpha = 90 degrees * Update kpath test value * Readability and edge case updates
1 parent a5ef4b4 commit 757af92

File tree

2 files changed

+57
-56
lines changed

2 files changed

+57
-56
lines changed

src/pymatgen/symmetry/analyzer.py

Lines changed: 40 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -771,95 +771,79 @@ def get_conventional_standard_structure(
771771
latt2 = Lattice([m[tp2[0]], m[tp2[1]], m[2]])
772772
lengths = latt2.lengths
773773
angles = latt2.angles
774-
if angles[0] > 90:
775-
# if the angle is > 90 we invert a and b to get
776-
# an angle < 90
777-
a, b, c, alpha, beta, gamma = Lattice([-m[tp2[0]], -m[tp2[1]], m[2]]).parameters
778-
transf = np.zeros(shape=(3, 3))
774+
alpha_degrees = angles[0]
775+
if alpha_degrees == 90:
776+
continue
777+
778+
transf = np.zeros(shape=(3, 3))
779+
transf[2][2] = 1
780+
781+
if alpha_degrees > 90:
782+
# if the angle is > 90 we invert a and b to get an angle < 90
783+
a, b, c, alpha_degrees, beta, gamma = Lattice([-m[tp2[0]], -m[tp2[1]], m[2]]).parameters
779784
transf[0][tp2[0]] = -1
780785
transf[1][tp2[1]] = -1
781-
transf[2][2] = 1
782-
alpha = math.pi * alpha / 180
783-
new_matrix = [
784-
[a, 0, 0],
785-
[0, b, 0],
786-
[0, c * cos(alpha), c * sin(alpha)],
787-
]
788-
continue
789786

790-
if angles[0] < 90:
791-
transf = np.zeros(shape=(3, 3))
787+
elif alpha_degrees < 90: # otherwise no inversion
792788
transf[0][tp2[0]] = 1
793789
transf[1][tp2[1]] = 1
794-
transf[2][2] = 1
795790
a, b, c = lengths
796-
alpha = math.pi * angles[0] / 180
797-
new_matrix = [
798-
[a, 0, 0],
799-
[0, b, 0],
800-
[0, c * cos(alpha), c * sin(alpha)],
801-
]
791+
alpha_degrees = angles[0]
792+
793+
alpha_radians = math.pi * alpha_degrees / 180
794+
new_matrix = [
795+
[a, 0, 0],
796+
[0, b, 0],
797+
[0, c * cos(alpha_radians), c * sin(alpha_radians)],
798+
]
802799

803800
if new_matrix is None:
804-
# this if is to treat the case
805-
# where alpha==90 (but we still have a monoclinic sg
801+
# this if is to treat the case where alpha==90 (but we still have a monoclinic sg), with C unchanged
802+
# transf is already defined as [[0,0,0],[0,0,0],[0,0,1]], and sorted_dic only contains a & b:
806803
new_matrix = [[a, 0, 0], [0, b, 0], [0, 0, c]]
807-
transf = np.zeros(shape=(3, 3))
808-
transf[2] = [0, 0, 1] # see issue #1929
809804
for idx, dct in enumerate(sorted_dic):
810805
transf[idx][dct["orig_index"]] = 1
806+
811807
# if not C-setting
812808
else:
813809
# try all permutations of the axis
814-
# keep the ones with the non-90 angle=alpha
815-
# and b<c
810+
# keep the ones with the non-90 angle=alpha and b<c
816811
new_matrix = None
817812

818813
for tp3 in itertools.permutations(list(range(3)), 3):
819814
m = lattice.matrix
820815
a, b, c, alpha, beta, gamma = Lattice([m[tp3[0]], m[tp3[1]], m[tp3[2]]]).parameters
816+
if alpha == 90 or b >= c:
817+
continue
818+
transf = np.zeros(shape=(3, 3))
819+
transf[2][tp3[2]] = 1
820+
821821
if alpha > 90 and b < c:
822822
a, b, c, alpha, beta, gamma = Lattice([-m[tp3[0]], -m[tp3[1]], m[tp3[2]]]).parameters
823-
transf = np.zeros(shape=(3, 3))
824823
transf[0][tp3[0]] = -1
825824
transf[1][tp3[1]] = -1
826-
transf[2][tp3[2]] = 1
827-
alpha = math.pi * alpha / 180
828-
new_matrix = [
829-
[a, 0, 0],
830-
[0, b, 0],
831-
[0, c * cos(alpha), c * sin(alpha)],
832-
]
833-
continue
834825

835-
if alpha < 90 and b < c:
836-
transf = np.zeros(shape=(3, 3))
826+
elif alpha < 90 and b < c:
837827
transf[0][tp3[0]] = 1
838828
transf[1][tp3[1]] = 1
839-
transf[2][tp3[2]] = 1
840-
alpha = math.pi * alpha / 180
841-
new_matrix = [
842-
[a, 0, 0],
843-
[0, b, 0],
844-
[0, c * cos(alpha), c * sin(alpha)],
845-
]
846829

847-
if new_matrix is None:
848-
# this if is to treat the case
849-
# where alpha==90 (but we still have a monoclinic sg
830+
alpha = math.pi * alpha / 180
850831
new_matrix = [
851-
[sorted_lengths[0], 0, 0],
852-
[0, sorted_lengths[1], 0],
853-
[0, 0, sorted_lengths[2]],
832+
[a, 0, 0],
833+
[0, b, 0],
834+
[0, c * cos(alpha), c * sin(alpha)],
854835
]
855-
transf = np.zeros(shape=(3, 3))
856-
for idx, dct in enumerate(sorted_dic):
836+
837+
if new_matrix is None:
838+
# this if is to treat the case where alpha==90 (but we still have a monoclinic sg)
839+
new_matrix = list(np.zeros(shape=(3, 3)))
840+
for idx, dct in enumerate(sorted_dic): # vectors sorted by length (a<=b<=c)
841+
new_matrix[idx] = dct["vec"]
857842
transf[idx][dct["orig_index"]] = 1
858843

859844
if international_monoclinic:
860845
# The above code makes alpha the non-right angle.
861-
# The following will convert to proper international convention
862-
# that beta is the non-right angle.
846+
# The following will convert to proper international convention that beta is the non-right angle
863847
op = [[0, 1, 0], [1, 0, 0], [0, 0, -1]]
864848
transf = np.dot(op, transf)
865849
new_matrix = np.dot(op, new_matrix)

tests/symmetry/test_analyzer.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,23 @@ def test_get_conventional_standard_structure(self):
379379
conventional = spga.get_conventional_standard_structure(keep_site_properties=False)
380380
assert conventional.site_properties.get("magmom") is None
381381

382+
mono_struct = Structure(
383+
lattice=np.array(
384+
[
385+
[-0.00000000e00, -0.00000000e00, -6.43473000e00],
386+
[3.78586000e00, 6.30170000e00, -0.00000000e00],
387+
[3.78586000e00, -6.30170000e00, -1.77635684e-15],
388+
]
389+
),
390+
species=["O"] * 3,
391+
coords=np.array([[0.5, 0.5, 0.66666667], [0.5, 0.0, 0.0], [0.5, 0.0, 0.33333333]]),
392+
)
393+
sga = SpacegroupAnalyzer(mono_struct)
394+
assert abs(np.linalg.det(sga.get_symmetry_dataset().primitive_lattice)) == approx(307.03126)
395+
assert abs(np.linalg.det(sga.get_primitive_standard_structure().lattice.matrix)) == approx(307.03126)
396+
assert abs(np.linalg.det(sga.get_conventional_standard_structure().lattice.matrix)) == approx(307.03126)
397+
assert abs(np.linalg.det(sga.get_refined_structure().lattice.matrix)) == approx(307.03126)
398+
382399
def test_get_primitive_standard_structure(self):
383400
for file_name, expected_angles, expected_abc in [
384401
("bcc_1927.cif", [109.47122063400001] * 3, [7.9657251015812145] * 3),

0 commit comments

Comments
 (0)