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