1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """Module for the handling of Frame Order."""
24
25
26 from math import cos, pi, sin
27 from numpy import divide, dot, eye, float64, multiply, swapaxes, tensordot
28 try:
29 from scipy.integrate import dblquad, quad
30 except ImportError:
31 pass
32
33
34 from lib.geometry.pec import pec
35 from lib.frame_order.matrix_ops import pcs_pivot_motion_torsionless_qr_int, pcs_pivot_motion_torsionless_quad_int, rotate_daeg
36 from lib.frame_order.pseudo_ellipse import tmax_pseudo_ellipse, tmax_pseudo_ellipse_array
37
38
40 """Generate the 1st degree Frame Order matrix for the torsionless pseudo-ellipse.
41
42 @param matrix: The Frame Order matrix, 1st degree to be populated.
43 @type matrix: numpy 3D, rank-2 array
44 @param R_eigen: The eigenframe rotation matrix.
45 @type R_eigen: numpy 3D, rank-2 array
46 @param theta_x: The cone opening angle along x.
47 @type theta_x: float
48 @param theta_y: The cone opening angle along y.
49 @type theta_y: float
50 """
51
52
53 fact = 2.0 * pec(theta_x, theta_y)
54
55
56 if fact == 0.0:
57 fact = 1e100
58 else:
59 fact = 1.0 / fact
60
61
62 matrix[0, 0] = fact * (2.0*pi + quad(part_int_daeg1_pseudo_ellipse_00, -pi, pi, args=(theta_x, theta_y), full_output=1)[0])
63 matrix[1, 1] = fact * (2.0*pi + quad(part_int_daeg1_pseudo_ellipse_11, -pi, pi, args=(theta_x, theta_y), full_output=1)[0])
64 matrix[2, 2] = fact * quad(part_int_daeg1_pseudo_ellipse_22, -pi, pi, args=(theta_x, theta_y), full_output=1)[0]
65
66
67 return rotate_daeg(matrix, R_eigen)
68
69
71 """Generate the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
72
73 @param matrix: The Frame Order matrix, 2nd degree to be populated.
74 @type matrix: numpy 9D, rank-2 array
75 @param Rx2_eigen: The Kronecker product of the eigenframe rotation matrix with itself.
76 @type Rx2_eigen: numpy 9D, rank-2 array
77 @param theta_x: The cone opening angle along x.
78 @type theta_x: float
79 @param theta_y: The cone opening angle along y.
80 @type theta_y: float
81 """
82
83
84 if theta_x == 0.0:
85
86 matrix[:] = 0.0
87 for i in range(len(matrix)):
88 matrix[i, i] = 1.0
89
90
91 return rotate_daeg(matrix, Rx2_eigen)
92
93
94 fact = 6.0 * pec(theta_x, theta_y)
95 fact2 = 0.5 * fact
96
97
98 if fact == 0.0:
99 fact = 1e100
100 fact2 = 1e100
101 else:
102 fact = 1.0 / fact
103 fact2 = 1.0 / fact2
104
105
106 matrix[0, 0] = fact2 * (3.0*pi + quad(part_int_daeg2_pseudo_ellipse_torsionless_00, -pi, pi, args=(theta_x, theta_y), full_output=1)[0])
107 matrix[1, 1] = fact * (2.0*pi + quad(part_int_daeg2_pseudo_ellipse_torsionless_11, -pi, pi, args=(theta_x, theta_y), full_output=1)[0])
108 matrix[2, 2] = fact * (5.0*pi - quad(part_int_daeg2_pseudo_ellipse_torsionless_22, -pi, pi, args=(theta_x, theta_y), full_output=1)[0])
109 matrix[3, 3] = matrix[1, 1]
110 matrix[4, 4] = fact2 * (3.0*pi + quad(part_int_daeg2_pseudo_ellipse_torsionless_44, -pi, pi, args=(theta_x, theta_y), full_output=1)[0])
111 matrix[5, 5] = fact * (5.0*pi - quad(part_int_daeg2_pseudo_ellipse_torsionless_55, -pi, pi, args=(theta_x, theta_y), full_output=1)[0])
112 matrix[6, 6] = matrix[2, 2]
113 matrix[7, 7] = matrix[5, 5]
114 matrix[8, 8] = fact2 * (2.0*pi - quad(part_int_daeg2_pseudo_ellipse_torsionless_88, -pi, pi, args=(theta_x, theta_y), full_output=1)[0])
115
116
117 matrix[0, 4] = matrix[4, 0] = fact2 * (pi + quad(part_int_daeg2_pseudo_ellipse_torsionless_04, -pi, pi, args=(theta_x, theta_y), full_output=1)[0])
118 matrix[0, 8] = matrix[8, 0] = fact2 * (2.0*pi + quad(part_int_daeg2_pseudo_ellipse_torsionless_08, -pi, pi, args=(theta_x, theta_y), full_output=1)[0])
119 matrix[4, 8] = matrix[8, 4] = fact2 * (2.0*pi + quad(part_int_daeg2_pseudo_ellipse_torsionless_48, -pi, pi, args=(theta_x, theta_y), full_output=1)[0])
120
121
122 matrix[1, 3] = matrix[3, 1] = matrix[0, 4]
123 matrix[2, 6] = matrix[6, 2] = -matrix[0, 8]
124 matrix[5, 7] = matrix[7, 5] = -matrix[4, 8]
125
126
127 return rotate_daeg(matrix, Rx2_eigen)
128
129
131 """The theta-sigma partial integral of the 1st degree Frame Order matrix element 00 for the pseudo-ellipse.
132
133 @param phi: The azimuthal tilt-torsion angle.
134 @type phi: float
135 @param x: The cone opening angle along x.
136 @type x: float
137 @param y: The cone opening angle along y.
138 @type y: float
139 @return: The theta-sigma partial integral.
140 @rtype: float
141 """
142
143
144 tmax = tmax_pseudo_ellipse(phi, x, y)
145
146
147 return cos(phi)**2 * sin(tmax)**2 - 2.0 * sin(phi)**2 * cos(tmax)
148
149
151 """The theta-sigma partial integral of the 1st degree Frame Order matrix element 11 for the pseudo-ellipse.
152
153 @param phi: The azimuthal tilt-torsion angle.
154 @type phi: float
155 @param x: The cone opening angle along x.
156 @type x: float
157 @param y: The cone opening angle along y.
158 @type y: float
159 @return: The theta-sigma partial integral.
160 @rtype: float
161 """
162
163
164 tmax = tmax_pseudo_ellipse(phi, x, y)
165
166
167 return sin(phi)**2 * sin(tmax)**2 - 2.0 * cos(phi)**2 * cos(tmax)
168
169
171 """The theta-sigma partial integral of the 1st degree Frame Order matrix element 22 for the pseudo-ellipse.
172
173 @param phi: The azimuthal tilt-torsion angle.
174 @type phi: float
175 @param x: The cone opening angle along x.
176 @type x: float
177 @param y: The cone opening angle along y.
178 @type y: float
179 @return: The theta-sigma partial integral.
180 @rtype: float
181 """
182
183
184 tmax = tmax_pseudo_ellipse(phi, x, y)
185
186
187 return sin(tmax)**2
188
189
191 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
192
193 @param phi: The azimuthal tilt-torsion angle.
194 @type phi: float
195 @param x: The cone opening angle along x.
196 @type x: float
197 @param y: The cone opening angle along y.
198 @type y: float
199 @return: The theta partial integral.
200 @rtype: float
201 """
202
203
204 tmax = tmax_pseudo_ellipse(phi, x, y)
205
206
207 return (cos(phi)**4*cos(tmax) + 3.0*cos(phi)**2.0*sin(phi)**2)*sin(tmax)**2 - (3.0*sin(phi)**4 + cos(phi)**4)*cos(tmax)
208
209
211 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
212
213 @param phi: The azimuthal tilt-torsion angle.
214 @type phi: float
215 @param x: The cone opening angle along x.
216 @type x: float
217 @param y: The cone opening angle along y.
218 @type y: float
219 @return: The theta partial integral.
220 @rtype: float
221 """
222
223
224 tmax = tmax_pseudo_ellipse(phi, x, y)
225
226
227 return (cos(phi)**2*sin(phi)**2*cos(tmax) - 3.0*cos(phi)**2*sin(phi)**2)*sin(tmax)**2 - 4.0*cos(phi)**2*sin(phi)**2*cos(tmax)
228
229
231 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
232
233 @param phi: The azimuthal tilt-torsion angle.
234 @type phi: float
235 @param x: The cone opening angle along x.
236 @type x: float
237 @param y: The cone opening angle along y.
238 @type y: float
239 @return: The theta partial integral.
240 @rtype: float
241 """
242
243
244 tmax = tmax_pseudo_ellipse(phi, x, y)
245
246
247 return cos(phi)**2*cos(tmax)**3 - 3.0*cos(phi)**2*cos(tmax)
248
249
251 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
252
253 @param phi: The azimuthal tilt-torsion angle.
254 @type phi: float
255 @param x: The cone opening angle along x.
256 @type x: float
257 @param y: The cone opening angle along y.
258 @type y: float
259 @return: The theta partial integral.
260 @rtype: float
261 """
262
263
264 tmax = tmax_pseudo_ellipse(phi, x, y)
265
266
267 return (2.0*cos(phi)**2*sin(phi)**2*cos(tmax) + 3.0*sin(phi)**4 + 3.0*cos(phi)**4)*sin(tmax)**2 - 8.0*cos(phi)**2*sin(phi)**2*cos(tmax)
268
269
271 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
272
273 @param phi: The azimuthal tilt-torsion angle.
274 @type phi: float
275 @param x: The cone opening angle along x.
276 @type x: float
277 @param y: The cone opening angle along y.
278 @type y: float
279 @return: The theta partial integral.
280 @rtype: float
281 """
282
283
284 tmax = tmax_pseudo_ellipse(phi, x, y)
285
286
287 return 2.0*cos(phi)**2*cos(tmax)**3 + 3.0*sin(phi)**2*cos(tmax)**2
288
289
291 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
292
293 @param phi: The azimuthal tilt-torsion angle.
294 @type phi: float
295 @param x: The cone opening angle along x.
296 @type x: float
297 @param y: The cone opening angle along y.
298 @type y: float
299 @return: The theta partial integral.
300 @rtype: float
301 """
302
303
304 tmax = tmax_pseudo_ellipse(phi, x, y)
305
306
307 return (sin(phi)**4*cos(tmax) + 3.0*cos(phi)**2*sin(phi)**2)*sin(tmax)**2 - (sin(phi)**4 + 3.0*cos(phi)**4)*cos(tmax)
308
309
311 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
312
313 @param phi: The azimuthal tilt-torsion angle.
314 @type phi: float
315 @param x: The cone opening angle along x.
316 @type x: float
317 @param y: The cone opening angle along y.
318 @type y: float
319 @return: The theta partial integral.
320 @rtype: float
321 """
322
323
324 tmax = tmax_pseudo_ellipse(phi, x, y)
325
326
327 return sin(phi)**2*cos(tmax)**3 - 3.0*sin(phi)**2*cos(tmax)
328
329
331 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
332
333 @param phi: The azimuthal tilt-torsion angle.
334 @type phi: float
335 @param x: The cone opening angle along x.
336 @type x: float
337 @param y: The cone opening angle along y.
338 @type y: float
339 @return: The theta partial integral.
340 @rtype: float
341 """
342
343
344 tmax = tmax_pseudo_ellipse(phi, x, y)
345
346
347 return 2.0*sin(phi)**2*cos(tmax)**3 + 3.0*cos(phi)**2*cos(tmax)**2
348
349
351 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
352
353 @param phi: The azimuthal tilt-torsion angle.
354 @type phi: float
355 @param x: The cone opening angle along x.
356 @type x: float
357 @param y: The cone opening angle along y.
358 @type y: float
359 @return: The theta partial integral.
360 @rtype: float
361 """
362
363
364 tmax = tmax_pseudo_ellipse(phi, x, y)
365
366
367 return cos(tmax)**3
368
369
370 -def pcs_numeric_qr_int_pseudo_ellipse_torsionless(points=None, max_points=None, theta_x=None, theta_y=None, c=None, full_in_ref_frame=None, r_pivot_atom=None, r_pivot_atom_rev=None, r_ln_pivot=None, A=None, R_eigen=None, RT_eigen=None, Ri_prime=None, pcs_theta=None, pcs_theta_err=None, missing_pcs=None):
371 """Determine the averaged PCS value via numerical integration.
372
373 @keyword points: The Sobol points in the torsion-tilt angle space.
374 @type points: numpy rank-2, 3D array
375 @keyword max_points: The maximum number of Sobol' points to use. Once this number is reached, the loop over the Sobol' torsion-tilt angles is terminated.
376 @type max_points: int
377 @keyword theta_x: The x-axis half cone angle.
378 @type theta_x: float
379 @keyword theta_y: The y-axis half cone angle.
380 @type theta_y: float
381 @keyword c: The PCS constant (without the interatomic distance and in Angstrom units).
382 @type c: numpy rank-1 array
383 @keyword full_in_ref_frame: An array of flags specifying if the tensor in the reference frame is the full or reduced tensor.
384 @type full_in_ref_frame: numpy rank-1 array
385 @keyword r_pivot_atom: The pivot point to atom vector.
386 @type r_pivot_atom: numpy rank-2, 3D array
387 @keyword r_pivot_atom_rev: The reversed pivot point to atom vector.
388 @type r_pivot_atom_rev: numpy rank-2, 3D array
389 @keyword r_ln_pivot: The lanthanide position to pivot point vector.
390 @type r_ln_pivot: numpy rank-2, 3D array
391 @keyword A: The full alignment tensor of the non-moving domain.
392 @type A: numpy rank-2, 3D array
393 @keyword R_eigen: The eigenframe rotation matrix.
394 @type R_eigen: numpy rank-2, 3D array
395 @keyword RT_eigen: The transpose of the eigenframe rotation matrix (for faster calculations).
396 @type RT_eigen: numpy rank-2, 3D array
397 @keyword Ri_prime: The array of pre-calculated rotation matrices for the in-frame torsionless, pseudo-elliptic cone motion, used to calculate the PCS for each state i in the numerical integration.
398 @type Ri_prime: numpy rank-3, array of 3D arrays
399 @keyword pcs_theta: The storage structure for the back-calculated PCS values.
400 @type pcs_theta: numpy rank-2 array
401 @keyword pcs_theta_err: The storage structure for the back-calculated PCS errors.
402 @type pcs_theta_err: numpy rank-2 array
403 @keyword missing_pcs: A structure used to indicate which PCS values are missing.
404 @type missing_pcs: numpy rank-2 array
405 """
406
407
408 pcs_theta[:] = 0.0
409 pcs_theta_err[:] = 0.0
410
411
412 Ri = dot(R_eigen, tensordot(Ri_prime, RT_eigen, axes=1))
413 Ri = swapaxes(Ri, 0, 1)
414
415
416 theta, phi = points
417
418
419 theta_max = tmax_pseudo_ellipse_array(phi, theta_x, theta_y)
420
421
422 num = 0
423 for i in range(len(points[0])):
424
425 if num == max_points:
426 break
427
428
429 if theta[i] > theta_y:
430 continue
431
432
433 if theta[i] > theta_max[i]:
434 continue
435
436
437 pcs_pivot_motion_torsionless_qr_int(full_in_ref_frame=full_in_ref_frame, r_pivot_atom=r_pivot_atom, r_pivot_atom_rev=r_pivot_atom_rev, r_ln_pivot=r_ln_pivot, A=A, Ri=Ri[i], pcs_theta=pcs_theta, pcs_theta_err=pcs_theta_err, missing_pcs=missing_pcs)
438
439
440 num += 1
441
442
443 if num == 0:
444
445 Ri_prime = eye(3, dtype=float64)
446 Ri = dot(R_eigen, tensordot(Ri_prime, RT_eigen, axes=1))
447 Ri = swapaxes(Ri, 0, 1)
448
449
450 pcs_pivot_motion_torsionless_qr_int(full_in_ref_frame=full_in_ref_frame, r_pivot_atom=r_pivot_atom, r_pivot_atom_rev=r_pivot_atom_rev, r_ln_pivot=r_ln_pivot, A=A, Ri=Ri, pcs_theta=pcs_theta, pcs_theta_err=pcs_theta_err, missing_pcs=missing_pcs)
451
452
453 multiply(c, pcs_theta, pcs_theta)
454
455
456 else:
457 multiply(c, pcs_theta, pcs_theta)
458 divide(pcs_theta, float(num), pcs_theta)
459
460
462 """Determine the averaged PCS value via numerical integration.
463
464 @keyword theta_x: The x-axis half cone angle.
465 @type theta_x: float
466 @keyword theta_y: The y-axis half cone angle.
467 @type theta_y: float
468 @keyword c: The PCS constant (without the interatomic distance and in Angstrom units).
469 @type c: float
470 @keyword r_pivot_atom: The pivot point to atom vector.
471 @type r_pivot_atom: numpy rank-1, 3D array
472 @keyword r_ln_pivot: The lanthanide position to pivot point vector.
473 @type r_ln_pivot: numpy rank-1, 3D array
474 @keyword A: The full alignment tensor of the non-moving domain.
475 @type A: numpy rank-2, 3D array
476 @keyword R_eigen: The eigenframe rotation matrix.
477 @type R_eigen: numpy rank-2, 3D array
478 @keyword RT_eigen: The transpose of the eigenframe rotation matrix (for faster calculations).
479 @type RT_eigen: numpy rank-2, 3D array
480 @keyword Ri_prime: The empty rotation matrix for the in-frame isotropic cone motion, used to calculate the PCS for each state i in the numerical integration.
481 @type Ri_prime: numpy rank-2, 3D array
482 @return: The averaged PCS value.
483 @rtype: float
484 """
485
486 def pseudo_ellipse(phi):
487 """The pseudo-ellipse wrapper formula."""
488
489 return tmax_pseudo_ellipse(phi, theta_x, theta_y)
490
491
492 result = dblquad(pcs_pivot_motion_torsionless_quad_int, -pi, pi, lambda phi: 0.0, pseudo_ellipse, args=(r_pivot_atom, r_ln_pivot, A, R_eigen, RT_eigen, Ri_prime))
493
494
495 SA = pec(theta_x, theta_y)
496
497
498 return c * result[0] / SA
499