advisor-scattering 0.5.2__py3-none-any.whl → 0.9.1__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/controllers/app_controller.py +2 -1
- advisor/domain/__init__.py +4 -0
- advisor/domain/core/lab.py +9 -2
- advisor/domain/core/lattice.py +2 -5
- advisor/domain/core/sample.py +9 -2
- advisor/domain/orientation.py +219 -0
- advisor/domain/orientation_calculator.py +174 -0
- advisor/features/__init__.py +7 -4
- advisor/features/scattering_geometry/domain/brillouin_calculator.py +133 -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/ui/dialogs/__init__.py +7 -0
- advisor/ui/dialogs/diffraction_test_dialog.py +264 -0
- advisor/ui/init_window.py +33 -0
- {advisor_scattering-0.5.2.dist-info → advisor_scattering-0.9.1.dist-info}/METADATA +4 -12
- {advisor_scattering-0.5.2.dist-info → advisor_scattering-0.9.1.dist-info}/RECORD +22 -18
- {advisor_scattering-0.5.2.dist-info → advisor_scattering-0.9.1.dist-info}/WHEEL +1 -1
- {advisor_scattering-0.5.2.dist-info → advisor_scattering-0.9.1.dist-info}/entry_points.txt +0 -0
- {advisor_scattering-0.5.2.dist-info → advisor_scattering-0.9.1.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
|
|
@@ -108,6 +105,18 @@ class BrillouinCalculator:
|
|
|
108
105
|
print(f"Error initializing calculator: {str(e)}")
|
|
109
106
|
return False
|
|
110
107
|
|
|
108
|
+
def change_energy(self, energy):
|
|
109
|
+
"""Change the energy of the X-ray source, in eV"""
|
|
110
|
+
self.energy = energy
|
|
111
|
+
self.lambda_A = self.ev_to_lambda / self.energy
|
|
112
|
+
self.k_in = 2 * np.pi / self.lambda_A
|
|
113
|
+
return True
|
|
114
|
+
|
|
115
|
+
def reorient_sample(self, roll, pitch, yaw):
|
|
116
|
+
"""Reorient the sample with respect to the lab"""
|
|
117
|
+
self.lab.reorient(roll, pitch, yaw)
|
|
118
|
+
return True
|
|
119
|
+
|
|
111
120
|
def _sample_to_lab_conversion(self, a_vec, b_vec, c_vec):
|
|
112
121
|
"""Convert vectors from sample coordinate system to lab coordinate system."""
|
|
113
122
|
# For now, just return the same vectors
|
|
@@ -145,24 +154,34 @@ class BrillouinCalculator:
|
|
|
145
154
|
):
|
|
146
155
|
"""Calculate scattering angles from HKL indices.
|
|
147
156
|
|
|
148
|
-
|
|
157
|
+
Returns up to two solutions if they exist.
|
|
149
158
|
|
|
150
159
|
Args:
|
|
151
|
-
|
|
160
|
+
H, K, L (float): HKL indices
|
|
161
|
+
fixed_angle (float): Value of the fixed angle in degrees
|
|
162
|
+
fixed_angle_name (str): Name of the fixed angle ('chi' or 'phi')
|
|
152
163
|
|
|
153
164
|
Returns:
|
|
154
|
-
dict: Dictionary containing
|
|
165
|
+
dict: Dictionary containing:
|
|
166
|
+
- tth (list): Scattering angles in degrees
|
|
167
|
+
- theta (list): Sample theta rotation values in degrees
|
|
168
|
+
- phi (list): Sample phi rotation values in degrees
|
|
169
|
+
- chi (list): Sample chi rotation values in degrees
|
|
170
|
+
- H, K, L (float): Input HKL indices
|
|
171
|
+
- number_of_solutions (int): Number of distinct solutions found
|
|
172
|
+
- feasible (list): Boolean list indicating if each solution is feasible
|
|
173
|
+
- success (bool): Whether calculation succeeded
|
|
174
|
+
- error (str or None): Error message if any
|
|
155
175
|
"""
|
|
156
176
|
|
|
157
177
|
if not self.is_initialized():
|
|
158
178
|
raise ValueError("Calculator not initialized")
|
|
159
179
|
|
|
160
|
-
|
|
161
180
|
calculate_angles = _calculate_angles_factory(fixed_angle_name)
|
|
162
181
|
a, b, c, alpha, beta, gamma = self.lab.get_lattice_parameters()
|
|
163
182
|
roll, pitch, yaw = self.lab.get_lattice_angles()
|
|
164
183
|
try:
|
|
165
|
-
|
|
184
|
+
result = calculate_angles(
|
|
166
185
|
self.k_in,
|
|
167
186
|
H,
|
|
168
187
|
K,
|
|
@@ -181,19 +200,31 @@ class BrillouinCalculator:
|
|
|
181
200
|
except Exception as e:
|
|
182
201
|
return {
|
|
183
202
|
"success": False,
|
|
184
|
-
"error": "No solution found; The Q point is
|
|
203
|
+
"error": "No solution found; The Q point is possibly not reachable at this energy and/or scattering angle tth. System message:" + str(e),
|
|
185
204
|
}
|
|
205
|
+
|
|
206
|
+
# Extract lists from result
|
|
207
|
+
tth_list = result["tth"]
|
|
208
|
+
theta_list = result["theta"]
|
|
209
|
+
phi_list = result["phi"]
|
|
210
|
+
chi_list = result["chi"]
|
|
211
|
+
num_solutions = result["number_of_solutions"]
|
|
212
|
+
|
|
213
|
+
# Calculate feasibility for each solution
|
|
214
|
+
feasible_list = [is_feasible(theta_list[i], tth_list[i]) for i in range(len(theta_list))]
|
|
215
|
+
|
|
186
216
|
return {
|
|
187
|
-
"tth":
|
|
188
|
-
"theta":
|
|
189
|
-
"phi":
|
|
190
|
-
"chi":
|
|
217
|
+
"tth": tth_list,
|
|
218
|
+
"theta": theta_list,
|
|
219
|
+
"phi": phi_list,
|
|
220
|
+
"chi": chi_list,
|
|
191
221
|
"H": H,
|
|
192
222
|
"K": K,
|
|
193
223
|
"L": L,
|
|
224
|
+
"number_of_solutions": num_solutions,
|
|
194
225
|
"success": True,
|
|
195
226
|
"error": None,
|
|
196
|
-
"feasible":
|
|
227
|
+
"feasible": feasible_list,
|
|
197
228
|
}
|
|
198
229
|
|
|
199
230
|
def calculate_angles_tth_fixed(
|
|
@@ -205,52 +236,77 @@ class BrillouinCalculator:
|
|
|
205
236
|
fixed_angle_name="chi",
|
|
206
237
|
fixed_angle=0.0,
|
|
207
238
|
):
|
|
239
|
+
"""Calculate scattering angles with fixed tth.
|
|
240
|
+
|
|
241
|
+
Returns up to two solutions if they exist.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
tth (float): Fixed scattering angle in degrees
|
|
245
|
+
H, K, L (float): HKL indices (one should be None to be solved)
|
|
246
|
+
fixed_angle_name (str): Name of the fixed angle ('chi' or 'phi')
|
|
247
|
+
fixed_angle (float): Value of the fixed angle in degrees
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
dict: Dictionary containing lists of solutions and metadata
|
|
251
|
+
"""
|
|
208
252
|
a, b, c, alpha, beta, gamma = self.lab.get_lattice_parameters()
|
|
209
253
|
roll, pitch, yaw = self.lab.get_lattice_angles()
|
|
210
254
|
|
|
211
255
|
try:
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
fixed_angle,
|
|
230
|
-
)
|
|
256
|
+
result = _calculate_angles_tth_fixed(
|
|
257
|
+
self.k_in,
|
|
258
|
+
tth,
|
|
259
|
+
a,
|
|
260
|
+
b,
|
|
261
|
+
c,
|
|
262
|
+
alpha,
|
|
263
|
+
beta,
|
|
264
|
+
gamma,
|
|
265
|
+
roll,
|
|
266
|
+
pitch,
|
|
267
|
+
yaw,
|
|
268
|
+
H,
|
|
269
|
+
K,
|
|
270
|
+
L,
|
|
271
|
+
fixed_angle_name,
|
|
272
|
+
fixed_angle,
|
|
231
273
|
)
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
274
|
+
|
|
275
|
+
# Extract data from result
|
|
276
|
+
tth_list = result["tth"]
|
|
277
|
+
theta_list = result["theta"]
|
|
278
|
+
phi_list = result["phi"]
|
|
279
|
+
chi_list = result["chi"]
|
|
280
|
+
momentum = result["momentum"]
|
|
281
|
+
num_solutions = result["number_of_solutions"]
|
|
282
|
+
|
|
283
|
+
# Update the solved HKL component
|
|
284
|
+
H_result = momentum if H is None else H
|
|
285
|
+
K_result = momentum if K is None else K
|
|
286
|
+
L_result = momentum if L is None else L
|
|
287
|
+
|
|
235
288
|
except Exception as e:
|
|
236
289
|
return {
|
|
237
290
|
"success": False,
|
|
238
|
-
"error": "No solution found; The Q point is
|
|
291
|
+
"error": "No solution found; The Q point is possibly not reachable at this energy and/or scattering angle tth. System message:" + str(e),
|
|
239
292
|
}
|
|
240
293
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
"
|
|
246
|
-
"
|
|
247
|
-
"
|
|
248
|
-
"
|
|
294
|
+
# Calculate feasibility for each solution
|
|
295
|
+
feasible_list = [is_feasible(theta_list[i], tth_list[i]) for i in range(len(theta_list))]
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
"tth": tth_list,
|
|
299
|
+
"theta": theta_list,
|
|
300
|
+
"phi": phi_list,
|
|
301
|
+
"chi": chi_list,
|
|
302
|
+
"H": H_result,
|
|
303
|
+
"K": K_result,
|
|
304
|
+
"L": L_result,
|
|
305
|
+
"number_of_solutions": num_solutions,
|
|
249
306
|
"success": True,
|
|
250
307
|
"error": None,
|
|
251
|
-
"feasible":
|
|
308
|
+
"feasible": feasible_list,
|
|
252
309
|
}
|
|
253
|
-
return result
|
|
254
310
|
|
|
255
311
|
def calculate_angles_tth_fixed_scan(
|
|
256
312
|
self,
|
|
@@ -264,6 +320,9 @@ class BrillouinCalculator:
|
|
|
264
320
|
):
|
|
265
321
|
"""Calculate scattering angles for a range of HKL values with fixed tth.
|
|
266
322
|
|
|
323
|
+
Each HKL point may have up to 2 solutions. Results are flattened into lists
|
|
324
|
+
with solution_group indicating which original HKL point each solution belongs to.
|
|
325
|
+
|
|
267
326
|
Args:
|
|
268
327
|
tth (float): Fixed scattering angle in degrees
|
|
269
328
|
start_points (tuple): Starting HKL values (the deactivated index will be ignored)
|
|
@@ -315,7 +374,7 @@ class BrillouinCalculator:
|
|
|
315
374
|
else [None] * num_points
|
|
316
375
|
)
|
|
317
376
|
|
|
318
|
-
# Initialize result lists
|
|
377
|
+
# Initialize result lists - flattened to include all solutions
|
|
319
378
|
all_tth = []
|
|
320
379
|
all_theta = []
|
|
321
380
|
all_phi = []
|
|
@@ -323,6 +382,9 @@ class BrillouinCalculator:
|
|
|
323
382
|
all_h = []
|
|
324
383
|
all_k = []
|
|
325
384
|
all_l = []
|
|
385
|
+
all_feasible = []
|
|
386
|
+
all_solution_group = [] # Track which HKL point each solution belongs to
|
|
387
|
+
all_solution_index = [] # Track if it's solution 1 or 2 within the group
|
|
326
388
|
|
|
327
389
|
# Calculate for each point
|
|
328
390
|
for i in range(num_points):
|
|
@@ -344,14 +406,20 @@ class BrillouinCalculator:
|
|
|
344
406
|
# Skip points that fail to calculate but don't fail completely
|
|
345
407
|
continue
|
|
346
408
|
|
|
347
|
-
# Each calculation
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
409
|
+
# Each calculation returns lists of solutions
|
|
410
|
+
num_solutions = result.get("number_of_solutions", 1)
|
|
411
|
+
for sol_idx in range(num_solutions):
|
|
412
|
+
all_tth.append(result["tth"][sol_idx])
|
|
413
|
+
all_theta.append(result["theta"][sol_idx])
|
|
414
|
+
all_phi.append(result["phi"][sol_idx])
|
|
415
|
+
all_chi.append(result["chi"][sol_idx])
|
|
416
|
+
all_h.append(result["H"])
|
|
417
|
+
all_k.append(result["K"])
|
|
418
|
+
all_l.append(result["L"])
|
|
419
|
+
all_feasible.append(result["feasible"][sol_idx])
|
|
420
|
+
all_solution_group.append(i) # Which HKL point
|
|
421
|
+
all_solution_index.append(sol_idx + 1) # Solution 1 or 2
|
|
422
|
+
|
|
355
423
|
except Exception as e:
|
|
356
424
|
# Log the error but continue with other points
|
|
357
425
|
print(f"Error calculating point {(h, k, l)}: {str(e)}")
|
|
@@ -371,10 +439,12 @@ class BrillouinCalculator:
|
|
|
371
439
|
"H": all_h,
|
|
372
440
|
"K": all_k,
|
|
373
441
|
"L": all_l,
|
|
374
|
-
"deactivated_index": deactivated_index,
|
|
442
|
+
"deactivated_index": deactivated_index,
|
|
443
|
+
"solution_group": all_solution_group,
|
|
444
|
+
"solution_index": all_solution_index,
|
|
375
445
|
"success": True,
|
|
376
446
|
"error": None,
|
|
377
|
-
"feasible":
|
|
447
|
+
"feasible": all_feasible,
|
|
378
448
|
}
|
|
379
449
|
|
|
380
450
|
def is_initialized(self):
|