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 double rotor frame order model.""" 
 24   
 25   
 26  from math import cos, pi, sin 
 27  from numpy import add, divide, dot, eye, float64, multiply, sinc, swapaxes, tensordot 
 28  try: 
 29      from scipy.integrate import dblquad 
 30  except ImportError: 
 31      pass 
 32   
 33   
 34  from lib.compat import norm 
 35  from lib.frame_order.matrix_ops import rotate_daeg 
 36   
 37   
 39      """Generate the 1st degree Frame Order matrix for the double rotor model. 
 40   
 41      @param matrix:      The Frame Order matrix, 1st degree to be populated. 
 42      @type matrix:       numpy 3D, rank-2 array 
 43      @param R_eigen:     The eigenframe rotation matrix. 
 44      @type R_eigen:      numpy 3D, rank-2 array 
 45      @param smax1:       The maximum torsion angle for the first rotor. 
 46      @type smax1:        float 
 47      @param smax2:       The maximum torsion angle for the second rotor. 
 48      @type smax2:        float 
 49      """ 
 50   
 51       
 52      sinc_smax1 = sinc(smax1/pi) 
 53      sinc_smax2 = sinc(smax2/pi) 
 54   
 55       
 56      matrix[0, 0] = sinc_smax1 
 57      matrix[1, 1] = sinc_smax2 
 58      matrix[2, 2] = sinc_smax1 * sinc_smax2 
 59   
 60       
 61      return rotate_daeg(matrix, R_eigen) 
  62   
 63   
 65      """Generate the rotated 2nd degree Frame Order matrix for the double rotor model. 
 66   
 67      The cone axis is assumed to be parallel to the z-axis in the eigenframe. 
 68   
 69   
 70      @param matrix:      The Frame Order matrix, 2nd degree to be populated. 
 71      @type matrix:       numpy 9D, rank-2 array 
 72      @param Rx2_eigen:   The Kronecker product of the eigenframe rotation matrix with itself. 
 73      @type Rx2_eigen:    numpy 9D, rank-2 array 
 74      @param smax1:       The maximum torsion angle for the first rotor. 
 75      @type smax1:        float 
 76      @param smax2:       The maximum torsion angle for the second rotor. 
 77      @type smax2:        float 
 78      """ 
 79   
 80       
 81      matrix[:] = 0.0 
 82   
 83       
 84      sinc_smax1 = sinc(smax1/pi) 
 85      sinc_2smax1 = sinc(2.0*smax1/pi) 
 86      sinc_2smax1p1 = sinc_2smax1 + 1.0 
 87      sinc_2smax1n1 = sinc_2smax1 - 1.0 
 88      sinc_smax2 = sinc(smax2/pi) 
 89      sinc_2smax2 = sinc(2.0*smax2/pi) 
 90      sinc_2smax2p1 = sinc_2smax2 + 1.0 
 91      sinc_2smax2n1 = sinc_2smax2 - 1.0 
 92   
 93       
 94      matrix[0, 0] = sinc_2smax1 + 1.0 
 95      matrix[1, 1] = 2.0 * sinc_smax1 * sinc_smax2 
 96      matrix[2, 2] = sinc_smax2 * sinc_2smax1p1 
 97      matrix[3, 3] = matrix[1, 1] 
 98      matrix[4, 4] = sinc_2smax2p1 
 99      matrix[5, 5] = sinc_smax1 * sinc_2smax2p1 
100      matrix[6, 6] = matrix[2, 2] 
101      matrix[7, 7] = matrix[5, 5] 
102      matrix[8, 8] = 0.5 * sinc_2smax1p1 * sinc_2smax2p1 
103   
104       
105      matrix[4, 0] = 0.5 * sinc_2smax1n1 * sinc_2smax2n1 
106      matrix[0, 8] = -sinc_2smax1n1 
107      matrix[8, 0] = -0.5 * sinc_2smax1n1 * sinc_2smax2p1 
108      matrix[4, 8] = -0.5 * sinc_2smax1p1 * sinc_2smax2n1 
109      matrix[8, 4] = -sinc_2smax2n1 
110   
111       
112      matrix[2, 6] = matrix[6, 2] = sinc_smax2 * sinc_2smax1n1 
113      matrix[5, 7] = matrix[7, 5] = sinc_smax1 * sinc_2smax2n1 
114   
115       
116      multiply(0.5, matrix, matrix) 
117   
118       
119      return rotate_daeg(matrix, Rx2_eigen) 
 120   
121   
122 -def pcs_numeric_qr_int_double_rotor(points=None, max_points=None, sigma_max=None, sigma_max_2=None, c=None, full_in_ref_frame=None, r_pivot_atom=None, r_pivot_atom_rev=None, r_ln_pivot=None, r_inter_pivot=None, A=None, R_eigen=None, RT_eigen=None, Ri_prime=None, Ri2_prime=None, pcs_theta=None, pcs_theta_err=None, missing_pcs=None): 
 123      """The averaged PCS value via numerical integration for the double rotor frame order model. 
124   
125      @keyword points:            The Sobol points in the torsion-tilt angle space. 
126      @type points:               numpy rank-2, 3D array 
127      @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. 
128      @type max_points:           int 
129      @keyword sigma_max:         The maximum opening angle for the first rotor. 
130      @type sigma_max:            float 
131      @keyword sigma_max_2:       The maximum opening angle for the second rotor. 
132      @type sigma_max_2:          float 
133      @keyword c:                 The PCS constant (without the interatomic distance and in Angstrom units). 
134      @type c:                    numpy rank-1 array 
135      @keyword full_in_ref_frame: An array of flags specifying if the tensor in the reference frame is the full or reduced tensor. 
136      @type full_in_ref_frame:    numpy rank-1 array 
137      @keyword r_pivot_atom:      The pivot point to atom vector. 
138      @type r_pivot_atom:         numpy rank-2, 3D array 
139      @keyword r_pivot_atom_rev:  The reversed pivot point to atom vector. 
140      @type r_pivot_atom_rev:     numpy rank-2, 3D array 
141      @keyword r_ln_pivot:        The lanthanide position to pivot point vector. 
142      @type r_ln_pivot:           numpy rank-2, 3D array 
143      @keyword r_inter_pivot:     The vector between the two pivots. 
144      @type r_inter_pivot:        numpy rank-1, 3D array 
145      @keyword A:                 The full alignment tensor of the non-moving domain. 
146      @type A:                    numpy rank-2, 3D array 
147      @keyword R_eigen:           The eigenframe rotation matrix. 
148      @type R_eigen:              numpy rank-2, 3D array 
149      @keyword RT_eigen:          The transpose of the eigenframe rotation matrix (for faster calculations). 
150      @type RT_eigen:             numpy rank-2, 3D array 
151      @keyword Ri_prime:          The array of pre-calculated rotation matrices for the in-frame double rotor motion for the 1st mode of motion, used to calculate the PCS for each state i in the numerical integration. 
152      @type Ri_prime:             numpy rank-3, array of 3D arrays 
153      @keyword Ri2_prime:         The array of pre-calculated rotation matrices for the in-frame double rotor motion for the 2nd mode of motion, used to calculate the PCS for each state i in the numerical integration. 
154      @type Ri2_prime:            numpy rank-3, array of 3D arrays 
155      @keyword pcs_theta:         The storage structure for the back-calculated PCS values. 
156      @type pcs_theta:            numpy rank-2 array 
157      @keyword pcs_theta_err:     The storage structure for the back-calculated PCS errors. 
158      @type pcs_theta_err:        numpy rank-2 array 
159      @keyword missing_pcs:       A structure used to indicate which PCS values are missing. 
160      @type missing_pcs:          numpy rank-2 array 
161      """ 
162   
163       
164      pcs_theta[:] = 0.0 
165      pcs_theta_err[:] = 0.0 
166   
167       
168      Ri = dot(R_eigen, tensordot(Ri_prime, RT_eigen, axes=1)) 
169      Ri = swapaxes(Ri, 0, 1) 
170      Ri2 = dot(R_eigen, tensordot(Ri2_prime, RT_eigen, axes=1)) 
171      Ri2 = swapaxes(Ri2, 0, 1) 
172   
173       
174      sigma, sigma2 = points 
175   
176       
177      num = 0 
178      for i in range(len(points[0])): 
179           
180          if num == max_points: 
181              break 
182   
183           
184          if abs(sigma[i]) > sigma_max: 
185              continue 
186          if abs(sigma2[i]) > sigma_max_2: 
187              continue 
188   
189           
190          pcs_pivot_motion_double_rotor_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, r_inter_pivot=r_inter_pivot, A=A, Ri=Ri[i], Ri2=Ri2[i], pcs_theta=pcs_theta, pcs_theta_err=pcs_theta_err, missing_pcs=missing_pcs) 
191   
192           
193          num += 1 
194   
195       
196      if num == 0: 
197           
198          Ri_prime = eye(3, dtype=float64) 
199          Ri = dot(R_eigen, tensordot(Ri_prime, RT_eigen, axes=1)) 
200          Ri = swapaxes(Ri, 0, 1) 
201   
202           
203          pcs_pivot_motion_double_rotor_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, r_inter_pivot=r_inter_pivot, A=A, Ri=Ri, Ri2=Ri, pcs_theta=pcs_theta, pcs_theta_err=pcs_theta_err, missing_pcs=missing_pcs) 
204   
205           
206          multiply(c, pcs_theta, pcs_theta) 
207   
208       
209      else: 
210          multiply(c, pcs_theta, pcs_theta) 
211          divide(pcs_theta, float(num), pcs_theta) 
 212   
213   
214 -def pcs_numeric_quad_int_double_rotor(sigma_max=None, sigma_max_2=None, c=None, r_pivot_atom=None, r_ln_pivot=None, r_inter_pivot=None, A=None, R_eigen=None, RT_eigen=None, Ri_prime=None, Ri2_prime=None): 
 215      """Determine the averaged PCS value via SciPy quadratic numerical integration. 
216   
217      @keyword sigma_max:         The maximum opening angle for the first rotor. 
218      @type sigma_max:            float 
219      @keyword sigma_max_2:       The maximum opening angle for the second rotor. 
220      @type sigma_max_2:          float 
221      @keyword c:                 The PCS constant (without the interatomic distance and in Angstrom units). 
222      @type c:                    numpy rank-1 array 
223      @keyword r_pivot_atom:      The pivot point to atom vector. 
224      @type r_pivot_atom:         numpy rank-1, 3D array 
225      @keyword r_ln_pivot:        The lanthanide position to pivot point vector. 
226      @type r_ln_pivot:           numpy rank-1, 3D array 
227      @keyword r_inter_pivot:     The vector between the two pivots. 
228      @type r_inter_pivot:        numpy rank-1, 3D array 
229      @keyword A:                 The full alignment tensor of the non-moving domain. 
230      @type A:                    numpy rank-2, 3D array 
231      @keyword R_eigen:           The eigenframe rotation matrix. 
232      @type R_eigen:              numpy rank-2, 3D array 
233      @keyword RT_eigen:          The transpose of the eigenframe rotation matrix (for faster calculations). 
234      @type RT_eigen:             numpy rank-2, 3D array 
235      @keyword Ri_prime:          The array of pre-calculated rotation matrices for the in-frame double rotor motion for the 1st mode of motion, used to calculate the PCS for each state i in the numerical integration. 
236      @type Ri_prime:             numpy rank-2, 3D array 
237      @keyword Ri2_prime:         The array of pre-calculated rotation matrices for the in-frame double rotor motion for the 2nd mode of motion, used to calculate the PCS for each state i in the numerical integration. 
238      @type Ri2_prime:            numpy rank-2, 3D array 
239      """ 
240   
241       
242      Ri_prime[0, 1] = 0.0 
243      Ri_prime[1, 0] = 0.0 
244      Ri_prime[1, 1] = 1.0 
245      Ri_prime[1, 2] = 0.0 
246      Ri_prime[2, 1] = 0.0 
247   
248       
249      Ri2_prime[0, 0] = 1.0 
250      Ri2_prime[0, 1] = 0.0 
251      Ri2_prime[0, 2] = 0.0 
252      Ri2_prime[1, 0] = 0.0 
253      Ri2_prime[2, 0] = 0.0 
254   
255       
256      result = dblquad(pcs_pivot_motion_double_rotor_quad_int, -sigma_max, sigma_max, lambda sigma2: -sigma_max_2, lambda sigma2: sigma_max_2, args=(r_pivot_atom, r_ln_pivot, r_inter_pivot, A, R_eigen, RT_eigen, Ri_prime, Ri2_prime)) 
257   
258       
259      SA = 4.0 * sigma_max * sigma_max_2 
260   
261       
262      return c * result[0] / SA 
 263   
264   
265 -def pcs_pivot_motion_double_rotor_qr_int(full_in_ref_frame=None, r_pivot_atom=None, r_pivot_atom_rev=None, r_ln_pivot=None, r_inter_pivot=None, A=None, Ri=None, Ri2=None, pcs_theta=None, pcs_theta_err=None, missing_pcs=None): 
 266      """Calculate the PCS value after a pivoted motion for the double rotor model. 
267   
268      @keyword full_in_ref_frame: An array of flags specifying if the tensor in the reference frame is the full or reduced tensor. 
269      @type full_in_ref_frame:    numpy rank-1 array 
270      @keyword r_pivot_atom:      The pivot point to atom vector. 
271      @type r_pivot_atom:         numpy rank-2, 3D array 
272      @keyword r_pivot_atom_rev:  The reversed pivot point to atom vector. 
273      @type r_pivot_atom_rev:     numpy rank-2, 3D array 
274      @keyword r_ln_pivot:        The lanthanide position to pivot point vector. 
275      @type r_ln_pivot:           numpy rank-2, 3D array 
276      @keyword r_inter_pivot:     The vector between the two pivots. 
277      @type r_inter_pivot:        numpy rank-1, 3D array 
278      @keyword A:                 The full alignment tensor of the non-moving domain. 
279      @type A:                    numpy rank-2, 3D array 
280      @keyword Ri:                The frame-shifted, pre-calculated rotation matrix for state i for the 1st mode of motion. 
281      @type Ri:                   numpy rank-2, 3D array 
282      @keyword Ri2:               The frame-shifted, pre-calculated rotation matrix for state i for the 2nd mode of motion. 
283      @type Ri2:                  numpy rank-2, 3D array 
284      @keyword pcs_theta:         The storage structure for the back-calculated PCS values. 
285      @type pcs_theta:            numpy rank-2 array 
286      @keyword pcs_theta_err:     The storage structure for the back-calculated PCS errors. 
287      @type pcs_theta_err:        numpy rank-2 array 
288      @keyword missing_pcs:       A structure used to indicate which PCS values are missing. 
289      @type missing_pcs:          numpy rank-2 array 
290      """ 
291   
292       
293      rot_vect = dot(r_pivot_atom, Ri) 
294   
295       
296      add(r_inter_pivot, rot_vect, rot_vect) 
297   
298       
299      rot_vect = dot(rot_vect, Ri2) 
300   
301       
302      add(rot_vect, r_ln_pivot, rot_vect) 
303   
304       
305      length = 1.0 / norm(rot_vect, axis=1)**5 
306   
307       
308      if min(full_in_ref_frame) == 0: 
309          rot_vect_rev = dot(r_pivot_atom_rev, Ri) 
310          add(r_inter_pivot, rot_vect_rev, rot_vect_rev) 
311          rot_vect_rev = dot(rot_vect_rev, Ri2) 
312          add(rot_vect_rev, r_ln_pivot, rot_vect_rev) 
313          length_rev = 1.0 / norm(rot_vect_rev, axis=1)**5 
314   
315       
316      for j in range(len(r_pivot_atom[:, 0])): 
317           
318          for i in range(len(pcs_theta)): 
319               
320              if missing_pcs[i, j]: 
321                  continue 
322   
323               
324              if full_in_ref_frame[i]: 
325                  proj = dot(rot_vect[j], dot(A[i], rot_vect[j])) 
326                  length_i = length[j] 
327              else: 
328                  proj = dot(rot_vect_rev[j], dot(A[i], rot_vect_rev[j])) 
329                  length_i = length_rev[j] 
330   
331               
332              pcs_theta[i, j] += proj * length_i 
 333   
334   
336      """Calculate the PCS value after a pivoted motion for the double rotor model. 
337   
338      @param sigma_i:             The 1st torsion angle for state i. 
339      @type sigma_i:              float 
340      @param sigma2_i:            The 1st torsion angle for state i. 
341      @type sigma2_i:             float 
342      @param r_pivot_atom:        The pivot point to atom vector. 
343      @type r_pivot_atom:         numpy rank-2, 3D array 
344      @param r_ln_pivot:          The lanthanide position to pivot point vector. 
345      @type r_ln_pivot:           numpy rank-2, 3D array 
346      @param r_inter_pivot:       The vector between the two pivots. 
347      @type r_inter_pivot:        numpy rank-1, 3D array 
348      @param A:                   The full alignment tensor of the non-moving domain. 
349      @type A:                    numpy rank-2, 3D array 
350      @param R_eigen:             The eigenframe rotation matrix. 
351      @type R_eigen:              numpy rank-2, 3D array 
352      @param RT_eigen:            The transpose of the eigenframe rotation matrix (for faster calculations). 
353      @type RT_eigen:             numpy rank-2, 3D array 
354      @param Ri_prime:            The empty rotation matrix for state i. 
355      @type Ri_prime:             numpy rank-2, 3D array 
356      @param Ri2_prime:           The 2nd empty rotation matrix for state i. 
357      @type Ri2_prime:            numpy rank-2, 3D array 
358      @return:                    The PCS value for the changed position. 
359      @rtype:                     float 
360      """ 
361   
362       
363      c_sigma = cos(sigma_i) 
364      s_sigma = sin(sigma_i) 
365      Ri_prime[0, 0] =  c_sigma 
366      Ri_prime[0, 2] =  s_sigma 
367      Ri_prime[2, 0] = -s_sigma 
368      Ri_prime[2, 2] =  c_sigma 
369   
370       
371      c_sigma = cos(sigma2_i) 
372      s_sigma = sin(sigma2_i) 
373      Ri2_prime[1, 1] =  c_sigma 
374      Ri2_prime[1, 2] = -s_sigma 
375      Ri2_prime[2, 1] =  s_sigma 
376      Ri2_prime[2, 2] =  c_sigma 
377   
378       
379      Ri = dot(R_eigen, dot(Ri_prime, RT_eigen)) 
380      Ri2 = dot(R_eigen, dot(Ri2_prime, RT_eigen)) 
381   
382       
383      rot_vect = dot(r_pivot_atom, Ri) 
384   
385       
386      add(r_inter_pivot, rot_vect, rot_vect) 
387   
388       
389      rot_vect = dot(rot_vect, Ri2) 
390   
391       
392      add(rot_vect, r_ln_pivot, rot_vect) 
393   
394       
395      length = norm(rot_vect) 
396   
397       
398      proj = dot(rot_vect, dot(A, rot_vect)) 
399   
400       
401      pcs = proj / length**5 
402   
403       
404      return pcs 
 405