Package lib :: Package frame_order :: Module rotor
[hide private]
[frames] | no frames]

Source Code for Module lib.frame_order.rotor

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2009-2012,2014 Edward d'Auvergne                              # 
  4  #                                                                             # 
  5  # This file is part of the program relax (http://www.nmr-relax.com).          # 
  6  #                                                                             # 
  7  # This program is free software: you can redistribute it and/or modify        # 
  8  # it under the terms of the GNU General Public License as published by        # 
  9  # the Free Software Foundation, either version 3 of the License, or           # 
 10  # (at your option) any later version.                                         # 
 11  #                                                                             # 
 12  # This program is distributed in the hope that it will be useful,             # 
 13  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 15  # GNU General Public License for more details.                                # 
 16  #                                                                             # 
 17  # You should have received a copy of the GNU General Public License           # 
 18  # along with this program.  If not, see <http://www.gnu.org/licenses/>.       # 
 19  #                                                                             # 
 20  ############################################################################### 
 21   
 22  # Module docstring. 
 23  """Module for the handling of Frame Order.""" 
 24   
 25  # Python module imports. 
 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  # relax module imports. 
 34  from lib.compat import norm 
 35  from lib.frame_order.matrix_ops import rotate_daeg 
 36   
 37   
38 -def compile_1st_matrix_rotor(matrix, R_eigen, sigma_max):
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 # Zeros. 50 matrix[:] = 0.0 51 52 # The sinc value. 53 val = sinc(sigma_max/pi) 54 55 # Diagonal values. 56 matrix[0, 0] = val 57 matrix[1, 1] = val 58 matrix[2, 2] = 1.0 59 60 # Rotate and return the frame order matrix. 61 return rotate_daeg(matrix, R_eigen)
62 63
64 -def compile_2nd_matrix_rotor(matrix, Rx2_eigen, smax):
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 # Zeros. 79 matrix[:] = 0.0 80 81 # Repetitive trig calculations. 82 sinc_smax = sinc(smax/pi) 83 sinc_2smax = sinc(2.0*smax/pi) 84 85 # Diagonal. 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 # Off diagonal set 1. 97 matrix[0, 4] = matrix[4, 0] = -(sinc_2smax - 1.0) / 2.0 98 99 # Off diagonal set 2. 100 matrix[1, 3] = matrix[3, 1] = -matrix[0, 4] 101 102 # Rotate and return the frame order matrix. 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 # Clear the data structures. 142 pcs_theta[:] = 0.0 143 pcs_theta_err[:] = 0.0 144 145 # Fast frame shift. 146 Ri = dot(R_eigen, tensordot(Ri_prime, RT_eigen, axes=1)) 147 Ri = swapaxes(Ri, 0, 1) 148 149 # Unpack the points (in this case, just an alias). 150 sigma = points[0] 151 152 # Loop over the samples. 153 num = 0 154 for i in range(len(points[0])): 155 # The maximum number of points has been reached (well, surpassed by one so exit the loop before it is used). 156 if num == max_points: 157 break 158 159 # Outside of the distribution, so skip the point. 160 if abs(sigma[i]) > sigma_max: 161 continue 162 163 # Calculate the PCSs for this state. 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 # Increment the number of points. 167 num += 1 168 169 # Default to the rigid state if no points lie in the distribution. 170 if num == 0: 171 # Fast identity frame shift. 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 # Calculate the PCSs for this state. 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 # Multiply the constant. 180 multiply(c, pcs_theta, pcs_theta) 181 182 # Average the PCS. 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 # Preset the rotation matrix elements for state i. 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 # Perform numerical integration. 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 # The surface area normalisation factor. 222 SA = 2.0 * sigma_max 223 224 # Return the value. 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 # Pre-calculate all the new vectors. 252 rot_vect = dot(r_pivot_atom, Ri) + r_ln_pivot 253 254 # The vector length (to the 5th power). 255 length = 1.0 / norm(rot_vect, axis=1)**5 256 257 # The reverse vectors and lengths. 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 # Loop over the atoms. 263 for j in range(len(r_pivot_atom[:, 0])): 264 # Loop over the alignments. 265 for i in range(len(pcs_theta)): 266 # Skip missing data. 267 if missing_pcs[i, j]: 268 continue 269 270 # The projection. 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 # The PCS. 279 pcs_theta[i, j] += proj * length_i
280 281
282 -def pcs_pivot_motion_rotor_quad_int(sigma_i, r_pivot_atom, r_ln_pivot, A, R_eigen, RT_eigen, Ri_prime):
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 # The rotation matrix. 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 # The rotation. 312 R_i = dot(R_eigen, dot(Ri_prime, RT_eigen)) 313 314 # Calculate the new vector. 315 vect = dot(R_i, r_pivot_atom) + r_ln_pivot 316 317 # The vector length. 318 length = norm(vect) 319 320 # The projection. 321 proj = dot(vect, dot(A, vect)) 322 323 # The PCS. 324 pcs = proj / length**5 325 326 # Return the PCS value (without the PCS constant). 327 return pcs
328