1   
   2   
   3   
   4   
   5   
   6   
   7   
   8   
   9   
  10   
  11   
  12   
  13   
  14   
  15   
  16   
  17   
  18   
  19   
  20   
  21   
  22   
  23  from math import sqrt 
  24  from numpy import array, dot, eye, float64, ones, rank, transpose, zeros 
  25   
  26   
  27  from float import isNaN 
  28  from maths_fns.alignment_tensor import dAi_dAxx, dAi_dAyy, dAi_dAxy, dAi_dAxz, dAi_dAyz, to_tensor 
  29  from maths_fns.chi2 import chi2, dchi2_element, d2chi2_element 
  30  from maths_fns.paramag_centre import vectors_single_centre, vectors_centre_per_state 
  31  from maths_fns.pcs import ave_pcs_tensor, ave_pcs_tensor_ddeltaij_dAmn, ave_pcs_tensor_ddeltaij_dc, pcs_constant_grad, pcs_tensor 
  32  from maths_fns.rdc import ave_rdc_tensor, ave_rdc_tensor_dDij_dAmn, rdc_tensor 
  33  from maths_fns.rotation_matrix import euler_to_R_zyz 
  34  from physical_constants import pcs_constant 
  35  from relax_errors import RelaxError, RelaxImplementError 
  36   
  37   
  39      """Class containing the target function of the optimisation of the N-state model.""" 
  40   
  41 -    def __init__(self, model=None, N=None, init_params=None, probs=None, full_tensors=None, red_data=None, red_errors=None, full_in_ref_frame=None, fixed_tensors=None, pcs=None, pcs_errors=None, pcs_weights=None, rdcs=None, rdc_errors=None, rdc_weights=None, rdc_vect=None, temp=None, frq=None, dip_const=None, absolute_rdc=None, atomic_pos=None, paramag_centre=None, scaling_matrix=None, centre_fixed=True): 
   42          """Set up the class instance for optimisation. 
  43   
  44          The N-state models 
  45          ================== 
  46   
  47          All constant data required for the N-state model are initialised here.  Depending on the base data used for optimisation, different data structures can be supplied.  However a number of structures must be provided for the N-state model.  These are: 
  48   
  49              - model, the type of N-state model.  This can be '2-domain', 'population', or 'fixed'. 
  50              - N, the number of states (or structures). 
  51              - init_params, the initial parameter values. 
  52              - scaling_matrix, the matrix used for parameter scaling during optimisation. 
  53   
  54   
  55          2-domain N-state model 
  56          ---------------------- 
  57   
  58          If the model type is set to '2-domain', then the following data structures should be supplied: 
  59   
  60              - full_tensors, the alignment tensors in matrix form. 
  61              - red_data, the alignment tensors in 5D form in a rank-1 array. 
  62              - red_errors, the alignment tensor errors in 5D form in a rank-1 array.  This data is not obligatory. 
  63              - full_in_ref_frame, an array of flags specifying if the tensor in the reference frame is the full or reduced tensor. 
  64   
  65   
  66          The population N-state model 
  67          ============================ 
  68   
  69          In this model, populations are optimised for each state.  Additionally the alignment tensors for anisotropic data can also be optimised if they have not been supplied (through the full_tensors arg). 
  70   
  71   
  72          PCS base data 
  73          ------------- 
  74   
  75          If pseudocontact shift data is to be used for optimisation, then the following should be supplied: 
  76   
  77              - pcs, the pseudocontact shifts. 
  78              - pcs_errors, the optional pseudocontact shift error values. 
  79              - temp, the temperatures of the experiments. 
  80              - frq, the frequencies of the experiments. 
  81   
  82   
  83          PCS and PRE base data 
  84          --------------------- 
  85   
  86          If either pseudocontact shift or PRE data is to be used for optimisation, then the following should be supplied: 
  87   
  88              - atomic_pos, the positions of all atoms. 
  89              - paramag_centre, the paramagnetic centre position. 
  90   
  91   
  92          RDC base data 
  93          ------------- 
  94   
  95          If residual dipolar coupling data is to be used for optimisation, then the following should be supplied: 
  96   
  97              - rdcs, the residual dipolar couplings. 
  98              - rdc_errors, the optional residual dipolar coupling errors. 
  99              - rdc_vect, the interatomic vectors. 
 100              - dip_const, the dipolar constants. 
 101              - absolute_rdc, the flags specifying whether each RDC is signless. 
 102   
 103   
 104          @keyword model:             The N-state model type.  This can be one of '2-domain', 'population' or 'fixed'. 
 105          @type model:                str 
 106          @keyword N:                 The number of states. 
 107          @type N:                    int 
 108          @keyword init_params:       The initial parameter values.  Optimisation must start at some point! 
 109          @type init_params:          numpy float64 array 
 110          @keyword probs:             The probabilities for each state c.  The length of this list should be equal to N. 
 111          @type probs:                list of float 
 112          @keyword full_tensors:      An array of the {Sxx, Syy, Sxy, Sxz, Syz} values for all full tensors.  The format is [Sxx1, Syy1, Sxy1, Sxz1, Syz1, Sxx2, Syy2, Sxy2, Sxz2, Syz2, ..., Sxxn, Syyn, Sxyn, Sxzn, Syzn] 
 113          @type full_tensors:         list of rank-2, 3D numpy arrays 
 114          @keyword red_data:          An array of the {Sxx, Syy, Sxy, Sxz, Syz} values for all reduced tensors.  The format is the same as for full_tensors. 
 115          @type red_data:             numpy float64 array 
 116          @keyword red_errors:        An array of the {Sxx, Syy, Sxy, Sxz, Syz} errors for all reduced tensors.  The array format is the same as for full_tensors. 
 117          @type red_errors:           numpy float64 array 
 118          @keyword full_in_ref_frame: An array of flags specifying if the tensor in the reference frame is the full or reduced tensor. 
 119          @type full_in_ref_frame:    numpy rank-1 array 
 120          @keyword fixed_tensors:     An array of flags specifying if the tensor is fixed or will be optimised. 
 121          @type fixed_tensors:        list of bool 
 122          @keyword pcs:               The PCS lists.  The first index must correspond to the different alignment media i and the second index to the spin systems j. 
 123          @type pcs:                  numpy rank-2 array 
 124          @keyword pcs_errors:        The PCS error lists.  The dimensions of this argument are the same as for 'pcs'. 
 125          @type pcs_errors:           numpy rank-2 array 
 126          @keyword pcs_weights:       The PCS weight lists.  The dimensions of this argument are the same as for 'pcs'. 
 127          @type pcs_weights:          numpy rank-2 array 
 128          @keyword rdcs:              The RDC lists.  The first index must correspond to the different alignment media i and the second index to the spin pairs k. 
 129          @type rdcs:                 numpy rank-2 array 
 130          @keyword rdc_errors:        The RDC error lists.  The dimensions of this argument are the same as for 'rdcs'. 
 131          @type rdc_errors:           numpy rank-2 array 
 132          @keyword rdc_weights:       The RDC weight lists.  The dimensions of this argument are the same as for 'rdcs'. 
 133          @type rdc_weights:          numpy rank-2 array 
 134          @keyword rdc_vect:          The unit vector lists for the RDC.  The first index must correspond to the spin pair and the second index to each structure (its size being equal to the number of states). 
 135          @type rdc_vect:             numpy rank-2 array 
 136          @keyword temp:              The temperature of each experiment, used for the PCS. 
 137          @type temp:                 numpy rank-1 array 
 138          @keyword frq:               The frequency of each alignment, used for the PCS. 
 139          @type frq:                  numpy rank-1 array 
 140          @keyword dip_const:         The dipolar constants for each spin pair.  The indices correspond to the spin pairs k. 
 141          @type dip_const:            numpy rank-1 array 
 142          @keyword absolute_rdc:      The signless or absolute value flags for the RDCs. 
 143          @type absolute_rdc:         numpy rank-2 array 
 144          @keyword atomic_pos:        The atomic positions of all spins for the PCS and PRE data.  The first index is the spin systems j and the second is the structure or state c. 
 145          @type atomic_pos:           numpy rank-3 array 
 146          @keyword paramag_centre:    The paramagnetic centre position (or positions). 
 147          @type paramag_centre:       numpy rank-1, 3D array or rank-2, Nx3 array 
 148          @keyword scaling_matrix:    The square and diagonal scaling matrix. 
 149          @type scaling_matrix:       numpy rank-2 array 
 150          @keyword centre_fixed:      A flag which if False will cause the paramagnetic centre to be optimised. 
 151          @type centre_fixed:         bool 
 152          """ 
 153   
 154           
 155          self.N = N 
 156          self.params = 1.0 * init_params     
 157          self.fixed_tensors = fixed_tensors 
 158          self.deltaij = pcs 
 159          self.Dij = rdcs 
 160          self.dip_vect = rdc_vect 
 161          self.dip_const = dip_const 
 162          self.absolute_rdc = absolute_rdc 
 163          self.temp = temp 
 164          self.frq = frq 
 165          self.atomic_pos = atomic_pos 
 166          self.paramag_centre = paramag_centre 
 167          self.centre_fixed = centre_fixed 
 168          self.total_num_params = len(init_params) 
 169   
 170           
 171          self.chi2 = 0.0 
 172          self.dchi2 = zeros((self.total_num_params), float64) 
 173          self.d2chi2 = zeros((self.total_num_params, self.total_num_params), float64) 
 174   
 175           
 176          self.scaling_matrix = scaling_matrix 
 177          if self.scaling_matrix != None: 
 178              self.scaling_flag = True 
 179          else: 
 180              self.scaling_flag = False 
 181   
 182           
 183          if model == '2-domain': 
 184               
 185              if red_data == None or not len(red_data): 
 186                  raise RelaxError("The red_data argument " + repr(red_data) + " must be supplied.") 
 187              if red_errors == None or not len(red_errors): 
 188                  raise RelaxError("The red_errors argument " + repr(red_errors) + " must be supplied.") 
 189              if full_in_ref_frame == None or not len(full_in_ref_frame): 
 190                  raise RelaxError("The full_in_ref_frame argument " + repr(full_in_ref_frame) + " must be supplied.") 
 191   
 192               
 193              self.full_tensors = array(full_tensors, float64) 
 194              self.num_tensors = int(len(self.full_tensors) / 5) 
 195              self.red_data = red_data 
 196              self.red_errors = red_errors 
 197              self.full_in_ref_frame = full_in_ref_frame 
 198   
 199               
 200              self.A = zeros((self.num_tensors, 3, 3), float64) 
 201              for align_index in range(self.num_tensors): 
 202                  to_tensor(self.A[align_index], self.full_tensors[5*align_index:5*align_index+5]) 
 203   
 204               
 205               
 206               
 207               
 208               
 209              self.R = zeros((self.N, 3, 3), float64) 
 210              self.RT = zeros((self.N, 3, 3), float64) 
 211              self.red_bc = zeros((self.num_tensors, 3, 3), float64) 
 212              self.red_bc_vector = zeros(self.num_tensors*5, float64) 
 213   
 214               
 215              self.func = self.func_2domain 
 216              self.dfunc = None 
 217              self.d2func = None 
 218   
 219           
 220          elif model in ['population', 'fixed']: 
 221               
 222              self.num_align = 0 
 223              if rdcs != None: 
 224                  self.num_align = len(rdcs) 
 225              if pcs != None: 
 226                  self.num_align = max(self.num_align, len(pcs)) 
 227   
 228               
 229              self.rdc_flag = [True] * self.num_align 
 230              self.pcs_flag = [True] * self.num_align 
 231              for align_index in range(self.num_align): 
 232                  if rdcs == None or len(rdcs[align_index]) == 0: 
 233                      self.rdc_flag[align_index] = False 
 234                  if pcs == None or len(pcs[align_index]) == 0: 
 235                      self.pcs_flag[align_index] = False 
 236              self.rdc_flag_sum = sum(self.rdc_flag) 
 237              self.pcs_flag_sum = sum(self.pcs_flag) 
 238   
 239               
 240              if self.rdc_flag_sum and (rdc_vect == None or not len(rdc_vect)): 
 241                  raise RelaxError("The rdc_vect argument " + repr(rdc_vect) + " must be supplied.") 
 242              if self.pcs_flag_sum and (atomic_pos == None or not len(atomic_pos)): 
 243                  raise RelaxError("The atomic_pos argument " + repr(atomic_pos) + " must be supplied.") 
 244   
 245               
 246              self.num_spins = 0 
 247              if self.pcs_flag_sum: 
 248                  self.num_spins = len(pcs[0]) 
 249   
 250               
 251              self.num_interatom = 0 
 252              if self.rdc_flag_sum: 
 253                  self.num_interatom = len(rdcs[0]) 
 254   
 255               
 256              self.A = zeros((self.num_align, 3, 3), float64) 
 257              self.dA = zeros((5, 3, 3), float64) 
 258   
 259               
 260              if full_tensors != None: 
 261                   
 262                  self.full_tensors = array(full_tensors, float64) 
 263   
 264               
 265              self.num_align_params = 0 
 266              index = 0 
 267              for align_index in range(self.num_align): 
 268                   
 269                  if fixed_tensors[align_index]: 
 270                      to_tensor(self.A[align_index], self.full_tensors[5*index:5*index+5]) 
 271                      index += 1 
 272   
 273                   
 274                  if not fixed_tensors[align_index]: 
 275                      self.num_align_params += 5 
 276   
 277               
 278              else: 
 279                   
 280                  dAi_dAxx(self.dA[0]) 
 281                  dAi_dAyy(self.dA[1]) 
 282                  dAi_dAxy(self.dA[2]) 
 283                  dAi_dAxz(self.dA[3]) 
 284                  dAi_dAyz(self.dA[4]) 
 285   
 286               
 287              if self.pcs_flag_sum: 
 288                  err = False 
 289                  for i in range(len(pcs_errors)): 
 290                      for j in range(len(pcs_errors[i])): 
 291                          if not isNaN(pcs_errors[i, j]): 
 292                              err = True 
 293                  if err: 
 294                      self.pcs_sigma_ij = pcs_errors 
 295                  else: 
 296                       
 297                      self.pcs_sigma_ij = 0.03 * 1e-6 * ones((self.num_align, self.num_spins), float64) 
 298   
 299               
 300              if self.rdc_flag_sum: 
 301                  err = False 
 302                  for i in range(len(rdc_errors)): 
 303                      for j in range(len(rdc_errors[i])): 
 304                          if not isNaN(rdc_errors[i, j]): 
 305                              err = True 
 306                  if err: 
 307                      self.rdc_sigma_ij = rdc_errors 
 308                  else: 
 309                       
 310                      self.rdc_sigma_ij = ones((self.num_align, self.num_interatom), float64) 
 311   
 312               
 313              if self.rdc_flag_sum: 
 314                  self.missing_Dij = zeros((self.num_align, self.num_interatom), float64) 
 315   
 316               
 317              if self.pcs_flag_sum: 
 318                  self.missing_deltaij = zeros((self.num_align, self.num_spins), float64) 
 319   
 320               
 321              if self.rdc_flag_sum or self.pcs_flag_sum: 
 322                  for align_index in range(self.num_align): 
 323                      if self.rdc_flag_sum: 
 324                          for j in range(self.num_interatom): 
 325                              if isNaN(self.Dij[align_index, j]): 
 326                                   
 327                                  self.missing_Dij[align_index, j] = 1 
 328   
 329                                   
 330                                  self.Dij[align_index, j] = 0.0 
 331   
 332                                   
 333                                  self.rdc_sigma_ij[align_index, j] = 1.0 
 334   
 335                                   
 336                                  rdc_weights[align_index, j] = 1.0 
 337   
 338                               
 339                              self.rdc_sigma_ij[align_index, j] = self.rdc_sigma_ij[align_index, j] / sqrt(rdc_weights[align_index, j]) 
 340   
 341   
 342                      if self.pcs_flag_sum: 
 343                          for j in range(self.num_spins): 
 344                              if isNaN(self.deltaij[align_index, j]): 
 345                                   
 346                                  self.missing_deltaij[align_index, j] = 1 
 347   
 348                                   
 349                                  self.deltaij[align_index, j] = 0.0 
 350   
 351                                   
 352                                  self.pcs_sigma_ij[align_index, j] = 1.0 
 353   
 354                                   
 355                                  pcs_weights[align_index, j] = 1.0 
 356   
 357                               
 358                              self.pcs_sigma_ij[align_index, j] = self.pcs_sigma_ij[align_index, j] / sqrt(pcs_weights[align_index, j]) 
 359   
 360               
 361              if self.pcs_flag_sum: 
 362                   
 363                  self.paramag_unit_vect = zeros(atomic_pos.shape, float64) 
 364                  self.paramag_dist = zeros((self.num_spins, self.N), float64) 
 365                  self.pcs_const = zeros((self.num_align, self.num_spins, self.N), float64) 
 366                  if self.paramag_centre == None: 
 367                      self.paramag_centre = zeros(3, float64) 
 368   
 369                   
 370                  if not self.centre_fixed: 
 371                      self.dpcs_const_theta = zeros((self.num_align, self.num_spins, self.N, 3), float64) 
 372                      self.dr_theta = -eye(3) 
 373   
 374                   
 375                  self.paramag_info() 
 376   
 377               
 378              self.deltaij_theta = zeros((self.num_align, self.num_spins), float64) 
 379              self.ddeltaij_theta = zeros((self.total_num_params, self.num_align, self.num_spins), float64) 
 380              self.d2deltaij_theta = zeros((self.total_num_params, self.total_num_params, self.num_align, self.num_spins), float64) 
 381   
 382               
 383              self.Dij_theta = zeros((self.num_align, self.num_interatom), float64) 
 384              self.dDij_theta = zeros((self.total_num_params, self.num_align, self.num_interatom), float64) 
 385              self.d2Dij_theta = zeros((self.total_num_params, self.total_num_params, self.num_align, self.num_interatom), float64) 
 386   
 387               
 388              self.func = self.func_standard 
 389              self.dfunc = self.dfunc_standard 
 390              self.d2func = self.d2func_standard 
 391   
 392           
 393          self.probs_fixed = True 
 394          if model == 'population': 
 395              self.probs_fixed = False 
 396   
 397           
 398          if model == 'fixed': 
 399               
 400              self.zero_hessian_rdc = zeros(self.num_interatom, float64) 
 401              self.zero_hessian_pcs = zeros(self.num_spins, float64) 
 402   
 403               
 404              if probs: 
 405                  self.probs = probs 
 406   
 407               
 408              else: 
 409                  self.probs = ones(self.N, float64) / self.N 
  410   
 411   
 412 -    def func_2domain(self, params): 
  413          """The target function for optimisation of the 2-domain N-state model. 
 414   
 415          This function should be passed to the optimisation algorithm.  It accepts, as an array, a vector of parameter values and, using these, returns the single chi-squared value corresponding to that coordinate in the parameter space.  If no tensor errors are supplied, then the SSE (the sum of squares error) value is returned instead.  The chi-squared is simply the SSE normalised to unit variance (the SSE divided by the error squared). 
 416   
 417          @param params:  The vector of parameter values. 
 418          @type params:   list of float 
 419          @return:        The chi-squared or SSE value. 
 420          @rtype:         float 
 421          """ 
 422   
 423           
 424          if self.scaling_flag: 
 425              params = dot(params, self.scaling_matrix) 
 426   
 427           
 428          self.red_bc = self.red_bc * 0.0 
 429   
 430           
 431          for c in range(self.N): 
 432               
 433              euler_to_R_zyz(params[self.N-1+3*c], params[self.N-1+3*c+1], params[self.N-1+3*c+2], self.R[c]) 
 434   
 435               
 436              self.RT[c] = transpose(self.R[c]) 
 437   
 438               
 439              if c < self.N-1: 
 440                  pc = params[c] 
 441   
 442               
 443              else: 
 444                  pc = 1.0 
 445                  for c2 in range(self.N-1): 
 446                      pc = pc - params[c2] 
 447   
 448               
 449              for align_index in range(self.num_tensors): 
 450                   
 451                  if self.full_in_ref_frame[align_index]: 
 452                      self.red_bc[align_index] = self.red_bc[align_index]  +  pc * dot(self.RT[c], dot(self.A[align_index], self.R[c])) 
 453   
 454                   
 455                  else: 
 456                      self.red_bc[align_index] = self.red_bc[align_index]  +  pc * dot(self.R[c], dot(self.A[align_index], self.RT[c])) 
 457   
 458           
 459          for align_index in range(self.num_tensors): 
 460              self.red_bc_vector[5*align_index]   = self.red_bc[align_index, 0, 0]     
 461              self.red_bc_vector[5*align_index+1] = self.red_bc[align_index, 1, 1]     
 462              self.red_bc_vector[5*align_index+2] = self.red_bc[align_index, 0, 1]     
 463              self.red_bc_vector[5*align_index+3] = self.red_bc[align_index, 0, 2]     
 464              self.red_bc_vector[5*align_index+4] = self.red_bc[align_index, 1, 2]     
 465   
 466           
 467          return chi2(self.red_data, self.red_bc_vector, self.red_errors) 
  468   
 469   
 471          """The target function for optimisation of the standard N-state model. 
 472   
 473          Description 
 474          =========== 
 475   
 476          This function should be passed to the optimisation algorithm.  It accepts, as an array, a vector of parameter values and, using these, returns the single chi-squared value corresponding to that coordinate in the parameter space.  If no RDC or PCS errors errors are supplied, then the SSE (the sum of squares error) value is returned instead.  The chi-squared is simply the SSE normalised to unit variance (the SSE divided by the error squared). 
 477   
 478   
 479          Indices 
 480          ======= 
 481   
 482          For this calculation, five indices are looped over and used in the various data structures.  These include: 
 483              - i, the index over alignments, 
 484              - j, the index over spin systems, 
 485              - c, the index over the N-states (or over the structures), 
 486              - n, the index over the first dimension of the alignment tensor n = {x, y, z}, 
 487              - m, the index over the second dimension of the alignment tensor m = {x, y, z}. 
 488   
 489   
 490          Equations 
 491          ========= 
 492   
 493          To calculate the function value, a chain of equations are used.  This includes the chi-squared equation and the RDC and PCS equations. 
 494   
 495   
 496          The chi-squared equation 
 497          ------------------------ 
 498   
 499          The equations are:: 
 500   
 501                           ___ 
 502                           \    (Dij - Dij(theta)) ** 2 
 503           chi^2(theta)  =  >   ----------------------- , 
 504                           /__       sigma_ij ** 2 
 505                            ij 
 506   
 507                           ___ 
 508                           \    (delta_ij - delta_ij(theta)) ** 2 
 509           chi^2(theta)  =  >   --------------------------------- , 
 510                           /__             sigma_ij ** 2 
 511                            ij 
 512   
 513          where: 
 514              - theta is the parameter vector, 
 515              - Dij are the measured RDCs for alignment i, spin j, 
 516              - Dij(theta) are the back calculated RDCs for alignment i, spin j, 
 517              - delta_ij are the measured PCSs for alignment i, spin j, 
 518              - delta_ij(theta) are the back calculated PCSs for alignment i, spin j, 
 519              - sigma_ij are the RDC or PCS errors. 
 520   
 521          Both chi-squared values sum. 
 522   
 523   
 524          The RDC equation 
 525          ---------------- 
 526   
 527          The RDC equation is:: 
 528   
 529                             _N_ 
 530                             \              T 
 531           Dij(theta)  =  dj  >   pc . mu_jc . Ai . mu_jc, 
 532                             /__ 
 533                             c=1 
 534   
 535          where: 
 536              - dj is the dipolar constant for spin j, 
 537              - N is the total number of states or structures, 
 538              - pc is the weight or probability associated with state c, 
 539              - mu_jc is the unit vector corresponding to spin j and state c, 
 540              - Ai is the alignment tensor. 
 541   
 542          In the fixed and equal probability case, the equation is:: 
 543   
 544                             _N_ 
 545                          dj \         T 
 546           Dij(theta)  =  --  >   mu_jc . Ai . mu_jc, 
 547                          N  /__ 
 548                             c=1 
 549   
 550          The dipolar constant is henceforth defined as:: 
 551   
 552              dj = 3 / (2pi) d', 
 553   
 554          where the factor of 2pi is to convert from units of rad.s^-1 to Hertz, the factor of 3 is associated with the alignment tensor and the pure dipolar constant in SI units is:: 
 555   
 556                     mu0 gI.gS.h_bar 
 557              d' = - --- ----------- , 
 558                     4pi    r**3 
 559   
 560          where: 
 561              - mu0 is the permeability of free space, 
 562              - gI and gS are the gyromagnetic ratios of the I and S spins, 
 563              - h_bar is Dirac's constant which is equal to Planck's constant divided by 2pi, 
 564              - r is the distance between the two spins. 
 565   
 566   
 567          The PCS equation 
 568          ---------------- 
 569   
 570          The PCS equation is:: 
 571   
 572                                 _N_ 
 573                                 \                    T 
 574              delta_ij(theta)  =  >  pc . dijc . mu_jc . Ai . mu_jc, 
 575                                 /__ 
 576                                 c=1 
 577   
 578          where: 
 579              - djci is the PCS constant for spin j, state c and experiment or alignment i, 
 580              - N is the total number of states or structures, 
 581              - pc is the weight or probability associated with state c, 
 582              - mu_jc is the unit vector corresponding to spin j and state c, 
 583              - Ai is the alignment tensor. 
 584   
 585          In the fixed and equal probability case, the equation is:: 
 586   
 587                                   _N_ 
 588                                 1 \               T 
 589              delta_ij(theta)  = -  >  dijc . mu_jc . Ai . mu_jc, 
 590                                 N /__ 
 591                                   c=1 
 592   
 593          The PCS constant is defined as:: 
 594   
 595                     mu0 15kT   1 
 596              dijc = --- ----- ---- , 
 597                     4pi Bo**2 r**3 
 598   
 599          where: 
 600              - mu0 is the permeability of free space, 
 601              - k is Boltzmann's constant, 
 602              - T is the absolute temperature (different for each experiment), 
 603              - Bo is the magnetic field strength (different for each experiment), 
 604              - r is the distance between the paramagnetic centre (electron spin) and the nuclear spin (different for each spin and state). 
 605   
 606   
 607          Stored data structures 
 608          ====================== 
 609   
 610          There are a number of data structures calculated by this function and stored for subsequent use in the gradient and Hessian functions.  This include the back calculated RDCs and PCSs and the alignment tensors. 
 611   
 612          Dij(theta) 
 613          ---------- 
 614   
 615          The back calculated RDCs.  This is a rank-2 tensor with indices {i, j}. 
 616   
 617          delta_ij(theta) 
 618          --------------- 
 619   
 620          The back calculated PCS.  This is a rank-2 tensor with indices {i, j}. 
 621   
 622          Ai 
 623          -- 
 624   
 625          The alignment tensors.  This is a rank-3 tensor with indices {i, n, m}. 
 626   
 627   
 628          @param params:  The vector of parameter values. 
 629          @type params:   numpy rank-1 array 
 630          @return:        The chi-squared or SSE value. 
 631          @rtype:         float 
 632          """ 
 633   
 634           
 635          if self.scaling_flag: 
 636              params = dot(params, self.scaling_matrix) 
 637   
 638           
 639          chi2_sum = 0.0 
 640   
 641           
 642          if not self.probs_fixed and not self.centre_fixed: 
 643               
 644              self.probs = params[-(self.N-1)-3:-3] 
 645   
 646           
 647          elif not self.probs_fixed: 
 648              self.probs = params[-(self.N-1):] 
 649   
 650           
 651          if not self.centre_fixed: 
 652              self.paramag_centre = params[-3:] 
 653              self.paramag_info() 
 654   
 655           
 656          index = 0 
 657          for align_index in range(self.num_align): 
 658               
 659              if not self.fixed_tensors[align_index]: 
 660                  to_tensor(self.A[align_index], params[5*index:5*index + 5]) 
 661                  index += 1 
 662   
 663               
 664              if self.rdc_flag[align_index]: 
 665                   
 666                  for j in range(self.num_interatom): 
 667                       
 668                      if not self.missing_Dij[align_index, j]: 
 669                          self.Dij_theta[align_index, j] = ave_rdc_tensor(self.dip_const[j], self.dip_vect[j], self.N, self.A[align_index], weights=self.probs, absolute=self.absolute_rdc[align_index, j]) 
 670   
 671               
 672              if self.pcs_flag[align_index]: 
 673                   
 674                  for j in range(self.num_spins): 
 675                       
 676                      if not self.missing_deltaij[align_index, j]: 
 677                          self.deltaij_theta[align_index, j] = ave_pcs_tensor(self.pcs_const[align_index, j], self.paramag_unit_vect[j], self.N, self.A[align_index], weights=self.probs) 
 678   
 679               
 680              if self.rdc_flag[align_index]: 
 681                  chi2_sum = chi2_sum + chi2(self.Dij[align_index], self.Dij_theta[align_index], self.rdc_sigma_ij[align_index]) 
 682   
 683               
 684              if self.pcs_flag[align_index]: 
 685                  chi2_sum = chi2_sum + chi2(self.deltaij[align_index], self.deltaij_theta[align_index], self.pcs_sigma_ij[align_index]) 
 686   
 687           
 688          return chi2_sum 
  689   
 690   
 692          """The gradient function for optimisation of the standard N-state model. 
 693   
 694          Description 
 695          =========== 
 696   
 697          This function should be passed to the optimisation algorithm.  It accepts, as an array, a vector of parameter values and, using these, returns the chi-squared gradient corresponding to that coordinate in the parameter space.  If no RDC or PCS errors are supplied, then the SSE (the sum of squares error) gradient is returned instead.  The chi-squared gradient is simply the SSE gradient normalised to unit variance (the SSE divided by the error squared). 
 698   
 699   
 700          Indices 
 701          ======= 
 702   
 703          For this calculation, six indices are looped over and used in the various data structures.  These include: 
 704              - k, the index over all parameters, 
 705              - i, the index over alignments, 
 706              - j, the index over spin systems, 
 707              - c, the index over the N-states (or over the structures), 
 708              - m, the index over the first dimension of the alignment tensor m = {x, y, z}. 
 709              - n, the index over the second dimension of the alignment tensor n = {x, y, z}, 
 710   
 711   
 712          Equations 
 713          ========= 
 714   
 715          To calculate the chi-squared gradient, a chain of equations are used.  This includes the chi-squared gradient, the RDC gradient and the alignment tensor gradient. 
 716   
 717   
 718          The chi-squared gradient 
 719          ------------------------ 
 720   
 721          The equation is:: 
 722                                ___ 
 723           dchi^2(theta)        \   / Dij - Dij(theta)     dDij(theta) \  
 724           -------------  =  -2  >  | ----------------  .  ----------- | 
 725              dthetak           /__ \   sigma_ij**2         dthetak    / 
 726                                ij 
 727   
 728          where: 
 729              - theta is the parameter vector, 
 730              - Dij are the measured RDCs or PCSs, 
 731              - Dij(theta) are the back calculated RDCs or PCSs, 
 732              - sigma_ij are the RDC or PCS errors, 
 733              - dDij(theta)/dthetak is the RDC or PCS gradient for parameter k. 
 734   
 735   
 736          The RDC gradient 
 737          ---------------- 
 738   
 739          This gradient is different for the various parameter types. 
 740   
 741          pc partial derivative 
 742          ~~~~~~~~~~~~~~~~~~~~~ 
 743   
 744          The population parameter partial derivative is:: 
 745   
 746               dDij(theta)               T 
 747               -----------  =  dj . mu_jc . Ai . mu_jc, 
 748                   dpc 
 749   
 750          where: 
 751              - dj is the dipolar constant for spin j, 
 752              - mu_jc is the unit vector corresponding to spin j and state c, 
 753              - Ai is the alignment tensor. 
 754   
 755          Amn partial derivative 
 756          ~~~~~~~~~~~~~~~~~~~~~~ 
 757   
 758          The alignment tensor element partial derivative is:: 
 759   
 760                              _N_ 
 761           dDij(theta)        \              T   dAi 
 762           -----------  =  dj  >   pc . mu_jc . ---- . mu_jc, 
 763              dAmn            /__               dAmn 
 764                              c=1 
 765   
 766          where: 
 767              - dj is the dipolar constant for spin j, 
 768              - pc is the weight or probability associated with state c, 
 769              - mu_jc is the unit vector corresponding to spin j and state c, 
 770              - dAi/dAmn is the partial derivative of the alignment tensor with respect to element Amn. 
 771   
 772          In the case of fixed and equal populations, the equation is:: 
 773   
 774                              _N_ 
 775           dDij(theta)     dj \         T   dAi 
 776           -----------  =  --  >   mu_jc . ---- . mu_jc, 
 777              dAmn         N  /__          dAmn 
 778                              c=1 
 779   
 780   
 781          The PCS gradient 
 782          ---------------- 
 783   
 784          This gradient is also different for the various parameter types. 
 785   
 786          pc partial derivative 
 787          ~~~~~~~~~~~~~~~~~~~~~ 
 788   
 789          The population parameter partial derivative is:: 
 790   
 791               ddeltaij(theta)                 T 
 792               ---------------  =  dijc . mu_jc . Ai . mu_jc, 
 793                    dpc 
 794   
 795          where: 
 796              - djc is the pseudocontact shift constant for spin j and state c, 
 797              - mu_jc is the unit vector corresponding to spin j and state c, 
 798              - Ai is the alignment tensor. 
 799   
 800          Amn partial derivative 
 801          ~~~~~~~~~~~~~~~~~~~~~~ 
 802   
 803          The alignment tensor element partial derivative is:: 
 804   
 805                                  _N_ 
 806              ddelta_ij(theta)    \                   T   dAi 
 807              ----------------  =  >  pc . djc . mu_jc . ---- . mu_jc, 
 808                    dAmn          /__                    dAmn 
 809                                  c=1 
 810   
 811          where: 
 812              - djc is the pseudocontact shift constant for spin j and state c, 
 813              - pc is the weight or probability associated with state c, 
 814              - mu_jc is the unit vector corresponding to spin j and state c, 
 815              - dAi/dAmn is the partial derivative of the alignment tensor with respect to element Amn. 
 816   
 817          In the case of fixed and equal populations, the equation is:: 
 818   
 819                                    _N_ 
 820              ddelta_ij(theta)    1 \              T   dAi 
 821              ----------------  = -  >  djc . mu_jc . ---- . mu_jc, 
 822                    dAmn          N /__               dAmn 
 823                                    c=1 
 824   
 825          xi partial derivative 
 826          ~~~~~~~~~~~~~~~~~~~~~ 
 827   
 828          The paramagnetic position partial derivative is:: 
 829   
 830                                  _N_ 
 831              ddelta_ij(theta)    \        / ddjc                       dr_jcT                          dr_jc \  
 832              ----------------  =  >  pc . | ----.r_jcT.Ai.r_jc  +  djc.------.Ai.r_jc  +  djc.r_jcT.Ai.----- | , 
 833                    dxi           /__      \ dxi                         dxi                             dxi  / 
 834                                  c=1 
 835   
 836          where xi are the paramagnetic position coordinates {x0, x1, x2} and the last two terms in the sum are equal due to the symmetry of the alignment tensor, and:: 
 837   
 838              ddjc    mu0 15kT                 5 (si - xi) 
 839              ----  = --- ----- ---------------------------------------------  , 
 840              dxi     4pi Bo**2 ((sx-x0)**2 + (sy-x1)**2 + (sz-x2)**2)**(7/2) 
 841   
 842          and:: 
 843   
 844              dr      | 1 |   dr      | 0 |   dr      | 0 | 
 845              --  = - | 0 | , --  = - | 1 | , --  = - | 0 | . 
 846              dx      | 0 |   dy      | 0 |   dy      | 1 | 
 847   
 848          The pseudocontact shift constant is defined here as:: 
 849   
 850                    mu0 15kT    1 
 851              djc = --- ----- ------ , 
 852                    4pi Bo**2 rjc**5 
 853   
 854   
 855          The alignment tensor gradient 
 856          ----------------------------- 
 857   
 858          The five unique elements of the tensor {Axx, Ayy, Axy, Axz, Ayz} give five different partial derivatives.  These are:: 
 859   
 860               dAi   | 1  0  0 | 
 861              ---- = | 0  0  0 |, 
 862              dAxx   | 0  0 -1 | 
 863   
 864               dAi   | 0  0  0 | 
 865              ---- = | 0  1  0 |, 
 866              dAyy   | 0  0 -1 | 
 867   
 868               dAi   | 0  1  0 | 
 869              ---- = | 1  0  0 |, 
 870              dAxy   | 0  0  0 | 
 871   
 872               dAi   | 0  0  1 | 
 873              ---- = | 0  0  0 |, 
 874              dAxz   | 1  0  0 | 
 875   
 876               dAi   | 0  0  0 | 
 877              ---- = | 0  0  1 |. 
 878              dAyz   | 0  1  0 | 
 879   
 880          As these are invariant, they can be pre-calculated. 
 881   
 882   
 883          Stored data structures 
 884          ====================== 
 885   
 886          There are a number of data structures calculated by this function and stored for subsequent use in the Hessian function.  This include the back calculated RDC and PCS gradients and the alignment tensor gradients. 
 887   
 888          dDij(theta)/dthetak 
 889          ------------------- 
 890   
 891          The back calculated RDC gradient.  This is a rank-3 tensor with indices {k, i, j}. 
 892   
 893          ddeltaij(theta)/dthetak 
 894          ----------------------- 
 895   
 896          The back calculated PCS gradient.  This is a rank-3 tensor with indices {k, i, j}. 
 897   
 898          dAi/dAmn 
 899          -------- 
 900   
 901          The alignment tensor gradients.  This is a rank-3 tensor with indices {5, n, m}. 
 902   
 903   
 904          @param params:  The vector of parameter values.  This is unused as it is assumed that func() was called first. 
 905          @type params:   numpy rank-1 array 
 906          @return:        The chi-squared or SSE gradient. 
 907          @rtype:         numpy rank-1 array 
 908          """ 
 909   
 910           
 911          self.dchi2 = self.dchi2 * 0.0 
 912   
 913           
 914          for align_index in range(self.num_align): 
 915               
 916              if not self.fixed_tensors[align_index]: 
 917                  for j in range(self.num_interatom): 
 918                      if self.rdc_flag[align_index] and not self.missing_Dij[align_index, j]: 
 919                          self.dDij_theta[align_index*5, align_index, j] = ave_rdc_tensor_dDij_dAmn(self.dip_const[j], self.dip_vect[j], self.N, self.dA[0], weights=self.probs, absolute=self.absolute_rdc[align_index, j]) 
 920                          self.dDij_theta[align_index*5+1, align_index, j] = ave_rdc_tensor_dDij_dAmn(self.dip_const[j], self.dip_vect[j], self.N, self.dA[1], weights=self.probs, absolute=self.absolute_rdc[align_index, j]) 
 921                          self.dDij_theta[align_index*5+2, align_index, j] = ave_rdc_tensor_dDij_dAmn(self.dip_const[j], self.dip_vect[j], self.N, self.dA[2], weights=self.probs, absolute=self.absolute_rdc[align_index, j]) 
 922                          self.dDij_theta[align_index*5+3, align_index, j] = ave_rdc_tensor_dDij_dAmn(self.dip_const[j], self.dip_vect[j], self.N, self.dA[3], weights=self.probs, absolute=self.absolute_rdc[align_index, j]) 
 923                          self.dDij_theta[align_index*5+4, align_index, j] = ave_rdc_tensor_dDij_dAmn(self.dip_const[j], self.dip_vect[j], self.N, self.dA[4], weights=self.probs, absolute=self.absolute_rdc[align_index, j]) 
 924   
 925               
 926              if not self.fixed_tensors[align_index]: 
 927                  for j in range(self.num_spins): 
 928                      if self.pcs_flag[align_index] and not self.missing_deltaij[align_index, j]: 
 929                          self.ddeltaij_theta[align_index*5, align_index, j] = ave_pcs_tensor_ddeltaij_dAmn(self.pcs_const[align_index, j], self.paramag_unit_vect[j], self.N, self.dA[0], weights=self.probs) 
 930                          self.ddeltaij_theta[align_index*5+1, align_index, j] = ave_pcs_tensor_ddeltaij_dAmn(self.pcs_const[align_index, j], self.paramag_unit_vect[j], self.N, self.dA[1], weights=self.probs) 
 931                          self.ddeltaij_theta[align_index*5+2, align_index, j] = ave_pcs_tensor_ddeltaij_dAmn(self.pcs_const[align_index, j], self.paramag_unit_vect[j], self.N, self.dA[2], weights=self.probs) 
 932                          self.ddeltaij_theta[align_index*5+3, align_index, j] = ave_pcs_tensor_ddeltaij_dAmn(self.pcs_const[align_index, j], self.paramag_unit_vect[j], self.N, self.dA[3], weights=self.probs) 
 933                          self.ddeltaij_theta[align_index*5+4, align_index, j] = ave_pcs_tensor_ddeltaij_dAmn(self.pcs_const[align_index, j], self.paramag_unit_vect[j], self.N, self.dA[4], weights=self.probs) 
 934   
 935               
 936              if not self.probs_fixed: 
 937                   
 938                  x = 0 
 939                  if not self.centre_fixed: 
 940                      x = 3 
 941   
 942                   
 943                  for c in range(self.N - 1 - x): 
 944                       
 945                      param_index = self.num_align_params + c 
 946   
 947                       
 948                      for j in range(self.num_interatom): 
 949                          if self.rdc_flag[align_index] and not self.missing_Dij[align_index, j]: 
 950                              self.dDij_theta[param_index, align_index, j] = rdc_tensor(self.dip_const[j], self.dip_vect[j, c], self.A[align_index], absolute=self.absolute_rdc[align_index, j]) 
 951   
 952                       
 953                      for j in range(self.num_spins): 
 954                          if self.pcs_flag[align_index] and not self.missing_deltaij[align_index, j]: 
 955                              self.ddeltaij_theta[param_index, align_index, j] = pcs_tensor(self.pcs_const[align_index, j, c], self.paramag_unit_vect[j, c], self.A[align_index]) 
 956   
 957               
 958              if not self.centre_fixed: 
 959                  for j in range(self.num_spins): 
 960                      if self.pcs_flag[align_index] and not self.missing_deltaij[align_index, j]: 
 961                          self.ddeltaij_theta[-3, align_index, j] = ave_pcs_tensor_ddeltaij_dc(ddj=self.dpcs_const_theta[align_index, j,:, 0], dj=self.pcs_const[align_index, j], r=self.paramag_dist[j], unit_vect=self.paramag_unit_vect[j], N=self.N, Ai=self.A[align_index], dr_dc=self.dr_theta[0], weights=self.probs) 
 962                          self.ddeltaij_theta[-2, align_index, j] = ave_pcs_tensor_ddeltaij_dc(ddj=self.dpcs_const_theta[align_index, j,:, 1], dj=self.pcs_const[align_index, j], r=self.paramag_dist[j], unit_vect=self.paramag_unit_vect[j], N=self.N, Ai=self.A[align_index], dr_dc=self.dr_theta[1], weights=self.probs) 
 963                          self.ddeltaij_theta[-1, align_index, j] = ave_pcs_tensor_ddeltaij_dc(ddj=self.dpcs_const_theta[align_index, j,:, 2], dj=self.pcs_const[align_index, j], r=self.paramag_dist[j], unit_vect=self.paramag_unit_vect[j], N=self.N, Ai=self.A[align_index], dr_dc=self.dr_theta[2], weights=self.probs) 
 964   
 965               
 966              for k in range(self.total_num_params): 
 967                   
 968                  if self.rdc_flag[align_index]: 
 969                      self.dchi2[k] = self.dchi2[k] + dchi2_element(self.Dij[align_index], self.Dij_theta[align_index], self.dDij_theta[k, align_index], self.rdc_sigma_ij[align_index]) 
 970   
 971                   
 972                  if self.pcs_flag[align_index]: 
 973                      self.dchi2[k] = self.dchi2[k] + dchi2_element(self.deltaij[align_index], self.deltaij_theta[align_index], self.ddeltaij_theta[k, align_index], self.pcs_sigma_ij[align_index]) 
 974   
 975           
 976          if self.scaling_flag: 
 977              self.dchi2 = dot(self.dchi2, self.scaling_matrix) 
 978   
 979           
 980          return self.dchi2 * 1.0 
  981   
 982   
 984          """The Hessian function for optimisation of the standard N-state model. 
 985   
 986          Description 
 987          =========== 
 988   
 989          This function should be passed to the optimisation algorithm.  It accepts, as an array, a vector of parameter values and, using these, returns the chi-squared Hessian corresponding to that coordinate in the parameter space.  If no RDC/PCS errors are supplied, then the SSE (the sum of squares error) Hessian is returned instead.  The chi-squared Hessian is simply the SSE Hessian normalised to unit variance (the SSE divided by the error squared). 
 990   
 991   
 992          Indices 
 993          ======= 
 994   
 995          For this calculation, six indices are looped over and used in the various data structures.  These include: 
 996              - k, the index over all parameters, 
 997              - i, the index over alignments, 
 998              - j, the index over spin systems, 
 999              - c, the index over the N-states (or over the structures), 
1000              - m, the index over the first dimension of the alignment tensor m = {x, y, z}. 
1001              - n, the index over the second dimension of the alignment tensor n = {x, y, z}, 
1002   
1003   
1004          Equations 
1005          ========= 
1006   
1007          To calculate the chi-squared gradient, a chain of equations are used.  This includes the chi-squared gradient, the RDC gradient and the alignment tensor gradient. 
1008   
1009   
1010          The chi-squared Hessian 
1011          ----------------------- 
1012   
1013          The equation is:: 
1014                                 ___ 
1015           d2chi^2(theta)        \       1      / dDij(theta)   dDij(theta)                         d2Dij(theta)   \  
1016           ---------------  =  2  >  ---------- | ----------- . -----------  -  (Dij-Dij(theta)) . --------------- |. 
1017           dthetaj.dthetak       /__ sigma_i**2 \  dthetaj       dthetak                           dthetaj.dthetak / 
1018                                 ij 
1019   
1020          where: 
1021              - theta is the parameter vector, 
1022              - Dij are the measured RDCs or PCSs, 
1023              - Dij(theta) are the back calculated RDCs or PCSs, 
1024              - sigma_ij are the RDC or PCS errors, 
1025              - dDij(theta)/dthetak is the RDC or PCS gradient for parameter k. 
1026              - d2Dij(theta)/dthetaj.dthetak is the RDC or PCS Hessian for parameters j and k. 
1027   
1028   
1029          The RDC Hessian 
1030          --------------- 
1031   
1032          pc-pd second partial derivatives 
1033          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
1034   
1035          The probability parameter second partial derivative is:: 
1036   
1037           d2Dij(theta) 
1038           ------------  =  0. 
1039             dpc.dpd 
1040   
1041   
1042          pc-Anm second partial derivatives 
1043          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
1044   
1045          The probability parameter-tensor element second partial derivative is:: 
1046   
1047           d2Dij(theta)               T   dAi 
1048           ------------  =  dj . mu_jc . ---- . mu_jc. 
1049             dpc.dAmn                    dAmn 
1050   
1051   
1052          Amn-Aop second partial derivatives 
1053          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
1054   
1055          The alignment tensor element second partial derivative is:: 
1056   
1057           d2Dij(theta) 
1058           ------------  =  0. 
1059            dAmn.dAop 
1060   
1061   
1062          The PCS Hessian 
1063          --------------- 
1064   
1065          pc-pd second partial derivatives 
1066          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
1067   
1068          The probability parameter second partial derivative is:: 
1069   
1070           d2delta_ij(theta) 
1071           -----------------  =  0. 
1072                dpc.dpd 
1073   
1074   
1075          pc-Anm second partial derivatives 
1076          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
1077   
1078          The probability parameter-tensor element second partial derivative is:: 
1079   
1080           d2delta_ij(theta)                T   dAi 
1081           -----------------  =  djc . mu_jc . ---- . mu_jc. 
1082               dpc.dAmn                        dAmn 
1083   
1084   
1085          Amn-Aop second partial derivatives 
1086          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
1087   
1088          The alignment tensor element second partial derivative is:: 
1089   
1090              d2delta_ij(theta) 
1091              -----------------  =  0 
1092                  dAmn.dAop 
1093   
1094   
1095          The alignment tensor Hessian 
1096          ---------------------------- 
1097   
1098          The five unique elements of the tensor {Axx, Ayy, Axy, Axz, Ayz} all have the same second partial derivative of:: 
1099   
1100                d2Ai      | 0  0  0 | 
1101              --------- = | 0  0  0 |. 
1102              dAmn.dAop   | 0  0  0 | 
1103   
1104   
1105          @param params:  The vector of parameter values.  This is unused as it is assumed that func() was called first. 
1106          @type params:   numpy rank-1 array 
1107          @return:        The chi-squared or SSE Hessian. 
1108          @rtype:         numpy rank-2 array 
1109          """ 
1110   
1111           
1112          self.d2chi2 = self.d2chi2 * 0.0 
1113   
1114           
1115          for align_index in range(self.num_align): 
1116               
1117              if not self.probs_fixed: 
1118                  for c in range(self.N - 1): 
1119                       
1120                      pc_index = self.num_align_params + c 
1121   
1122                       
1123                      for j in range(self.num_interatom): 
1124                          if self.fixed_tensors[align_index] and self.rdc_flag[align_index] and not self.missing_Dij[align_index, j]: 
1125                              self.d2Dij_theta[pc_index, align_index*5+0, align_index, j] = self.d2Dij_theta[align_index*5+0, pc_index, align_index, j] = rdc_tensor(self.dip_const[j], self.dip_vect[j, c], self.dA[0], absolute=self.absolute_rdc[align_index, j]) 
1126                              self.d2Dij_theta[pc_index, align_index*5+1, align_index, j] = self.d2Dij_theta[align_index*5+1, pc_index, align_index, j] = rdc_tensor(self.dip_const[j], self.dip_vect[j, c], self.dA[1], absolute=self.absolute_rdc[align_index, j]) 
1127                              self.d2Dij_theta[pc_index, align_index*5+2, align_index, j] = self.d2Dij_theta[align_index*5+2, pc_index, align_index, j] = rdc_tensor(self.dip_const[j], self.dip_vect[j, c], self.dA[2], absolute=self.absolute_rdc[align_index, j]) 
1128                              self.d2Dij_theta[pc_index, align_index*5+3, align_index, j] = self.d2Dij_theta[align_index*5+3, pc_index, align_index, j] = rdc_tensor(self.dip_const[j], self.dip_vect[j, c], self.dA[3], absolute=self.absolute_rdc[align_index, j]) 
1129                              self.d2Dij_theta[pc_index, align_index*5+4, align_index, j] = self.d2Dij_theta[align_index*5+4, pc_index, align_index, j] = rdc_tensor(self.dip_const[j], self.dip_vect[j, c], self.dA[4], absolute=self.absolute_rdc[align_index, j]) 
1130   
1131                       
1132                      for j in range(self.num_spins): 
1133                          if self.fixed_tensors[align_index] and self.pcs_flag[align_index] and not self.missing_deltaij[align_index, j]: 
1134                              self.d2deltaij_theta[pc_index, align_index*5+0, align_index, j] = self.d2deltaij_theta[align_index*5+0, pc_index, align_index, j] = pcs_tensor(self.pcs_const[align_index, j, c], self.paramag_unit_vect[j, c], self.dA[0]) 
1135                              self.d2deltaij_theta[pc_index, align_index*5+1, align_index, j] = self.d2deltaij_theta[align_index*5+1, pc_index, align_index, j] = pcs_tensor(self.pcs_const[align_index, j, c], self.paramag_unit_vect[j, c], self.dA[1]) 
1136                              self.d2deltaij_theta[pc_index, align_index*5+2, align_index, j] = self.d2deltaij_theta[align_index*5+2, pc_index, align_index, j] = pcs_tensor(self.pcs_const[align_index, j, c], self.paramag_unit_vect[j, c], self.dA[2]) 
1137                              self.d2deltaij_theta[pc_index, align_index*5+3, align_index, j] = self.d2deltaij_theta[align_index*5+3, pc_index, align_index, j] = pcs_tensor(self.pcs_const[align_index, j, c], self.paramag_unit_vect[j, c], self.dA[3]) 
1138                              self.d2deltaij_theta[pc_index, align_index*5+4, align_index, j] = self.d2deltaij_theta[align_index*5+4, pc_index, align_index, j] = pcs_tensor(self.pcs_const[align_index, j, c], self.paramag_unit_vect[j, c], self.dA[4]) 
1139   
1140               
1141              if not self.centre_fixed: 
1142                  raise RelaxError("The Hessian equations for optimising the paramagnetic centre position are not yet implemented.") 
1143   
1144               
1145              for j in range(self.total_num_params): 
1146                  for k in range(self.total_num_params): 
1147                       
1148                      if self.rdc_flag[align_index]: 
1149                          self.d2chi2[j, k] = self.d2chi2[j, k] + d2chi2_element(self.Dij[align_index], self.Dij_theta[align_index], self.dDij_theta[j, align_index], self.dDij_theta[k, align_index], self.d2Dij_theta[j, k, align_index], self.rdc_sigma_ij[align_index]) 
1150   
1151                       
1152                      if self.pcs_flag[align_index]: 
1153                          self.d2chi2[j, k] = self.d2chi2[j, k] + d2chi2_element(self.deltaij[align_index], self.deltaij_theta[align_index], self.ddeltaij_theta[j, align_index], self.ddeltaij_theta[k, align_index], self.d2deltaij_theta[j, k, align_index], self.pcs_sigma_ij[align_index]) 
1154   
1155           
1156          if self.scaling_flag: 
1157              self.d2chi2 = dot(self.d2chi2, self.scaling_matrix) 
1158   
1159           
1160          return self.d2chi2 * 1.0 
 1161   
1162   
1164          """Calculate the paramagnetic centre to spin vectors, distances and constants.""" 
1165   
1166           
1167          if rank(self.paramag_centre) == 1: 
1168              vectors_single_centre(self.atomic_pos, self.paramag_centre, self.paramag_unit_vect, self.paramag_dist) 
1169          else: 
1170              vectors_centre_per_state(self.atomic_pos, self.paramag_centre, self.paramag_unit_vect, self.paramag_dist) 
1171   
1172           
1173          for align_index in range(self.num_align): 
1174              for j in range(self.num_spins): 
1175                  for c in range(self.N): 
1176                      self.pcs_const[align_index, j, c] = pcs_constant(self.temp[align_index], self.frq[align_index], self.paramag_dist[j, c]) 
1177   
1178                       
1179                      if not self.centre_fixed: 
1180                          pcs_constant_grad(T=self.temp[align_index], Bo=self.frq[align_index], r=self.paramag_dist[j, c], unit_vect=self.paramag_unit_vect[j, c], grad=self.dpcs_const_theta[align_index, j, c]) 
  1181