"""
This module contains various crystalographics classifications,
and the LatticeData object, that provides various informations about a lattice.
"""
from collections import namedtuple
from ..common.decorators import cached_property
import numpy as np
from ase2sprkkr.bindings.spglib import spglib_dataset
from ase.units import Bohr
from scipy.optimize import linear_sum_assignment
from typing import Union
[docs]
def reorder_matrix(target, source):
# normalize vectors (rows)
src_norm = source / np.linalg.norm(source, axis=1, keepdims=True)
tgt_norm = target / np.linalg.norm(target, axis=1, keepdims=True)
# compute similarity matrix (cosine similarity)
sim = tgt_norm @ src_norm.T # shape (3,3)
# convert to cost matrix (maximize similarity -> minimize negative similarity)
cost = -sim
# Hungarian algorithm
row_ind, col_ind = linear_sum_assignment(cost)
# reorder target rows to best match source
reordered = target[row_ind[np.argsort(col_ind)]]
return reordered
# Example
S = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]], dtype=float)
T = np.array([[0, 0, 2], [3, 0, 0], [0, 5, 0]], dtype=float)
[docs]
class Pearson(
namedtuple(
"Pearson",
[
"pearson_symbol",
"bravais_number",
"family",
"centering_type",
"herman_mauguin",
"shoenflies", # Shoenflies
],
)
):
"""This class holds informations about translation symmetry of a lattice with a given
Pearson symbol."""
[docs]
def xband_data(self):
"""Data used by Xband sysfile and SPRKKR to describe translation symmetry."""
return self[1:]
[docs]
@staticmethod
def from_symbol(pearson_symbol: str):
"""Return the Pearson symbol (describing the translation symmetry) that corresponds to the
given Pearson symbol"""
return Pearson.pearsons[Pearson.normalize_symbol(pearson_symbol)]
[docs]
@staticmethod
def normalize_symbol(pearson_symbol):
"""Normalize a Pearson symbol to its cannonical shape"""
if pearson_symbol[1] in ("A", "B", "C"):
pearson_symbol = pearson_symbol[0] + "S"
return pearson_symbol
pearson = None
""" Mapping of all possible Pearson symbols to Pearson objects"""
Pearson.pearsons = {
i.pearson_symbol: i
for i in [
Pearson("aP", 1, "triclinic", "primitive", "-1", "C_i"),
Pearson("mP", 2, "monoclinic", "primitive", "2/m", "C_2h"),
Pearson("mS", 3, "monoclinic", "primitive", "2/m", "C_2h"),
Pearson("oP", 4, "orthorombic", "primitive", "mmm", "D_2h"),
Pearson("oS", 5, "orthorombic", "base-centered", "mmm", "D_2h"),
Pearson("oI", 6, "orthorombic", "body-centered", "mmm", "D_2h"),
Pearson("oF", 7, "orthorombic", "face-centered", "mmm", "D_2h"),
Pearson("tP", 8, "tetragonal", "primitive", "4/mmm", "D_4h"),
Pearson("tI", 9, "tetragonal", "body-centered", "4/mmm", "D_4h"),
Pearson("hR", 10, "trigonal", "primitive", "-3m", "D_3d"),
Pearson("hP", 11, "hexagonal", "primitive", "6/mmm", "D_6h"),
Pearson("cP", 12, "cubic", "primitive", "m3m", "O_h"),
Pearson("cF", 13, "cubic", "face-centered", "m3m", "O_h"),
Pearson("cI", 14, "cubic", "body-centered", "m3m", "O_h"),
]
}
#: Translation of international tables numbers to A. Perlovs numbering.
international_numbers_to_AP = {
1: 1,
2: 2,
3: 3,
4: 5,
5: 7,
6: 13,
7: 15,
8: 21,
9: 27,
10: 39,
11: 41,
12: 43,
13: 50,
14: 55,
15: 61,
16: 73,
17: 74,
18: 77,
19: 80,
20: 81,
21: 84,
22: 87,
23: 88,
24: 89,
25: 90,
26: 93,
27: 99,
28: 102,
29: 108,
30: 114,
31: 120,
32: 126,
33: 129,
34: 135,
35: 138,
36: 145,
37: 147,
38: 150,
39: 156,
40: 162,
41: 168,
42: 174,
43: 177,
44: 180,
45: 183,
46: 186,
47: 192,
48: 193,
49: 195,
50: 198,
51: 204,
52: 210,
53: 216,
54: 222,
55: 228,
56: 231,
57: 234,
58: 240,
59: 247,
60: 249,
61: 255,
62: 257,
63: 263,
64: 269,
65: 275,
66: 278,
67: 281,
68: 287,
69: 299,
70: 300,
71: 302,
72: 303,
73: 306,
74: 308,
75: 314,
76: 315,
77: 316,
78: 317,
79: 318,
80: 319,
81: 320,
82: 321,
83: 322,
84: 323,
85: 324,
86: 326,
87: 328,
88: 329,
89: 331,
90: 332,
91: 333,
92: 334,
93: 335,
94: 336,
95: 337,
96: 338,
97: 339,
98: 340,
99: 341,
100: 342,
101: 343,
102: 344,
103: 345,
104: 346,
105: 347,
106: 348,
107: 349,
108: 350,
109: 351,
110: 352,
111: 353,
112: 354,
113: 355,
114: 356,
115: 357,
116: 358,
117: 359,
118: 360,
119: 361,
120: 362,
121: 363,
122: 364,
123: 365,
124: 366,
125: 368,
126: 370,
127: 371,
128: 372,
129: 374,
130: 376,
131: 377,
132: 378,
133: 380,
134: 382,
135: 383,
136: 384,
137: 386,
138: 388,
139: 389,
140: 390,
141: 392,
142: 394,
143: 396,
144: 398,
145: 400,
146: 401,
147: 404,
148: 405,
149: 407,
150: 408,
151: 409,
152: 410,
153: 411,
154: 412,
155: 414,
156: 415,
157: 416,
158: 417,
159: 418,
160: 419,
161: 422,
162: 423,
163: 424,
164: 425,
165: 426,
166: 428,
167: 430,
168: 431,
169: 432,
170: 433,
171: 434,
172: 435,
173: 436,
174: 437,
175: 438,
176: 439,
177: 440,
178: 441,
179: 442,
180: 443,
181: 444,
182: 445,
183: 446,
184: 447,
185: 448,
186: 449,
187: 450,
188: 451,
189: 452,
190: 453,
191: 454,
192: 455,
193: 456,
194: 457,
195: 458,
196: 459,
197: 460,
198: 461,
199: 462,
200: 463,
201: 465,
202: 466,
203: 468,
204: 469,
205: 470,
206: 471,
207: 472,
208: 473,
209: 474,
210: 475,
211: 476,
212: 477,
213: 478,
214: 479,
215: 480,
216: 481,
217: 482,
218: 483,
219: 484,
220: 485,
221: 486,
222: 488,
223: 489,
224: 491,
225: 492,
226: 493,
227: 494,
229: 498,
230: 499,
}
_CELL_SHAPE_AP_OVERRIDES = {
143: {"hP": 395, "hR": 396},
144: {"hP": 397, "hR": 398},
145: {"hP": 399, "hR": 400},
147: {"hP": 403, "hR": 404},
}
#: Xband AP numbers keyed by Hall number for the full Hall-number range 1..530.
#: A few primitive trigonal space groups still need cell-shape overrides because
#: spglib uses the same Hall number for both hexagonal and rhombohedral cells.
hall_numbers_to_AP = {
1: 1,
2: 2,
3: 4,
4: 3,
5: 3,
6: 6,
7: 5,
8: 5,
9: 10,
10: 11,
11: 12,
12: 8,
13: 7,
14: 9,
15: 8,
16: 7,
17: 9,
18: 14,
19: 13,
20: 13,
21: 18,
22: 20,
23: 19,
24: 16,
25: 17,
26: 15,
27: 16,
28: 17,
29: 15,
30: 24,
31: 25,
32: 26,
33: 22,
34: 21,
35: 23,
36: 22,
37: 21,
38: 23,
39: 33,
40: 36,
41: 37,
42: 35,
43: 34,
44: 38,
45: 29,
46: 28,
47: 32,
48: 27,
49: 30,
50: 31,
51: 29,
52: 28,
53: 32,
54: 27,
55: 30,
56: 31,
57: 40,
58: 39,
59: 39,
60: 42,
61: 41,
62: 41,
63: 46,
64: 47,
65: 48,
66: 44,
67: 43,
68: 45,
69: 44,
70: 43,
71: 45,
72: 52,
73: 54,
74: 53,
75: 50,
76: 51,
77: 49,
78: 50,
79: 51,
80: 49,
81: 55,
82: 56,
83: 57,
84: 58,
85: 59,
86: 60,
87: 500,
88: 501,
89: 502,
90: 67,
91: 70,
92: 71,
93: 69,
94: 68,
95: 72,
96: 63,
97: 62,
98: 66,
99: 61,
100: 64,
101: 65,
102: 63,
103: 62,
104: 66,
105: 61,
106: 64,
107: 65,
108: 73,
109: 74,
110: 75,
111: 76,
112: 77,
113: 78,
114: 79,
115: 80,
116: 81,
117: 82,
118: 83,
119: 84,
120: 85,
121: 86,
122: 87,
123: 88,
124: 89,
125: 90,
126: 91,
127: 92,
128: 93,
129: 94,
130: 95,
131: 96,
132: 97,
133: 98,
134: 99,
135: 100,
136: 101,
137: 102,
138: 103,
139: 104,
140: 105,
141: 106,
142: 107,
143: 108,
144: 109,
145: 110,
146: 111,
147: 112,
148: 113,
149: 114,
150: 115,
151: 116,
152: 117,
153: 118,
154: 119,
155: 120,
156: 121,
157: 122,
158: 123,
159: 124,
160: 125,
161: 126,
162: 127,
163: 128,
164: 129,
165: 130,
166: 131,
167: 132,
168: 133,
169: 134,
170: 135,
171: 136,
172: 137,
173: 138,
174: 139,
175: 140,
176: 145,
177: 146,
178: 143,
179: 144,
180: 141,
181: 142,
182: 147,
183: 148,
184: 149,
185: 150,
186: 151,
187: 152,
188: 153,
189: 154,
190: 155,
191: 156,
192: 157,
193: 158,
194: 159,
195: 160,
196: 161,
197: 162,
198: 163,
199: 164,
200: 165,
201: 166,
202: 167,
203: 168,
204: 169,
205: 170,
206: 171,
207: 172,
208: 173,
209: 174,
210: 175,
211: 176,
212: 177,
213: 178,
214: 179,
215: 180,
216: 181,
217: 182,
218: 183,
219: 184,
220: 185,
221: 186,
222: 187,
223: 188,
224: 189,
225: 190,
226: 191,
227: 192,
228: 193,
229: 193,
230: 195,
231: 196,
232: 197,
233: 198,
234: 198,
235: 198,
236: 198,
237: 198,
238: 198,
239: 204,
240: 205,
241: 206,
242: 207,
243: 208,
244: 209,
245: 210,
246: 211,
247: 212,
248: 213,
249: 214,
250: 215,
251: 216,
252: 217,
253: 218,
254: 219,
255: 220,
256: 221,
257: 222,
258: 223,
259: 224,
260: 225,
261: 226,
262: 227,
263: 228,
264: 229,
265: 230,
266: 231,
267: 232,
268: 233,
269: 234,
270: 235,
271: 236,
272: 237,
273: 238,
274: 239,
275: 240,
276: 241,
277: 242,
278: 247,
279: 247,
280: 247,
281: 247,
282: 247,
283: 247,
284: 249,
285: 250,
286: 251,
287: 252,
288: 253,
289: 254,
290: 255,
291: 256,
292: 257,
293: 258,
294: 259,
295: 260,
296: 261,
297: 262,
298: 263,
299: 264,
300: 265,
301: 266,
302: 267,
303: 268,
304: 269,
305: 270,
306: 271,
307: 272,
308: 273,
309: 274,
310: 275,
311: 276,
312: 277,
313: 278,
314: 279,
315: 280,
316: 281,
317: 282,
318: 283,
319: 284,
320: 285,
321: 286,
322: 287,
323: 287,
324: 287,
325: 287,
326: 287,
327: 287,
328: 287,
329: 287,
330: 287,
331: 287,
332: 287,
333: 287,
334: 299,
335: 300,
336: 300,
337: 302,
338: 303,
339: 304,
340: 305,
341: 306,
342: 307,
343: 308,
344: 309,
345: 310,
346: 311,
347: 312,
348: 313,
349: 314,
350: 315,
351: 316,
352: 317,
353: 318,
354: 319,
355: 320,
356: 321,
357: 322,
358: 323,
359: 324,
360: 324,
361: 326,
362: 326,
363: 328,
364: 329,
365: 329,
366: 331,
367: 332,
368: 333,
369: 334,
370: 335,
371: 336,
372: 337,
373: 338,
374: 339,
375: 340,
376: 341,
377: 342,
378: 343,
379: 344,
380: 345,
381: 346,
382: 347,
383: 348,
384: 349,
385: 350,
386: 351,
387: 352,
388: 353,
389: 354,
390: 355,
391: 356,
392: 357,
393: 358,
394: 359,
395: 360,
396: 361,
397: 362,
398: 363,
399: 364,
400: 365,
401: 366,
402: 368,
403: 368,
404: 370,
405: 370,
406: 371,
407: 372,
408: 374,
409: 374,
410: 376,
411: 376,
412: 377,
413: 378,
414: 380,
415: 380,
416: 382,
417: 382,
418: 383,
419: 384,
420: 386,
421: 386,
422: 388,
423: 388,
424: 389,
425: 390,
426: 392,
427: 392,
428: 394,
429: 394,
430: 396,
431: 398,
432: 400,
433: 402,
434: 401,
435: 404,
436: 406,
437: 405,
438: 407,
439: 408,
440: 409,
441: 410,
442: 411,
443: 412,
444: 414,
445: 413,
446: 415,
447: 416,
448: 417,
449: 418,
450: 420,
451: 419,
452: 422,
453: 421,
454: 423,
455: 424,
456: 425,
457: 426,
458: 428,
459: 427,
460: 430,
461: 429,
462: 431,
463: 432,
464: 433,
465: 434,
466: 435,
467: 436,
468: 437,
469: 438,
470: 439,
471: 440,
472: 441,
473: 442,
474: 443,
475: 444,
476: 445,
477: 446,
478: 447,
479: 448,
480: 449,
481: 450,
482: 451,
483: 452,
484: 453,
485: 454,
486: 455,
487: 456,
488: 457,
489: 458,
490: 459,
491: 460,
492: 461,
493: 462,
494: 463,
495: 465,
496: 465,
497: 466,
498: 468,
499: 468,
500: 469,
501: 470,
502: 471,
503: 472,
504: 473,
505: 474,
506: 475,
507: 476,
508: 477,
509: 478,
510: 479,
511: 480,
512: 481,
513: 482,
514: 483,
515: 484,
516: 485,
517: 486,
518: 488,
519: 488,
520: 489,
521: 490,
522: 491,
523: 492,
524: 493,
525: 494,
526: 495,
527: 496,
528: 497,
529: 498,
530: 499,
}
[docs]
def ap_number_from_spacegroup(spacegroup_number: int, hall_number: Union[int, None] = None, cell=None) -> int:
"""Return the Xband A. Perlov number for a space-group setting.
The trigonal space groups 143, 144, 145, and 147 need the cell shape to
distinguish hP and hR settings. Other ambiguous groups are resolved by Hall
number when it is available, otherwise the international-number fallback is used.
"""
if cell is not None:
overrides = _CELL_SHAPE_AP_OVERRIDES.get(spacegroup_number)
if overrides is not None:
pearson_symbol = cell.get_bravais_lattice().pearson_symbol
cell_shape_ap_number = overrides.get(pearson_symbol)
if cell_shape_ap_number is not None:
return cell_shape_ap_number
if hall_number is not None and hall_number in hall_numbers_to_AP:
return hall_numbers_to_AP[hall_number]
return international_numbers_to_AP[spacegroup_number]
[docs]
class LatticeData:
[docs]
def __init__(self, atoms):
cell = atoms.get_cell()
bl = cell.get_bravais_lattice()
sg = spglib_dataset(atoms)
ps = bl.pearson_symbol
self.pearson = Pearson.from_symbol(ps)
self.basis = 0
self.sgno = sg.number
self.apno = ap_number_from_spacegroup(sg.number, sg.hall_number, cell=cell)
self.bravais = cell.get_bravais_lattice()
self.boa = cell.cellpar()[1] / cell.cellpar()[0]
self.coa = cell.cellpar()[2] / cell.cellpar()[0]
self.alat = bl.a / Bohr
self.blat = self.boa * self.alat
self.clat = self.coa * self.alat
self.alpha = cell.cellpar()[3]
self.beta = cell.cellpar()[4]
self.gamma = cell.cellpar()[5]
self.sg = sg
self.cell = cell
[docs]
@cached_property
def rbas(self):
# self.rbas = sg.scaled_primitive_cell
sg = self.sg
out = sg.primitive_lattice.dot(np.linalg.inv(sg.std_lattice))
return reorder_matrix(out, self.cell)
@property
def bravais_number(self):
return self.pearson.bravais_number
@property
def shoenflies_symbol(self):
return self.pearson.shoenflies