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