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 import dep_check
27
28
29 from math import cos, pi, sin, sqrt
30 if dep_check.scipy_module:
31 from scipy.integrate import dblquad, quad
32
33
34 from lib.geometry.pec import pec
35 from lib.frame_order.matrix_ops import pcs_pivot_motion_torsionless, pcs_pivot_motion_torsionless_qrint, rotate_daeg
36 from lib.frame_order.pseudo_ellipse import tmax_pseudo_ellipse
37
38
40 """Generate the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
41
42 @param matrix: The Frame Order matrix, 2nd degree to be populated.
43 @type matrix: numpy 9D, rank-2 array
44 @param Rx2_eigen: The Kronecker product of the eigenframe rotation matrix with itself.
45 @type Rx2_eigen: numpy 9D, 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 = 1.0 / (6.0 * pec(theta_x, theta_y))
54
55
56 matrix[0, 0] = fact * (6.0*pi + quad(part_int_daeg2_pseudo_ellipse_torsionless_00, -pi, pi, args=(theta_x, theta_y), full_output=1)[0])
57 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])
58 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])
59 matrix[3, 3] = matrix[1, 1]
60 matrix[4, 4] = fact * (6.0*pi + quad(part_int_daeg2_pseudo_ellipse_torsionless_44, -pi, pi, args=(theta_x, theta_y), full_output=1)[0])
61 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])
62 matrix[6, 6] = matrix[2, 2]
63 matrix[7, 7] = matrix[5, 5]
64 matrix[8, 8] = fact * quad(part_int_daeg2_pseudo_ellipse_torsionless_88, -pi, pi, args=(theta_x, theta_y), full_output=1)[0]
65
66
67 matrix[0, 4] = matrix[4, 0] = fact * (2.0*pi + quad(part_int_daeg2_pseudo_ellipse_torsionless_04, -pi, pi, args=(theta_x, theta_y), full_output=1)[0])
68 matrix[0, 8] = matrix[8, 0] = fact * (4.0*pi + quad(part_int_daeg2_pseudo_ellipse_torsionless_08, -pi, pi, args=(theta_x, theta_y), full_output=1)[0])
69 matrix[4, 8] = matrix[8, 4] = fact * (4.0*pi + quad(part_int_daeg2_pseudo_ellipse_torsionless_48, -pi, pi, args=(theta_x, theta_y), full_output=1)[0])
70
71
72 matrix[1, 3] = matrix[3, 1] = matrix[0, 4]
73 matrix[2, 6] = matrix[6, 2] = -matrix[0, 8]
74 matrix[5, 7] = matrix[7, 5] = -matrix[4, 8]
75
76
77 return rotate_daeg(matrix, Rx2_eigen)
78
79
81 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
82
83 @param phi: The azimuthal tilt-torsion angle.
84 @type phi: float
85 @param x: The cone opening angle along x.
86 @type x: float
87 @param y: The cone opening angle along y.
88 @type y: float
89 @return: The theta partial integral.
90 @rtype: float
91 """
92
93
94 tmax = tmax_pseudo_ellipse(phi, x, y)
95
96
97 return (2*cos(phi)**4*cos(tmax) + 6*cos(phi)**2*sin(phi)**2)*sin(tmax)**2 - (6*sin(phi)**4 + 2*cos(phi)**4)*cos(tmax)
98
99
101 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
102
103 @param phi: The azimuthal tilt-torsion angle.
104 @type phi: float
105 @param x: The cone opening angle along x.
106 @type x: float
107 @param y: The cone opening angle along y.
108 @type y: float
109 @return: The theta partial integral.
110 @rtype: float
111 """
112
113
114 tmax = tmax_pseudo_ellipse(phi, x, y)
115
116
117 return (2*cos(phi)**2*sin(phi)**2*cos(tmax) - 6*cos(phi)**2*sin(phi)**2)*sin(tmax)**2 - 8*cos(phi)**2*sin(phi)**2*cos(tmax)
118
119
121 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
122
123 @param phi: The azimuthal tilt-torsion angle.
124 @type phi: float
125 @param x: The cone opening angle along x.
126 @type x: float
127 @param y: The cone opening angle along y.
128 @type y: float
129 @return: The theta partial integral.
130 @rtype: float
131 """
132
133
134 tmax = tmax_pseudo_ellipse(phi, x, y)
135
136
137 return 2*cos(phi)**2*cos(tmax)**3 - 6*cos(phi)**2*cos(tmax)
138
139
141 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
142
143 @param phi: The azimuthal tilt-torsion angle.
144 @type phi: float
145 @param x: The cone opening angle along x.
146 @type x: float
147 @param y: The cone opening angle along y.
148 @type y: float
149 @return: The theta partial integral.
150 @rtype: float
151 """
152
153
154 tmax = tmax_pseudo_ellipse(phi, x, y)
155
156
157 return (2*cos(phi)**2*sin(phi)**2*cos(tmax) + 3*sin(phi)**4 + 3*cos(phi)**4)*sin(tmax)**2 - 8*cos(phi)**2*sin(phi)**2*cos(tmax)
158
159
161 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
162
163 @param phi: The azimuthal tilt-torsion angle.
164 @type phi: float
165 @param x: The cone opening angle along x.
166 @type x: float
167 @param y: The cone opening angle along y.
168 @type y: float
169 @return: The theta partial integral.
170 @rtype: float
171 """
172
173
174 tmax = tmax_pseudo_ellipse(phi, x, y)
175
176
177 return (2*sin(phi)**2 - 2)*cos(tmax)**3 - 3*sin(phi)**2*cos(tmax)**2
178
179
181 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
182
183 @param phi: The azimuthal tilt-torsion angle.
184 @type phi: float
185 @param x: The cone opening angle along x.
186 @type x: float
187 @param y: The cone opening angle along y.
188 @type y: float
189 @return: The theta partial integral.
190 @rtype: float
191 """
192
193
194 tmax = tmax_pseudo_ellipse(phi, x, y)
195
196
197 return (2*sin(phi)**4*cos(tmax) + 6*cos(phi)**2*sin(phi)**2)*sin(tmax)**2 - (2*sin(phi)**4 + 6*cos(phi)**4)*cos(tmax)
198
199
201 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
202
203 @param phi: The azimuthal tilt-torsion angle.
204 @type phi: float
205 @param x: The cone opening angle along x.
206 @type x: float
207 @param y: The cone opening angle along y.
208 @type y: float
209 @return: The theta partial integral.
210 @rtype: float
211 """
212
213
214 tmax = tmax_pseudo_ellipse(phi, x, y)
215
216
217 return 2*sin(phi)**2*cos(tmax)**3 - 6*sin(phi)**2*cos(tmax)
218
219
221 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
222
223 @param phi: The azimuthal tilt-torsion angle.
224 @type phi: float
225 @param x: The cone opening angle along x.
226 @type x: float
227 @param y: The cone opening angle along y.
228 @type y: float
229 @return: The theta partial integral.
230 @rtype: float
231 """
232
233
234 tmax = tmax_pseudo_ellipse(phi, x, y)
235
236
237 return (2*cos(phi)**2 - 2)*cos(tmax)**3 - 3*cos(phi)**2*cos(tmax)**2
238
239
241 """The theta partial integral of the 2nd degree Frame Order matrix for the torsionless pseudo-ellipse.
242
243 @param phi: The azimuthal tilt-torsion angle.
244 @type phi: float
245 @param x: The cone opening angle along x.
246 @type x: float
247 @param y: The cone opening angle along y.
248 @type y: float
249 @return: The theta partial integral.
250 @rtype: float
251 """
252
253
254 tmax = tmax_pseudo_ellipse(phi, x, y)
255
256
257 return 2 - 2*cos(tmax)**3
258
259
261 """Determine the averaged PCS value via numerical integration.
262
263 @keyword theta_x: The x-axis half cone angle.
264 @type theta_x: float
265 @keyword theta_y: The y-axis half cone angle.
266 @type theta_y: float
267 @keyword c: The PCS constant (without the interatomic distance and in Angstrom units).
268 @type c: float
269 @keyword r_pivot_atom: The pivot point to atom vector.
270 @type r_pivot_atom: numpy rank-1, 3D array
271 @keyword r_ln_pivot: The lanthanide position to pivot point vector.
272 @type r_ln_pivot: numpy rank-1, 3D array
273 @keyword A: The full alignment tensor of the non-moving domain.
274 @type A: numpy rank-2, 3D array
275 @keyword R_eigen: The eigenframe rotation matrix.
276 @type R_eigen: numpy rank-2, 3D array
277 @keyword RT_eigen: The transpose of the eigenframe rotation matrix (for faster calculations).
278 @type RT_eigen: numpy rank-2, 3D array
279 @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.
280 @type Ri_prime: numpy rank-2, 3D array
281 @return: The averaged PCS value.
282 @rtype: float
283 """
284
285 def pseudo_ellipse(phi):
286 """The pseudo-ellipse wrapper formula."""
287
288 return tmax_pseudo_ellipse(phi, theta_x, theta_y)
289
290
291 result = dblquad(pcs_pivot_motion_torsionless, -pi, pi, lambda phi: 0.0, pseudo_ellipse, args=(r_pivot_atom, r_ln_pivot, A, R_eigen, RT_eigen, Ri_prime))
292
293
294 SA = pec(theta_x, theta_y)
295
296
297 return c * result[0] / SA
298
299
300 -def pcs_numeric_int_pseudo_ellipse_torsionless_qrint(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, error_flag=False):
301 """Determine the averaged PCS value via numerical integration.
302
303 @keyword points: The Sobol points in the torsion-tilt angle space.
304 @type points: numpy rank-2, 3D array
305 @keyword theta_x: The x-axis half cone angle.
306 @type theta_x: float
307 @keyword theta_y: The y-axis half cone angle.
308 @type theta_y: float
309 @keyword c: The PCS constant (without the interatomic distance and in Angstrom units).
310 @type c: numpy rank-1 array
311 @keyword full_in_ref_frame: An array of flags specifying if the tensor in the reference frame is the full or reduced tensor.
312 @type full_in_ref_frame: numpy rank-1 array
313 @keyword r_pivot_atom: The pivot point to atom vector.
314 @type r_pivot_atom: numpy rank-2, 3D array
315 @keyword r_pivot_atom_rev: The reversed pivot point to atom vector.
316 @type r_pivot_atom_rev: numpy rank-2, 3D array
317 @keyword r_ln_pivot: The lanthanide position to pivot point vector.
318 @type r_ln_pivot: numpy rank-2, 3D array
319 @keyword A: The full alignment tensor of the non-moving domain.
320 @type A: numpy rank-2, 3D array
321 @keyword R_eigen: The eigenframe rotation matrix.
322 @type R_eigen: numpy rank-2, 3D array
323 @keyword RT_eigen: The transpose of the eigenframe rotation matrix (for faster calculations).
324 @type RT_eigen: numpy rank-2, 3D array
325 @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.
326 @type Ri_prime: numpy rank-2, 3D array
327 @keyword pcs_theta: The storage structure for the back-calculated PCS values.
328 @type pcs_theta: numpy rank-2 array
329 @keyword pcs_theta_err: The storage structure for the back-calculated PCS errors.
330 @type pcs_theta_err: numpy rank-2 array
331 @keyword missing_pcs: A structure used to indicate which PCS values are missing.
332 @type missing_pcs: numpy rank-2 array
333 @keyword error_flag: A flag which if True will cause the PCS errors to be estimated and stored in pcs_theta_err.
334 @type error_flag: bool
335 """
336
337
338 for i in range(len(pcs_theta)):
339 for j in range(len(pcs_theta[i])):
340 pcs_theta[i, j] = 0.0
341 pcs_theta_err[i, j] = 0.0
342
343
344 num = 0
345 for i in range(len(points)):
346
347 theta, phi = points[i]
348
349
350 theta_max = tmax_pseudo_ellipse(phi, theta_x, theta_y)
351
352
353 if theta > theta_max:
354 continue
355
356
357 pcs_pivot_motion_torsionless_qrint(theta_i=theta, phi_i=phi, 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, R_eigen=R_eigen, RT_eigen=RT_eigen, Ri_prime=Ri_prime, pcs_theta=pcs_theta, pcs_theta_err=pcs_theta_err, missing_pcs=missing_pcs)
358
359
360 num += 1
361
362
363 for i in range(len(pcs_theta)):
364 for j in range(len(pcs_theta[i])):
365
366 pcs_theta[i, j] = c[i] * pcs_theta[i, j] / float(num)
367
368
369 if error_flag:
370 pcs_theta_err[i, j] = abs(pcs_theta_err[i, j] / float(num) - pcs_theta[i, j]**2) / float(num)
371 pcs_theta_err[i, j] = c[i] * sqrt(pcs_theta_err[i, j])
372 print("%8.3f +/- %-8.3f" % (pcs_theta[i, j]*1e6, pcs_theta_err[i, j]*1e6))
373