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, sinc, swapaxes, tensordot 
 28  try: 
 29      from scipy.integrate import quad 
 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 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 sigma_max:   The maximum torsion angle. 
 46      @type sigma_max:    float 
 47      """ 
 48   
 49       
 50      matrix[:] = 0.0 
 51   
 52       
 53      val = sinc(sigma_max/pi) 
 54   
 55       
 56      matrix[0, 0] = val 
 57      matrix[1, 1] = val 
 58      matrix[2, 2] = 1.0 
 59   
 60       
 61      return rotate_daeg(matrix, R_eigen) 
  62   
 63   
 65      """Generate the rotated 2nd degree Frame Order matrix for the 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 smax:        The maximum torsion angle. 
 75      @type smax:         float 
 76      """ 
 77   
 78       
 79      matrix[:] = 0.0 
 80   
 81       
 82      sinc_smax = sinc(smax/pi) 
 83      sinc_2smax = sinc(2.0*smax/pi) 
 84   
 85       
 86      matrix[0, 0] = (sinc_2smax + 1.0) / 2.0 
 87      matrix[1, 1] = matrix[0, 0] 
 88      matrix[2, 2] = sinc_smax 
 89      matrix[3, 3] = matrix[0, 0] 
 90      matrix[4, 4] = matrix[0, 0] 
 91      matrix[5, 5] = matrix[2, 2] 
 92      matrix[6, 6] = matrix[2, 2] 
 93      matrix[7, 7] = matrix[2, 2] 
 94      matrix[8, 8] = 1.0 
 95   
 96       
 97      matrix[0, 4] = matrix[4, 0] = -(sinc_2smax - 1.0) / 2.0 
 98   
 99       
100      matrix[1, 3] = matrix[3, 1] = -matrix[0, 4] 
101   
102       
103      return rotate_daeg(matrix, Rx2_eigen) 
 104   
105   
106 -def pcs_numeric_qr_int_rotor(points=None, max_points=None, sigma_max=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): 
 107      """Determine the averaged PCS value via numerical integration. 
108   
109      @keyword points:            The Sobol points in the torsion-tilt angle space. 
110      @type points:               numpy rank-2, 3D array 
111      @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. 
112      @type max_points:           int 
113      @keyword sigma_max:         The maximum rotor angle. 
114      @type sigma_max:            float 
115      @keyword c:                 The PCS constant (without the interatomic distance and in Angstrom units). 
116      @type c:                    numpy rank-1 array 
117      @keyword full_in_ref_frame: An array of flags specifying if the tensor in the reference frame is the full or reduced tensor. 
118      @type full_in_ref_frame:    numpy rank-1 array 
119      @keyword r_pivot_atom:      The pivot point to atom vector. 
120      @type r_pivot_atom:         numpy rank-2, 3D array 
121      @keyword r_pivot_atom_rev:  The reversed pivot point to atom vector. 
122      @type r_pivot_atom_rev:     numpy rank-2, 3D array 
123      @keyword r_ln_pivot:        The lanthanide position to pivot point vector. 
124      @type r_ln_pivot:           numpy rank-2, 3D array 
125      @keyword A:                 The full alignment tensor of the non-moving domain. 
126      @type A:                    numpy rank-2, 3D array 
127      @keyword R_eigen:           The eigenframe rotation matrix. 
128      @type R_eigen:              numpy rank-2, 3D array 
129      @keyword RT_eigen:          The transpose of the eigenframe rotation matrix (for faster calculations). 
130      @type RT_eigen:             numpy rank-2, 3D array 
131      @keyword Ri_prime:          The array of pre-calculated rotation matrices for the in-frame rotor motion, used to calculate the PCS for each state i in the numerical integration. 
132      @type Ri_prime:             numpy rank-3, array of 3D arrays 
133      @keyword pcs_theta:         The storage structure for the back-calculated PCS values. 
134      @type pcs_theta:            numpy rank-2 array 
135      @keyword pcs_theta_err:     The storage structure for the back-calculated PCS errors. 
136      @type pcs_theta_err:        numpy rank-2 array 
137      @keyword missing_pcs:       A structure used to indicate which PCS values are missing. 
138      @type missing_pcs:          numpy rank-2 array 
139      """ 
140   
141       
142      pcs_theta[:] = 0.0 
143      pcs_theta_err[:] = 0.0 
144   
145       
146      Ri = dot(R_eigen, tensordot(Ri_prime, RT_eigen, axes=1)) 
147      Ri = swapaxes(Ri, 0, 1) 
148   
149       
150      sigma = points[0] 
151   
152       
153      num = 0 
154      for i in range(len(points[0])): 
155           
156          if num == max_points: 
157              break 
158   
159           
160          if abs(sigma[i]) > sigma_max: 
161              continue 
162   
163           
164          pcs_pivot_motion_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, A=A, Ri=Ri[i], pcs_theta=pcs_theta, pcs_theta_err=pcs_theta_err, missing_pcs=missing_pcs) 
165   
166           
167          num += 1 
168   
169       
170      if num == 0: 
171           
172          Ri_prime = eye(3, dtype=float64) 
173          Ri = dot(R_eigen, tensordot(Ri_prime, RT_eigen, axes=1)) 
174          Ri = swapaxes(Ri, 0, 1) 
175   
176           
177          pcs_pivot_motion_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, A=A, Ri=Ri, pcs_theta=pcs_theta, pcs_theta_err=pcs_theta_err, missing_pcs=missing_pcs) 
178   
179           
180          multiply(c, pcs_theta, pcs_theta) 
181   
182       
183      else: 
184          multiply(c, pcs_theta, pcs_theta) 
185          divide(pcs_theta, float(num), pcs_theta) 
 186   
187   
188 -def pcs_numeric_quad_int_rotor(sigma_max=None, c=None, r_pivot_atom=None, r_ln_pivot=None, A=None, R_eigen=None, RT_eigen=None, Ri_prime=None): 
 189      """Determine the averaged PCS value via SciPy quadratic numerical integration. 
190   
191      @keyword sigma_max:     The maximum rotor angle. 
192      @type sigma_max:        float 
193      @keyword c:             The PCS constant (without the interatomic distance and in Angstrom units). 
194      @type c:                float 
195      @keyword r_pivot_atom:  The pivot point to atom vector. 
196      @type r_pivot_atom:     numpy rank-1, 3D array 
197      @keyword r_ln_pivot:    The lanthanide position to pivot point vector. 
198      @type r_ln_pivot:       numpy rank-1, 3D array 
199      @keyword A:             The full alignment tensor of the non-moving domain. 
200      @type A:                numpy rank-2, 3D array 
201      @keyword R_eigen:       The eigenframe rotation matrix. 
202      @type R_eigen:          numpy rank-2, 3D array 
203      @keyword RT_eigen:      The transpose of the eigenframe rotation matrix (for faster calculations). 
204      @type RT_eigen:         numpy rank-2, 3D array 
205      @keyword Ri_prime:      The empty rotation matrix for the in-frame rotor motion, used to calculate the PCS for each state i in the numerical integration. 
206      @type Ri_prime:         numpy rank-2, 3D array 
207      @return:                The averaged PCS value. 
208      @rtype:                 float 
209      """ 
210   
211       
212      Ri_prime[0, 2] = 0.0 
213      Ri_prime[1, 2] = 0.0 
214      Ri_prime[2, 0] = 0.0 
215      Ri_prime[2, 1] = 0.0 
216      Ri_prime[2, 2] = 1.0 
217   
218       
219      result = quad(pcs_pivot_motion_rotor_quad_int, -sigma_max, sigma_max, args=(r_pivot_atom, r_ln_pivot, A, R_eigen, RT_eigen, Ri_prime)) 
220   
221       
222      SA = 2.0 * sigma_max 
223   
224       
225      return c * result[0] / SA 
 226   
227   
228 -def pcs_pivot_motion_rotor_qr_int(full_in_ref_frame=None, r_pivot_atom=None, r_pivot_atom_rev=None, r_ln_pivot=None, A=None, Ri=None, pcs_theta=None, pcs_theta_err=None, missing_pcs=None): 
 229      """Calculate the PCS value after a pivoted motion for the rotor model. 
230   
231      @keyword full_in_ref_frame: An array of flags specifying if the tensor in the reference frame is the full or reduced tensor. 
232      @type full_in_ref_frame:    numpy rank-1 array 
233      @keyword r_pivot_atom:      The pivot point to atom vector. 
234      @type r_pivot_atom:         numpy rank-2, 3D array 
235      @keyword r_pivot_atom_rev:  The reversed pivot point to atom vector. 
236      @type r_pivot_atom_rev:     numpy rank-2, 3D array 
237      @keyword r_ln_pivot:        The lanthanide position to pivot point vector. 
238      @type r_ln_pivot:           numpy rank-2, 3D array 
239      @keyword A:                 The full alignment tensor of the non-moving domain. 
240      @type A:                    numpy rank-2, 3D array 
241      @keyword Ri:                The frame-shifted, pre-calculated rotation matrix for state i. 
242      @type Ri:                   numpy rank-2, 3D array 
243      @keyword pcs_theta:         The storage structure for the back-calculated PCS values. 
244      @type pcs_theta:            numpy rank-2 array 
245      @keyword pcs_theta_err:     The storage structure for the back-calculated PCS errors. 
246      @type pcs_theta_err:        numpy rank-2 array 
247      @keyword missing_pcs:       A structure used to indicate which PCS values are missing. 
248      @type missing_pcs:          numpy rank-2 array 
249      """ 
250   
251       
252      rot_vect = dot(r_pivot_atom, Ri) + r_ln_pivot 
253   
254       
255      length = 1.0 / norm(rot_vect, axis=1)**5 
256   
257       
258      if min(full_in_ref_frame) == 0: 
259          rot_vect_rev = dot(r_pivot_atom_rev, Ri) + r_ln_pivot 
260          length_rev = 1.0 / norm(rot_vect_rev, axis=1)**5 
261   
262       
263      for j in range(len(r_pivot_atom[:, 0])): 
264           
265          for i in range(len(pcs_theta)): 
266               
267              if missing_pcs[i, j]: 
268                  continue 
269   
270               
271              if full_in_ref_frame[i]: 
272                  proj = dot(rot_vect[j], dot(A[i], rot_vect[j])) 
273                  length_i = length[j] 
274              else: 
275                  proj = dot(rot_vect_rev[j], dot(A[i], rot_vect_rev[j])) 
276                  length_i = length_rev[j] 
277   
278               
279              pcs_theta[i, j] += proj * length_i 
 280   
281   
283      """Calculate the PCS value after a pivoted motion for the rotor model. 
284   
285      @param sigma_i:             The rotor angle for state i. 
286      @type sigma_i:              float 
287      @param r_pivot_atom:        The pivot point to atom vector. 
288      @type r_pivot_atom:         numpy rank-1, 3D array 
289      @param r_ln_pivot:          The lanthanide position to pivot point vector. 
290      @type r_ln_pivot:           numpy rank-1, 3D array 
291      @param A:                   The full alignment tensor of the non-moving domain. 
292      @type A:                    numpy rank-2, 3D array 
293      @param R_eigen:             The eigenframe rotation matrix. 
294      @type R_eigen:              numpy rank-2, 3D array 
295      @param RT_eigen:            The transpose of the eigenframe rotation matrix (for faster calculations). 
296      @type RT_eigen:             numpy rank-2, 3D array 
297      @param Ri_prime:            The empty rotation matrix for the in-frame rotor motion for state i. 
298      @type Ri_prime:             numpy rank-2, 3D array 
299      @return:                    The PCS value for the changed position. 
300      @rtype:                     float 
301      """ 
302   
303       
304      c_sigma = cos(sigma_i) 
305      s_sigma = sin(sigma_i) 
306      Ri_prime[0, 0] =  c_sigma 
307      Ri_prime[0, 1] = -s_sigma 
308      Ri_prime[1, 0] =  s_sigma 
309      Ri_prime[1, 1] =  c_sigma 
310   
311       
312      R_i = dot(R_eigen, dot(Ri_prime, RT_eigen)) 
313   
314       
315      vect = dot(R_i, r_pivot_atom) + r_ln_pivot 
316   
317       
318      length = norm(vect) 
319   
320       
321      proj = dot(vect, dot(A, vect)) 
322   
323       
324      pcs = proj / length**5 
325   
326       
327      return pcs 
 328