advisor-scattering 0.5.2__py3-none-any.whl → 0.5.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- advisor/features/scattering_geometry/domain/brillouin_calculator.py +121 -63
- advisor/features/scattering_geometry/domain/core.py +187 -134
- advisor/features/scattering_geometry/ui/components/hk_angles_components.py +175 -79
- advisor/features/scattering_geometry/ui/components/hkl_scan_components.py +42 -17
- advisor/features/scattering_geometry/ui/components/hkl_to_angles_components.py +175 -79
- advisor/features/scattering_geometry/ui/scattering_geometry_tab.py +57 -48
- {advisor_scattering-0.5.2.dist-info → advisor_scattering-0.5.3.dist-info}/METADATA +4 -12
- {advisor_scattering-0.5.2.dist-info → advisor_scattering-0.5.3.dist-info}/RECORD +11 -11
- {advisor_scattering-0.5.2.dist-info → advisor_scattering-0.5.3.dist-info}/WHEEL +1 -1
- {advisor_scattering-0.5.2.dist-info → advisor_scattering-0.5.3.dist-info}/entry_points.txt +0 -0
- {advisor_scattering-0.5.2.dist-info → advisor_scattering-0.5.3.dist-info}/top_level.txt +0 -0
|
@@ -5,11 +5,9 @@ from scipy.optimize import fsolve
|
|
|
5
5
|
|
|
6
6
|
from advisor.domain import angle_to_matrix
|
|
7
7
|
from advisor.domain.core import Lab
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
_calculate_hkl,
|
|
12
|
-
)
|
|
8
|
+
|
|
9
|
+
from .core import (_calculate_angles_factory, _calculate_angles_tth_fixed,
|
|
10
|
+
_calculate_hkl)
|
|
13
11
|
|
|
14
12
|
|
|
15
13
|
def is_feasible(theta, tth):
|
|
@@ -34,6 +32,7 @@ class BrillouinCalculator:
|
|
|
34
32
|
self.hPlanck = 6.62607015e-34 # Planck's constant [J·s]
|
|
35
33
|
self.c_light = 299792458 # Speed of light [m/s]
|
|
36
34
|
self.e = 1.602176634e-19 # Elementary charge [C]
|
|
35
|
+
self.ev_to_lambda = 12398.42 # eV to Angstrom
|
|
37
36
|
|
|
38
37
|
# Initialize sample
|
|
39
38
|
self.lab = Lab()
|
|
@@ -97,9 +96,7 @@ class BrillouinCalculator:
|
|
|
97
96
|
chi,
|
|
98
97
|
)
|
|
99
98
|
# Calculate wavelength and wavevector
|
|
100
|
-
self.lambda_A =
|
|
101
|
-
(self.hPlanck * self.c_light) / (self.energy * self.e) * 1e10
|
|
102
|
-
)
|
|
99
|
+
self.lambda_A = self.ev_to_lambda / self.energy
|
|
103
100
|
self.k_in = 2 * np.pi / self.lambda_A
|
|
104
101
|
|
|
105
102
|
self._initialized = True
|
|
@@ -145,24 +142,34 @@ class BrillouinCalculator:
|
|
|
145
142
|
):
|
|
146
143
|
"""Calculate scattering angles from HKL indices.
|
|
147
144
|
|
|
148
|
-
|
|
145
|
+
Returns up to two solutions if they exist.
|
|
149
146
|
|
|
150
147
|
Args:
|
|
151
|
-
|
|
148
|
+
H, K, L (float): HKL indices
|
|
149
|
+
fixed_angle (float): Value of the fixed angle in degrees
|
|
150
|
+
fixed_angle_name (str): Name of the fixed angle ('chi' or 'phi')
|
|
152
151
|
|
|
153
152
|
Returns:
|
|
154
|
-
dict: Dictionary containing
|
|
153
|
+
dict: Dictionary containing:
|
|
154
|
+
- tth (list): Scattering angles in degrees
|
|
155
|
+
- theta (list): Sample theta rotation values in degrees
|
|
156
|
+
- phi (list): Sample phi rotation values in degrees
|
|
157
|
+
- chi (list): Sample chi rotation values in degrees
|
|
158
|
+
- H, K, L (float): Input HKL indices
|
|
159
|
+
- number_of_solutions (int): Number of distinct solutions found
|
|
160
|
+
- feasible (list): Boolean list indicating if each solution is feasible
|
|
161
|
+
- success (bool): Whether calculation succeeded
|
|
162
|
+
- error (str or None): Error message if any
|
|
155
163
|
"""
|
|
156
164
|
|
|
157
165
|
if not self.is_initialized():
|
|
158
166
|
raise ValueError("Calculator not initialized")
|
|
159
167
|
|
|
160
|
-
|
|
161
168
|
calculate_angles = _calculate_angles_factory(fixed_angle_name)
|
|
162
169
|
a, b, c, alpha, beta, gamma = self.lab.get_lattice_parameters()
|
|
163
170
|
roll, pitch, yaw = self.lab.get_lattice_angles()
|
|
164
171
|
try:
|
|
165
|
-
|
|
172
|
+
result = calculate_angles(
|
|
166
173
|
self.k_in,
|
|
167
174
|
H,
|
|
168
175
|
K,
|
|
@@ -181,19 +188,31 @@ class BrillouinCalculator:
|
|
|
181
188
|
except Exception as e:
|
|
182
189
|
return {
|
|
183
190
|
"success": False,
|
|
184
|
-
"error": "No solution found; The Q point is
|
|
191
|
+
"error": "No solution found; The Q point is possibly not reachable at this energy and/or scattering angle tth. System message:" + str(e),
|
|
185
192
|
}
|
|
193
|
+
|
|
194
|
+
# Extract lists from result
|
|
195
|
+
tth_list = result["tth"]
|
|
196
|
+
theta_list = result["theta"]
|
|
197
|
+
phi_list = result["phi"]
|
|
198
|
+
chi_list = result["chi"]
|
|
199
|
+
num_solutions = result["number_of_solutions"]
|
|
200
|
+
|
|
201
|
+
# Calculate feasibility for each solution
|
|
202
|
+
feasible_list = [is_feasible(theta_list[i], tth_list[i]) for i in range(len(theta_list))]
|
|
203
|
+
|
|
186
204
|
return {
|
|
187
|
-
"tth":
|
|
188
|
-
"theta":
|
|
189
|
-
"phi":
|
|
190
|
-
"chi":
|
|
205
|
+
"tth": tth_list,
|
|
206
|
+
"theta": theta_list,
|
|
207
|
+
"phi": phi_list,
|
|
208
|
+
"chi": chi_list,
|
|
191
209
|
"H": H,
|
|
192
210
|
"K": K,
|
|
193
211
|
"L": L,
|
|
212
|
+
"number_of_solutions": num_solutions,
|
|
194
213
|
"success": True,
|
|
195
214
|
"error": None,
|
|
196
|
-
"feasible":
|
|
215
|
+
"feasible": feasible_list,
|
|
197
216
|
}
|
|
198
217
|
|
|
199
218
|
def calculate_angles_tth_fixed(
|
|
@@ -205,52 +224,77 @@ class BrillouinCalculator:
|
|
|
205
224
|
fixed_angle_name="chi",
|
|
206
225
|
fixed_angle=0.0,
|
|
207
226
|
):
|
|
227
|
+
"""Calculate scattering angles with fixed tth.
|
|
228
|
+
|
|
229
|
+
Returns up to two solutions if they exist.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
tth (float): Fixed scattering angle in degrees
|
|
233
|
+
H, K, L (float): HKL indices (one should be None to be solved)
|
|
234
|
+
fixed_angle_name (str): Name of the fixed angle ('chi' or 'phi')
|
|
235
|
+
fixed_angle (float): Value of the fixed angle in degrees
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
dict: Dictionary containing lists of solutions and metadata
|
|
239
|
+
"""
|
|
208
240
|
a, b, c, alpha, beta, gamma = self.lab.get_lattice_parameters()
|
|
209
241
|
roll, pitch, yaw = self.lab.get_lattice_angles()
|
|
210
242
|
|
|
211
243
|
try:
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
fixed_angle,
|
|
230
|
-
)
|
|
244
|
+
result = _calculate_angles_tth_fixed(
|
|
245
|
+
self.k_in,
|
|
246
|
+
tth,
|
|
247
|
+
a,
|
|
248
|
+
b,
|
|
249
|
+
c,
|
|
250
|
+
alpha,
|
|
251
|
+
beta,
|
|
252
|
+
gamma,
|
|
253
|
+
roll,
|
|
254
|
+
pitch,
|
|
255
|
+
yaw,
|
|
256
|
+
H,
|
|
257
|
+
K,
|
|
258
|
+
L,
|
|
259
|
+
fixed_angle_name,
|
|
260
|
+
fixed_angle,
|
|
231
261
|
)
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
262
|
+
|
|
263
|
+
# Extract data from result
|
|
264
|
+
tth_list = result["tth"]
|
|
265
|
+
theta_list = result["theta"]
|
|
266
|
+
phi_list = result["phi"]
|
|
267
|
+
chi_list = result["chi"]
|
|
268
|
+
momentum = result["momentum"]
|
|
269
|
+
num_solutions = result["number_of_solutions"]
|
|
270
|
+
|
|
271
|
+
# Update the solved HKL component
|
|
272
|
+
H_result = momentum if H is None else H
|
|
273
|
+
K_result = momentum if K is None else K
|
|
274
|
+
L_result = momentum if L is None else L
|
|
275
|
+
|
|
235
276
|
except Exception as e:
|
|
236
277
|
return {
|
|
237
278
|
"success": False,
|
|
238
|
-
"error": "No solution found; The Q point is
|
|
279
|
+
"error": "No solution found; The Q point is possibly not reachable at this energy and/or scattering angle tth. System message:" + str(e),
|
|
239
280
|
}
|
|
240
281
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
"
|
|
246
|
-
"
|
|
247
|
-
"
|
|
248
|
-
"
|
|
282
|
+
# Calculate feasibility for each solution
|
|
283
|
+
feasible_list = [is_feasible(theta_list[i], tth_list[i]) for i in range(len(theta_list))]
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
"tth": tth_list,
|
|
287
|
+
"theta": theta_list,
|
|
288
|
+
"phi": phi_list,
|
|
289
|
+
"chi": chi_list,
|
|
290
|
+
"H": H_result,
|
|
291
|
+
"K": K_result,
|
|
292
|
+
"L": L_result,
|
|
293
|
+
"number_of_solutions": num_solutions,
|
|
249
294
|
"success": True,
|
|
250
295
|
"error": None,
|
|
251
|
-
"feasible":
|
|
296
|
+
"feasible": feasible_list,
|
|
252
297
|
}
|
|
253
|
-
return result
|
|
254
298
|
|
|
255
299
|
def calculate_angles_tth_fixed_scan(
|
|
256
300
|
self,
|
|
@@ -264,6 +308,9 @@ class BrillouinCalculator:
|
|
|
264
308
|
):
|
|
265
309
|
"""Calculate scattering angles for a range of HKL values with fixed tth.
|
|
266
310
|
|
|
311
|
+
Each HKL point may have up to 2 solutions. Results are flattened into lists
|
|
312
|
+
with solution_group indicating which original HKL point each solution belongs to.
|
|
313
|
+
|
|
267
314
|
Args:
|
|
268
315
|
tth (float): Fixed scattering angle in degrees
|
|
269
316
|
start_points (tuple): Starting HKL values (the deactivated index will be ignored)
|
|
@@ -315,7 +362,7 @@ class BrillouinCalculator:
|
|
|
315
362
|
else [None] * num_points
|
|
316
363
|
)
|
|
317
364
|
|
|
318
|
-
# Initialize result lists
|
|
365
|
+
# Initialize result lists - flattened to include all solutions
|
|
319
366
|
all_tth = []
|
|
320
367
|
all_theta = []
|
|
321
368
|
all_phi = []
|
|
@@ -323,6 +370,9 @@ class BrillouinCalculator:
|
|
|
323
370
|
all_h = []
|
|
324
371
|
all_k = []
|
|
325
372
|
all_l = []
|
|
373
|
+
all_feasible = []
|
|
374
|
+
all_solution_group = [] # Track which HKL point each solution belongs to
|
|
375
|
+
all_solution_index = [] # Track if it's solution 1 or 2 within the group
|
|
326
376
|
|
|
327
377
|
# Calculate for each point
|
|
328
378
|
for i in range(num_points):
|
|
@@ -344,14 +394,20 @@ class BrillouinCalculator:
|
|
|
344
394
|
# Skip points that fail to calculate but don't fail completely
|
|
345
395
|
continue
|
|
346
396
|
|
|
347
|
-
# Each calculation
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
397
|
+
# Each calculation returns lists of solutions
|
|
398
|
+
num_solutions = result.get("number_of_solutions", 1)
|
|
399
|
+
for sol_idx in range(num_solutions):
|
|
400
|
+
all_tth.append(result["tth"][sol_idx])
|
|
401
|
+
all_theta.append(result["theta"][sol_idx])
|
|
402
|
+
all_phi.append(result["phi"][sol_idx])
|
|
403
|
+
all_chi.append(result["chi"][sol_idx])
|
|
404
|
+
all_h.append(result["H"])
|
|
405
|
+
all_k.append(result["K"])
|
|
406
|
+
all_l.append(result["L"])
|
|
407
|
+
all_feasible.append(result["feasible"][sol_idx])
|
|
408
|
+
all_solution_group.append(i) # Which HKL point
|
|
409
|
+
all_solution_index.append(sol_idx + 1) # Solution 1 or 2
|
|
410
|
+
|
|
355
411
|
except Exception as e:
|
|
356
412
|
# Log the error but continue with other points
|
|
357
413
|
print(f"Error calculating point {(h, k, l)}: {str(e)}")
|
|
@@ -371,10 +427,12 @@ class BrillouinCalculator:
|
|
|
371
427
|
"H": all_h,
|
|
372
428
|
"K": all_k,
|
|
373
429
|
"L": all_l,
|
|
374
|
-
"deactivated_index": deactivated_index,
|
|
430
|
+
"deactivated_index": deactivated_index,
|
|
431
|
+
"solution_group": all_solution_group,
|
|
432
|
+
"solution_index": all_solution_index,
|
|
375
433
|
"success": True,
|
|
376
434
|
"error": None,
|
|
377
|
-
"feasible":
|
|
435
|
+
"feasible": all_feasible,
|
|
378
436
|
}
|
|
379
437
|
|
|
380
438
|
def is_initialized(self):
|