Skip to content

Commit cf91a3e

Browse files
committed
speed up
1 parent 290d25b commit cf91a3e

File tree

7 files changed

+572
-195
lines changed

7 files changed

+572
-195
lines changed

pyxtal/XRD_indexer.py

Lines changed: 99 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -69,125 +69,98 @@ def get_cell_params(spg, hkls, two_thetas, wave_length=1.54184):
6969
thetas = np.radians(two_thetas / 2)
7070
d_spacings = wave_length / (2 * np.sin(thetas))
7171

72-
cell_values = []
72+
cells = []
7373
if spg >= 195: # cubic, only need a
7474
h_sq_sum = np.sum(hkls**2, axis=1)
75-
a_values = d_spacings * np.sqrt(h_sq_sum)
76-
cell_values = [[a] for a in a_values if 0 < a < 50]
75+
cells = d_spacings * np.sqrt(h_sq_sum)
76+
cells = cells[cells < 50.0]
77+
cells = np.reshape(cells, [len(cells), 1])
7778

7879
elif 143 <= spg <= 194: # hexagonal, need a and c
7980
# need two hkls to determine a and c
8081
len_solutions = len(hkls) // 2
81-
for i in range(len_solutions):
82-
(h1, k1, l1), (h2, k2, l2) = hkls[2*i], hkls[2*i + 1]
83-
theta1 = np.radians(two_thetas[2*i] / 2)
84-
theta2 = np.radians(two_thetas[2*i + 1] / 2)
85-
d1 = wave_length / (2 * np.sin(theta1))
86-
d2 = wave_length / (2 * np.sin(theta2))
87-
A = np.array([[4/3 * (h1**2 + h1*k1 + k1**2), l1**2],
88-
[4/3 * (h2**2 + h2*k2 + k2**2), l2**2]])
89-
b = np.array([1/d1**2, 1/d2**2])
90-
try:
91-
x = np.linalg.solve(A, b)
92-
if x[0] <= 0 or x[1] <= 0:
93-
continue
94-
a = np.sqrt(1/x[0])
95-
c = np.sqrt(1/x[1])
96-
if a > 50: continue
97-
cell_values.append((a, c))
98-
except np.linalg.LinAlgError:
99-
continue
82+
ds = (2 * np.sin(thetas) / wave_length)**2
83+
A = np.zeros([len(hkls), 2])
84+
A[:, 0] = 4/3 * (hkls[:, 0] ** 2 + hkls[:, 0] * hkls[:, 1] + hkls[:, 1] ** 2)
85+
A[:, 1] = hkls[:, 2] ** 2
86+
B = np.reshape(ds, [len_solutions, 2])
87+
A = np.reshape(A, [len_solutions, 2, 2])#; print(A.shape, B.shape)
88+
xs = np.linalg.solve(A, B)#; print(xs); import sys; sys.exit()
89+
xs = xs[xs[:, 0] > 0]
90+
xs = xs[xs[:, 1] > 0]
91+
cells = np.sqrt(1/xs)
92+
cells = cells[cells[:, 0] < 50.0]
10093

10194
elif 75 <= spg <= 142: # tetragonal, need a and c
10295
# need two hkls to determine a and c
10396
len_solutions = len(hkls) // 2
104-
for i in range(len_solutions):
105-
(h1, k1, l1), (h2, k2, l2) = hkls[2*i], hkls[2*i + 1]
106-
theta1 = np.radians(two_thetas[2*i] / 2)
107-
theta2 = np.radians(two_thetas[2*i + 1] / 2)
108-
d1 = wave_length / (2 * np.sin(theta1))
109-
d2 = wave_length / (2 * np.sin(theta2))
110-
A = np.array([[h1**2 + k1**2, l1**2],
111-
[h2**2 + k2**2, l2**2]])
112-
b = np.array([1/d1**2, 1/d2**2])
113-
try:
114-
x = np.linalg.solve(A, b)
115-
if x[0] <= 0 or x[1] <= 0:
116-
continue
117-
a = np.sqrt(1/x[0])
118-
c = np.sqrt(1/x[1])
119-
if a > 50: continue
120-
cell_values.append((a, c))
121-
except np.linalg.LinAlgError:
122-
continue
97+
ds = (2 * np.sin(thetas) / wave_length)**2
98+
A = np.zeros([len(hkls), 2])
99+
A[:, 0] = hkls[:, 0] ** 2 + hkls[:, 1] ** 2
100+
A[:, 1] = hkls[:, 2] ** 2
101+
B = np.reshape(ds, [len_solutions, 2])
102+
A = np.reshape(A, [len_solutions, 2, 2])#; print(A.shape, B.shape)
103+
xs = np.linalg.solve(A, B)#; print(xs); import sys; sys.exit()
104+
xs = xs[xs[:, 0] > 0]
105+
xs = xs[xs[:, 1] > 0]
106+
cells = np.sqrt(1/xs)
107+
cells = cells[cells[:, 0] < 50.0]
108+
123109
elif 16 <= spg <= 74: # orthorhombic, need a, b, c
124110
# need three hkls to determine a, b, c
125111
len_solutions = len(hkls) // 3
126-
for i in range(len_solutions):
127-
(h1, k1, l1), (h2, k2, l2), (h3, k3, l3) = hkls[3*i], hkls[3*i + 1], hkls[3*i + 2]
128-
theta1 = np.radians(two_thetas[3*i] / 2)
129-
theta2 = np.radians(two_thetas[3*i + 1] / 2)
130-
theta3 = np.radians(two_thetas[3*i + 2] / 2)
131-
d1 = wave_length / (2 * np.sin(theta1))
132-
d2 = wave_length / (2 * np.sin(theta2))
133-
d3 = wave_length / (2 * np.sin(theta3))
134-
A = np.array([[h1**2, k1**2, l1**2],
135-
[h2**2, k2**2, l2**2],
136-
[h3**2, k3**2, l3**2]])
137-
b = np.array([1/d1**2, 1/d2**2, 1/d3**2])
138-
try:
139-
x = np.linalg.solve(A, b)
140-
if x[0] <= 0 or x[1] <= 0 or x[2] <= 0:
141-
continue
142-
a = np.sqrt(1/x[0])
143-
b = np.sqrt(1/x[1])
144-
c = np.sqrt(1/x[2])
145-
if a > 50 or b > 50 or c > 50: continue
146-
cell_values.append((a, b, c))
147-
except np.linalg.LinAlgError:
148-
continue
112+
ds = (2 * np.sin(thetas) / wave_length)**2
113+
A = np.zeros([len(hkls), 3])
114+
A[:, 0] = hkls[:, 0] ** 2
115+
A[:, 1] = hkls[:, 1] ** 2
116+
A[:, 2] = hkls[:, 2] ** 2
117+
B = np.reshape(ds, [len_solutions, 3])
118+
A = np.reshape(A, [len_solutions, 3, 3])#; print(A.shape, B.shape)
119+
xs = np.linalg.solve(A, B)#; print(xs); import sys; sys.exit()
120+
xs = xs[xs[:, 0] > 0]
121+
xs = xs[xs[:, 1] > 0]
122+
xs = xs[xs[:, 2] > 0]
123+
cells = np.sqrt(1/xs)
124+
cells = cells[np.all(cells[:, :3] < 50.0, axis=1)]
149125

150126
elif 3 <= spg <= 15: # monoclinic, need a, b, c, beta
151127
# need four hkls to determine a, b, c, beta
152-
N = 4
153-
len_solutions = len(hkls) // N
154-
for i in range(len_solutions):
155-
hkls_sub = hkls[N*i:N*i+N]
156-
thetas_sub = np.radians(two_thetas[N*i:N*i+N]/2)
157-
d_sub = (2 * np.sin(thetas_sub) / wave_length)**2
158-
h1, k1, l1 = hkls_sub[0]
159-
h2, k2, l2 = hkls_sub[1]
160-
h3, k3, l3 = hkls_sub[2]
161-
h4, k4, l4 = hkls_sub[3]
162-
d1, d2, d3, d4 = d_sub[0], d_sub[1], d_sub[2], d_sub[3]
163-
A = np.array([[h1**2, k1**2, l1**2, h1*l1],
164-
[h2**2, k2**2, l2**2, h2*l2],
165-
[h3**2, k3**2, l3**2, h3*l3],
166-
[h4**2, k4**2, l4**2, h4*l4]])
167-
b = np.array([d1, d2, d3, d4])
168-
#print(A, b)
169-
try:
170-
x = np.linalg.solve(A, b)#; print(A, x)
171-
a1, a2, a3, a4 = x
172-
if a1 <= 0 or a2 <= 0 or a3 <= 0: continue
173-
b = np.sqrt(1/a2)#; print('b value', b); import sys; sys.exit()
174-
cos_beta = - a4 / (2 * np.sqrt(a1) * np.sqrt(a3))
175-
if cos_beta < -1 or cos_beta > 1: continue
176-
betar = np.arccos(cos_beta)
177-
sin_beta = np.sin(betar)
178-
if sin_beta <= np.sin(np.pi/6): continue
179-
a = np.sqrt(1/a1) / sin_beta
180-
c = np.sqrt(1/a3) / sin_beta
181-
if a > 50 or b > 50 or c > 50: continue
182-
cell_values.append((a, b, c, np.degrees(betar)))
183-
#print(h1, k1, l1, cell_values[-1])
184-
except np.linalg.LinAlgError:
185-
continue
128+
len_solutions = len(hkls) // 4
129+
thetas = np.radians(two_thetas/2)
130+
ds = (2 * np.sin(thetas) / wave_length)**2
131+
# hkls (4*N, 3) A=> N, 4, 4
132+
A = np.zeros([len(hkls), 4])
133+
A[:, 0] = hkls[:, 0] ** 2
134+
A[:, 1] = hkls[:, 1] ** 2
135+
A[:, 2] = hkls[:, 2] ** 2
136+
A[:, 3] = hkls[:, 0] * hkls[:, 2]
137+
B = np.reshape(ds, [len_solutions, 4])
138+
A = np.reshape(A, [len_solutions, 4, 4])#; print(A.shape, B.shape)
139+
xs = np.linalg.solve(A, B)#; print(xs); import sys; sys.exit()
140+
xs = xs[xs[:, 0] > 0]
141+
xs = xs[xs[:, 1] > 0]
142+
xs = xs[xs[:, 2] > 0]
143+
cos_betas = -xs[:, 3] / (2 * np.sqrt(xs[:, 0] * xs[:, 2]))
144+
masks = np.abs(cos_betas) <= 1/np.sqrt(2)
145+
xs = xs[masks]
146+
cos_betas = cos_betas[masks]
147+
sin_betas = np.sqrt(1 - cos_betas ** 2)
148+
cells = np.zeros([len(xs), 4])
149+
cells[:, 1] = np.sqrt(1/xs[:, 1])
150+
cells[:, 3] = np.degrees(np.arccos(cos_betas))
151+
cells[:, 0] = np.sqrt(1/xs[:, 0]) / sin_betas
152+
cells[:, 2] = np.sqrt(1/xs[:, 2]) / sin_betas
153+
154+
# force angle to be less than 90
155+
mask = cells[:, 3] > 90.0
156+
cells[mask, 3] = 180.0 - cells[mask, 3]
157+
cells = cells[np.all(cells[:, :3] < 50.0, axis=1)]
158+
#print(cells)
186159
else:
187160
msg = "Only cubic, tetragonal, hexagonal, and orthorhombic systems are supported."
188161
raise NotImplementedError(msg)
189162

190-
return cell_values
163+
return cells
191164

192165
def get_d_hkl_from_cell(spg, cells, h, k, l):
193166
"""
@@ -382,6 +355,10 @@ def get_cell_from_multi_hkls(spg, hkls, two_thetas, long_thetas=None, wave_lengt
382355

383356
cells = np.array(cells)
384357
if len(cells) == 0: return []
358+
# keep cells up to 4 decimal places
359+
if spg < 16:
360+
cells[:, -1] = np.round(cells[:, -1], decimals=2)
361+
cells[:, :3] = np.round(cells[:, :3], decimals=4)
385362
cells = np.unique(cells, axis=0)#; print(cells) # remove duplicates
386363

387364
# get the maximum h from assuming the cell[-1] is (h00)
@@ -434,6 +411,7 @@ def get_cell_from_multi_hkls(spg, hkls, two_thetas, long_thetas=None, wave_lengt
434411
avg_error = np.mean([match[-1] for match in matched_peaks])
435412
consistency_score = 1.0 / (1.0 + avg_error) # lower error = higher score
436413
score = coverage * consistency_score
414+
#print("Cell:", cell, "Matched:", n_matched, "Score:", score)
437415

438416
if score > min_score:
439417
solutions.append({
@@ -450,10 +428,12 @@ def get_cell_from_multi_hkls(spg, hkls, two_thetas, long_thetas=None, wave_lengt
450428
from pyxtal import pyxtal
451429
from itertools import combinations
452430
np.set_printoptions(precision=4, suppress=True)
453-
431+
454432
xtal = pyxtal()
455-
#xtal.from_seed('pyxtal/database/cifs/JVASP-62168.cif')
456-
xtal.from_seed('pyxtal/database/cifs/JVASP-98225.cif')
433+
xtal.from_seed('pyxtal/database/cifs/JVASP-62168.cif')
434+
#xtal.from_seed('pyxtal/database/cifs/JVASP-98225.cif')
435+
#xtal.from_seed('pyxtal/database/cifs/JVASP-50935.cif')
436+
#xtal.from_seed('pyxtal/database/cifs/JVASP-28565.cif')
457437
xrd = xtal.get_XRD(thetas=[0, 120], SCALED_INTENSITY_TOL=0.5)
458438
cell_ref = np.sort(np.array(xtal.lattice.encode()))
459439
long_thetas = xrd.pxrd[:15, 0]
@@ -465,17 +445,23 @@ def get_cell_from_multi_hkls(spg, hkls, two_thetas, long_thetas=None, wave_lengt
465445
if spg >= 16:
466446
guesses = xtal.group.generate_hkl_guesses(2, 3, 5, max_square=29, total_square=40, verbose=True)
467447
else:
468-
guesses = xtal.group.generate_hkl_guesses(2, 2, 2, max_square=20, total_square=30, verbose=True)
448+
guesses = xtal.group.generate_hkl_guesses(3, 3, 6, max_square=38, total_square=48, verbose=True)
469449
guesses = np.array(guesses)
470450
print("Total guesses:", len(guesses))
471451
sum_squares = np.sum(guesses**2, axis=(1,2))
472452
sorted_indices = np.argsort(sum_squares)
473453
guesses = guesses[sorted_indices]
474454
#guesses = np.array([[[2, 0, 0], [1, 1, 0], [0, 1, 1], [0, 0, 2]]])
475455
#guesses = np.array([[[2, 0, 0], [1, 1, 0], [0, 0, 2], [2, 0, -2]]])
456+
#guesses = np.array([[[0, 2, 0], [0, 0, 1], [1, 0, 0], [1, 0, -1]]])
476457

477458
# Check the quality of each (hkl, 2theta) solutions
459+
cell2 = np.sort(np.array(xtal.lattice.encode()))
460+
if spg <= 15 and cell2[3] > 90: cell2[3] = 180 - cell2[3]
461+
cells_all = np.reshape(cell2, (1, len(cell2)))
462+
478463
for guess in guesses[:]:
464+
#print('New guess', guess.flatten())
479465
found = False
480466
n_peaks = len(guess)
481467

@@ -490,75 +476,19 @@ def get_cell_from_multi_hkls(spg, hkls, two_thetas, long_thetas=None, wave_lengt
490476

491477
solutions = get_cell_from_multi_hkls(spg, hkls_t, thetas, long_thetas, use_seed=False)
492478
for sol in solutions:
493-
d2 = np.sum(guess**2)
494-
print("Guess:", guess.flatten(), d2, "->", sol['cell'], thetas[:len(guess)], "Score:", sol['score'])
495479
cell1 = np.sort(np.array(sol['cell']))
496-
diff = np.sum((cell1 - cell_ref)**2)
497-
if diff < 0.1:
480+
if spg <= 15 and cell1[3] > 90: cell1[3] = 180 - cell1[3]
481+
482+
# Check if it is a new solution
483+
diffs = np.sum((cells_all - cell1)**2, axis=1)
484+
if len(cells_all[diffs < 0.1]) == 0:
485+
print("Guess:", guess.flatten(), np.sum(guess**2), "->", cell1, sol['score'])
486+
cells_all = np.vstack((cells_all, cell1))
487+
488+
if diffs[0] < 0.1: #result['score'] > 0.9999:
489+
print("Guess:", guess.flatten(), np.sum(guess**2), "->", cell1, sol['score'])
498490
print("High score, exiting early.")
499491
found = True
500492
break
501493
if found:
502494
break
503-
504-
505-
#guesses = [
506-
# [(1, 1, 1), (2, 2, 0), (3, 1, 1)],
507-
# [(0, 0, 2), (1, 0, 0)],#, (1, 0, 1)],
508-
# [(0, 0, 2), (1, 0, 1), (1, 1, 0)],
509-
# [(1, 0, 0), (1, 0, 1), (1, 1, 0)],
510-
# [(2, 0, 0), (1, 0, 1), (2, 1, 0)],
511-
# [(1, 0, 0), (1, 1, 1), (1, 0, 2)],
512-
# [(1, 0, 1), (1, 1, 1), (3, 1, 1)],
513-
# [(2, 2, 0), (3, 1, 1), (2, 2, 2)],
514-
# [(1, 1, 1), (2, 0, 0), (2, 2, 0)],
515-
# [(1, 1), (2, 0, 0), (2, 2, 0)], # test bad case
516-
# [(0, 0, 1), (2, 0, -1), (2, 0, 1), (4, 0, 0), (1, 1, 0)],
517-
# ]
518-
#for prototype in ["diamond", "graphite", "a-cristobalite", "olivine", "beta-Ga2O3"]:
519-
# xtal.from_prototype(prototype)
520-
# xrd = xtal.get_XRD(thetas=[0, 120], SCALED_INTENSITY_TOL=0.5)
521-
# spg = xtal.group.number
522-
# print("\nTesting prototype:", prototype, xtal.lattice, xtal.group.symbol, xtal.group.number)
523-
# print(xrd.by_hkl(N_max=10))
524-
# if True:
525-
# if xtal.group.number >= 195:
526-
# guesses = xtal.group.generate_hkl_guesses(3, max_square=16)
527-
# elif xtal.group.number >= 75:
528-
# guesses = xtal.group.generate_hkl_guesses(2, 2, 6, max_square=36)
529-
# elif xtal.group.number >= 16:
530-
# guesses = xtal.group.generate_hkl_guesses(3, 3, 3, max_square=20)
531-
# else:
532-
# guesses = xtal.group.generate_hkl_guesses(4, 2, 2, max_square=17, verbose=True)
533-
534-
# guesses = np.array(guesses)
535-
# print("Total guesses:", len(guesses))
536-
# sum_squares = np.sum(guesses**2, axis=(1,2))
537-
# sorted_indices = np.argsort(sum_squares)
538-
# guesses = guesses[sorted_indices]
539-
540-
# for guess in guesses:
541-
# hkls = [tuple(hkl) for hkl in guess if len(hkl) == 3]
542-
# if len(hkls)>3 and (hkls[0] == (1, 1, -1) and hkls[1] == (0, 0, -1) and hkls[2] == (2, 0, -1)):
543-
# print("Trying guess:", hkls)
544-
# run = True
545-
# else:
546-
# run = False
547-
# n_peaks = len(guess)
548-
# # select n peaks from first n+1 peaks, skipping a peak
549-
# exit = False
550-
# if n_peaks < len(xrd.pxrd):
551-
# available_peaks = xrd.pxrd[:n_peaks+1, 0]
552-
# # Try each combination of n peaks from the first n+1 peaks
553-
# for peak_combo in combinations(range(n_peaks+1), n_peaks):
554-
# theta = available_peaks[list(peak_combo)]
555-
# result = get_cell_from_multi_hkls(spg, hkls, theta, xrd.pxrd[:15, 0])
556-
# if result is not None and result['score'] > 0.95:
557-
# print("Guess:", hkls, "->", result['cell'], "Score:", result['score'],
558-
# "Matched peaks:", result['n_matched'], "/", result['n_total'])
559-
# if result['score'] > 0.992:
560-
# print("High score, exiting early.")
561-
# exit = True
562-
# break
563-
# if exit:
564-
# break
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#############################################################
2+
# ______ _ _ _ #
3+
# (_____ \ \ \ / / | | #
4+
# _____) ) _ \ \/ / |_ ____| | #
5+
# | ____/ | | | ) (| _)/ _ | | #
6+
# | | | |_| |/ /\ \ |_( (_| | |___ #
7+
# |_| \__ /_/ \_\___)__|_|_____) #
8+
# (____/ #
9+
#---------------------(version 1.1.1)--------------------#
10+
# A Python package for random crystal generation #
11+
# url: https://github.com/qzhu2017/pyxtal #
12+
# @Zhu's group at U. North Carolina at Charlotte #
13+
#############################################################
14+
data_from_pyxtal
15+
16+
_symmetry_space_group_name_H-M 'Cm'
17+
_symmetry_Int_Tables_number 8
18+
_symmetry_cell_setting monoclinic
19+
_cell_length_a 5.809700
20+
_cell_length_b 3.354234
21+
_cell_length_c 29.227006
22+
_cell_angle_alpha 90.000000
23+
_cell_angle_beta 90.010508
24+
_cell_angle_gamma 90.000000
25+
_cell_volume 569.549357
26+
27+
loop_
28+
_symmetry_equiv_pos_site_id
29+
_symmetry_equiv_pos_as_xyz
30+
1 'x, y, z'
31+
2 'x, -y, z'
32+
3 'x+1/2, y+1/2, z'
33+
4 'x+1/2, -y+1/2, z'
34+
35+
loop_
36+
_atom_site_label
37+
_atom_site_type_symbol
38+
_atom_site_symmetry_multiplicity
39+
_atom_site_fract_x
40+
_atom_site_fract_y
41+
_atom_site_fract_z
42+
_atom_site_occupancy
43+
Te Te 2 0.666712 0.000000 0.408805 1
44+
Te Te 2 0.666593 0.000000 0.278867 1
45+
Mo Mo 2 0.666481 0.000000 0.114581 1
46+
Mo Mo 2 0.333320 0.000000 0.343874 1
47+
W W 2 0.666885 0.000000 0.582526 1
48+
Se Se 2 0.333504 0.000000 0.525262 1
49+
Se Se 2 0.333600 0.000000 0.639696 1
50+
S S 2 0.333097 0.000000 0.062761 1
51+
S S 2 0.333204 0.000000 0.166468 1
52+
#END
53+

0 commit comments

Comments
 (0)