1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 """Module for the specific analysis of the N-state dynamic model."""
25
26
27 from math import acos, cos, pi, sqrt
28 from minfx.generic import generic_minimise
29 from minfx.grid import grid
30 from numpy import array, dot, float64, identity, ones, zeros
31 from numpy.linalg import inv, norm
32 from re import search
33 from warnings import warn
34
35
36 from api_base import API_base
37 from api_common import API_common
38 import arg_check
39 from float import isNaN, isInf
40 import generic_fns
41 from generic_fns.align_tensor import all_tensors_fixed, get_tensor_object, num_tensors, return_tensor
42 from generic_fns.mol_res_spin import return_spin, spin_loop
43 from generic_fns import pcs, pipes, rdc
44 from generic_fns.structure.cones import Iso_cone
45 import generic_fns.structure.geometric
46 from generic_fns.structure.internal import Internal
47 import generic_fns.structure.mass
48 from maths_fns.n_state_model import N_state_opt
49 from maths_fns.potential import quad_pot
50 from maths_fns.rotation_matrix import two_vect_to_R, euler_to_R_zyz
51 from physical_constants import dipolar_constant, g1H, return_gyromagnetic_ratio
52 from relax_errors import RelaxError, RelaxInfError, RelaxModelError, RelaxNaNError, RelaxNoModelError, RelaxNoTensorError, RelaxNoValueError, RelaxProtonTypeError, RelaxSpinTypeError
53 from relax_io import open_write_file
54 from relax_warnings import RelaxWarning, RelaxDeselectWarning
55 from user_functions.data import Uf_tables; uf_tables = Uf_tables()
56 from user_functions.objects import Desc_container
57
58
60 """Class containing functions for the N-state model."""
61
63 """Initialise the class by placing API_common methods into the API."""
64
65
66 super(N_state_model, self).__init__()
67
68
69 self.model_loop = self._model_loop_single_global
70 self.overfit_deselect = self._overfit_deselect_dummy
71 self.return_conversion_factor = self._return_no_conversion_factor
72 self.set_selected_sim = self._set_selected_sim_global
73 self.sim_return_selected = self._sim_return_selected_global
74 self.test_grid_ops = self._test_grid_ops_general
75
76
77 self.PARAMS.add('r', scope='spin', units='Angstrom', desc='Bond length', py_type=float, grace_string='Bond length')
78 self.PARAMS.add('csa', scope='spin', units='ppm', desc='CSA value', py_type=float, grace_string='\\qCSA\\Q')
79 self.PARAMS.add('heteronuc_type', scope='spin', default='15N', desc='The heteronucleus type', py_type=str)
80 self.PARAMS.add('proton_type', scope='spin', default='1H', desc='The proton type', py_type=str)
81
82
83 self.PARAMS.add_min_data(min_stats_global=False, min_stats_spin=True)
84
85
87 """Assemble all the parameters of the model into a single array.
88
89 @param sim_index: The index of the simulation to optimise. This should be None if
90 normal optimisation is desired.
91 @type sim_index: None or int
92 @return: The parameter vector used for optimisation.
93 @rtype: numpy array
94 """
95
96
97 if not hasattr(cdp, 'model') or not isinstance(cdp.model, str):
98 raise RelaxNoModelError
99
100
101 data_types = self._base_data_types()
102
103
104 param_vector = []
105
106
107 if ('rdc' in data_types or 'pcs' in data_types) and not all_tensors_fixed():
108
109 for i in xrange(len(cdp.align_tensors)):
110
111 if cdp.align_tensors[i].name not in cdp.align_ids:
112 continue
113
114
115 if cdp.align_tensors[i].fixed:
116 continue
117
118
119 param_vector = param_vector + list(cdp.align_tensors[i].A_5D)
120
121
122 if sim_index != None:
123
124 if cdp.model in ['2-domain', 'population']:
125 probs = cdp.probs_sim[sim_index]
126
127
128 if cdp.model == '2-domain':
129 alpha = cdp.alpha_sim[sim_index]
130 beta = cdp.beta_sim[sim_index]
131 gamma = cdp.gamma_sim[sim_index]
132
133
134 else:
135
136 if cdp.model in ['2-domain', 'population']:
137 probs = cdp.probs
138
139
140 if cdp.model == '2-domain':
141 alpha = cdp.alpha
142 beta = cdp.beta
143 gamma = cdp.gamma
144
145
146 if cdp.model in ['2-domain', 'population']:
147 param_vector = param_vector + probs[0:-1]
148
149
150 if cdp.model == '2-domain':
151 for i in xrange(cdp.N):
152 param_vector.append(alpha[i])
153 param_vector.append(beta[i])
154 param_vector.append(gamma[i])
155
156
157 if hasattr(cdp, 'paramag_centre_fixed') and not cdp.paramag_centre_fixed:
158 if not hasattr(cdp, 'paramagnetic_centre'):
159 param_vector.append(0.0)
160 param_vector.append(0.0)
161 param_vector.append(0.0)
162
163 else:
164 param_vector.append(cdp.paramagnetic_centre[0])
165 param_vector.append(cdp.paramagnetic_centre[1])
166 param_vector.append(cdp.paramagnetic_centre[2])
167
168
169 for i in xrange(len(param_vector)):
170 if param_vector[i] == None:
171 param_vector[i] = 0.0
172
173
174 return array(param_vector, float64)
175
176
178 """Create and return the scaling matrix.
179
180 @keyword data_types: The base data types used in the optimisation. This list can contain
181 the elements 'rdc', 'pcs' or 'tensor'.
182 @type data_types: list of str
183 @keyword scaling: If False, then the identity matrix will be returned.
184 @type scaling: bool
185 @return: The square and diagonal scaling matrix.
186 @rtype: numpy rank-2 array
187 """
188
189
190 scaling_matrix = identity(self._param_num(), float64)
191
192
193 if not scaling:
194 return scaling_matrix
195
196
197 pop_start = 0
198 if ('rdc' in data_types or 'pcs' in data_types) and not all_tensors_fixed():
199
200 tensor_num = 0
201 for i in range(len(cdp.align_tensors)):
202
203 if cdp.align_tensors[i].fixed:
204 continue
205
206
207 pop_start = pop_start + 5
208
209
210 for j in range(5):
211 scaling_matrix[5*tensor_num + j, 5*tensor_num + j] = 1.0
212
213
214 tensor_num += 1
215
216
217 if cdp.model in ['2-domain', 'population']:
218 factor = 0.1
219 for i in xrange(pop_start, pop_start + (cdp.N-1)):
220 scaling_matrix[i, i] = factor
221
222
223 if hasattr(cdp, 'paramag_centre_fixed') and not cdp.paramag_centre_fixed:
224 for i in range(-3, 0):
225 scaling_matrix[i, i] = 1e2
226
227
228 return scaling_matrix
229
230
232 """Determine all the base data types.
233
234 The base data types can include::
235 - 'rdc', residual dipolar couplings.
236 - 'pcs', pseudo-contact shifts.
237 - 'noesy', NOE restraints.
238 - 'tensor', alignment tensors.
239
240 @return: A list of all the base data types.
241 @rtype: list of str
242 """
243
244
245 list = []
246
247
248 for spin in spin_loop():
249 if hasattr(spin, 'rdc'):
250 list.append('rdc')
251 break
252
253
254 for spin in spin_loop():
255 if hasattr(spin, 'pcs'):
256 list.append('pcs')
257 break
258
259
260 if not ('rdc' in list or 'pcs' in list) and hasattr(cdp, 'align_tensors'):
261 list.append('tensor')
262
263
264 if hasattr(cdp, 'noe_restraints'):
265 list.append('noesy')
266
267
268 if not list:
269 raise RelaxError("Neither RDC, PCS, NOESY nor alignment tensor data is present.")
270
271
272 return list
273
274
276 """Calculate the average distances.
277
278 The formula used is::
279
280 _N_
281 / 1 \ \ 1/exp
282 <r> = | - > |p1i - p2i|^exp |
283 \ N /__ /
284 i
285
286 where i are the members of the ensemble, N is the total number of structural models, and p1
287 and p2 at the two atom positions.
288
289
290 @param atom1: The atom identification string of the first atom.
291 @type atom1: str
292 @param atom2: The atom identification string of the second atom.
293 @type atom2: str
294 @keyword exp: The exponent used for the averaging, e.g. 1 for linear averaging and -6 for
295 r^-6 NOE averaging.
296 @type exp: int
297 @return: The average distance between the two atoms.
298 @rtype: float
299 """
300
301
302 spin1 = return_spin(atom1)
303 spin2 = return_spin(atom2)
304
305
306 num_models = len(spin1.pos)
307 ave_dist = 0.0
308 for i in range(num_models):
309
310 dist = norm(spin1.pos[i] - spin2.pos[i])
311 ave_dist = ave_dist + dist**(exp)
312
313
314 ave_dist = ave_dist / num_models
315
316
317 ave_dist = ave_dist**(1.0/exp)
318
319
320 return ave_dist
321
322
407
408
409 - def _cone_pdb(self, cone_type=None, scale=1.0, file=None, dir=None, force=False):
410 """Create a PDB file containing a geometric object representing the various cone models.
411
412 Currently the only cone types supported are 'diff in cone' and 'diff on cone'.
413
414
415 @param cone_type: The type of cone model to represent.
416 @type cone_type: str
417 @param scale: The size of the geometric object is eqaul to the average pivot-CoM
418 vector length multiplied by this scaling factor.
419 @type scale: float
420 @param file: The name of the PDB file to create.
421 @type file: str
422 @param dir: The name of the directory to place the PDB file into.
423 @type dir: str
424 @param force: Flag which if set to True will cause any pre-existing file to be
425 overwritten.
426 @type force: int
427 """
428
429
430 if cone_type == 'diff in cone':
431 if not hasattr(cdp, 'S_diff_in_cone'):
432 raise RelaxError("The diffusion in a cone model has not yet been determined.")
433 elif cone_type == 'diff on cone':
434 if not hasattr(cdp, 'S_diff_on_cone'):
435 raise RelaxError("The diffusion on a cone model has not yet been determined.")
436 else:
437 raise RelaxError("The cone type " + repr(cone_type) + " is unknown.")
438
439
440 inc = 20
441
442
443 R = zeros((3, 3), float64)
444 two_vect_to_R(array([0, 0, 1], float64), cdp.ave_pivot_CoM/norm(cdp.ave_pivot_CoM), R)
445
446
447 if cone_type == 'diff in cone':
448 angle = cdp.theta_diff_in_cone
449 elif cone_type == 'diff on cone':
450 angle = cdp.theta_diff_on_cone
451 cone = Iso_cone(angle)
452
453
454 structure = Internal()
455
456
457 structure.add_molecule(name='cone')
458
459
460 mol = structure.structural_data[0].mol[0]
461
462
463 mol.atom_add(pdb_record='HETATM', atom_num=1, atom_name='R', res_name='PIV', res_num=1, pos=cdp.pivot_point, element='C')
464
465
466 print("\nGenerating the average pivot-CoM vectors.")
467 sim_vectors = None
468 if hasattr(cdp, 'ave_pivot_CoM_sim'):
469 sim_vectors = cdp.ave_pivot_CoM_sim
470 res_num = generic_fns.structure.geometric.generate_vector_residues(mol=mol, vector=cdp.ave_pivot_CoM, atom_name='Ave', res_name_vect='AVE', sim_vectors=sim_vectors, res_num=2, origin=cdp.pivot_point, scale=scale)
471
472
473 print("\nGenerating the cone outer edge.")
474 cap_start_atom = mol.atom_num[-1]+1
475 generic_fns.structure.geometric.cone_edge(mol=mol, cone=cone, res_name='CON', res_num=3, apex=cdp.pivot_point, R=R, scale=norm(cdp.pivot_CoM), inc=inc)
476
477
478 if cone_type == 'diff in cone':
479 print("\nGenerating the cone cap.")
480 cone_start_atom = mol.atom_num[-1]+1
481 generic_fns.structure.geometric.generate_vector_dist(mol=mol, res_name='CON', res_num=3, centre=cdp.pivot_point, R=R, limit_check=cone.limit_check, scale=norm(cdp.pivot_CoM), inc=inc)
482 generic_fns.structure.geometric.stitch_cone_to_edge(mol=mol, cone=cone, dome_start=cone_start_atom, edge_start=cap_start_atom+1, inc=inc)
483
484
485 print("\nGenerating the PDB file.")
486 pdb_file = open_write_file(file, dir, force=force)
487 structure.write_pdb(pdb_file)
488 pdb_file.close()
489
490
492 """Disassemble the parameter vector and place the values into the relevant variables.
493
494 For the 2-domain N-state model, the parameters are stored in the probability and Euler angle data structures. For the population N-state model, only the probabilities are stored. If RDCs are present and alignment tensors are optimised, then these are stored as well.
495
496 @keyword data_types: The base data types used in the optimisation. This list can contain the elements 'rdc', 'pcs' or 'tensor'.
497 @type data_types: list of str
498 @keyword param_vector: The parameter vector returned from optimisation.
499 @type param_vector: numpy array
500 @keyword sim_index: The index of the simulation to optimise. This should be None if normal optimisation is desired.
501 @type sim_index: None or int
502 """
503
504
505 if not hasattr(cdp, 'model') or not isinstance(cdp.model, str):
506 raise RelaxNoModelError
507
508
509 if ('rdc' in data_types or 'pcs' in data_types) and not all_tensors_fixed():
510
511 tensor_num = 0
512 for i in xrange(len(cdp.align_tensors)):
513
514 if cdp.align_tensors[i].name not in cdp.align_ids:
515 continue
516
517
518 if cdp.align_tensors[i].fixed:
519 continue
520
521
522 if sim_index == None:
523 cdp.align_tensors[i].Axx = param_vector[5*tensor_num]
524 cdp.align_tensors[i].Ayy = param_vector[5*tensor_num+1]
525 cdp.align_tensors[i].Axy = param_vector[5*tensor_num+2]
526 cdp.align_tensors[i].Axz = param_vector[5*tensor_num+3]
527 cdp.align_tensors[i].Ayz = param_vector[5*tensor_num+4]
528
529
530 else:
531 cdp.align_tensors[i].Axx_sim[sim_index] = param_vector[5*tensor_num]
532 cdp.align_tensors[i].Ayy_sim[sim_index] = param_vector[5*tensor_num+1]
533 cdp.align_tensors[i].Axy_sim[sim_index] = param_vector[5*tensor_num+2]
534 cdp.align_tensors[i].Axz_sim[sim_index] = param_vector[5*tensor_num+3]
535 cdp.align_tensors[i].Ayz_sim[sim_index] = param_vector[5*tensor_num+4]
536
537
538 tensor_num += 1
539
540
541 param_vector = param_vector[5*tensor_num:]
542
543
544 if sim_index != None:
545
546 if cdp.model in ['2-domain', 'population']:
547 probs = cdp.probs_sim[sim_index]
548
549
550 if cdp.model == '2-domain':
551 alpha = cdp.alpha_sim[sim_index]
552 beta = cdp.beta_sim[sim_index]
553 gamma = cdp.gamma_sim[sim_index]
554
555
556 else:
557
558 if cdp.model in ['2-domain', 'population']:
559 probs = cdp.probs
560
561
562 if cdp.model == '2-domain':
563 alpha = cdp.alpha
564 beta = cdp.beta
565 gamma = cdp.gamma
566
567
568 if cdp.model in ['2-domain', 'population']:
569 for i in xrange(cdp.N-1):
570 probs[i] = param_vector[i]
571
572
573 probs[-1] = 1 - sum(probs[0:-1])
574
575
576 if cdp.model == '2-domain':
577 for i in xrange(cdp.N):
578 alpha[i] = param_vector[cdp.N-1 + 3*i]
579 beta[i] = param_vector[cdp.N-1 + 3*i + 1]
580 gamma[i] = param_vector[cdp.N-1 + 3*i + 2]
581
582
583 if hasattr(cdp, 'paramag_centre_fixed') and not cdp.paramag_centre_fixed:
584
585 if not hasattr(cdp, 'paramagnetic_centre'):
586 cdp.paramagnetic_centre = zeros(3, float64)
587
588
589 cdp.paramagnetic_centre[0] = param_vector[-3]
590 cdp.paramagnetic_centre[1] = param_vector[-2]
591 cdp.paramagnetic_centre[2] = param_vector[-1]
592
593
595 """Remove all structures or states which have no probability."""
596
597
598 pipes.test()
599
600
601 if not hasattr(cdp, 'model'):
602 raise RelaxNoModelError('N-state')
603
604
605 if not hasattr(cdp, 'probs'):
606 raise RelaxError("The N-state model populations do not exist.")
607
608
609 i = 0
610 while True:
611
612 if i == cdp.N - 1:
613 break
614
615
616 if cdp.probs[i] < 1e-5:
617
618 cdp.probs.pop(i)
619
620
621 cdp.structure.structural_data.pop(i)
622
623
624 for spin in spin_loop():
625
626 if hasattr(spin, 'pos'):
627 spin.pos.pop(i)
628
629
630 if hasattr(spin, 'xh_vect'):
631 spin.xh_vect.pop(i)
632 if hasattr(spin, 'bond_vect'):
633 spin.bond_vect.pop(i)
634
635
636 cdp.N -= 1
637
638
639 continue
640
641
642 i += 1
643
644
646 """Function for setting up the linear constraint matrices A and b.
647
648 Standard notation
649 =================
650
651 The N-state model constraints are::
652
653 0 <= pc <= 1,
654
655 where p is the probability and c corresponds to state c.
656
657
658 Matrix notation
659 ===============
660
661 In the notation A.x >= b, where A is an matrix of coefficients, x is an array of parameter
662 values, and b is a vector of scalars, these inequality constraints are::
663
664 | 1 0 0 | | 0 |
665 | | | |
666 |-1 0 0 | | -1 |
667 | | | |
668 | 0 1 0 | | 0 |
669 | | | p0 | | |
670 | 0 -1 0 | | | | -1 |
671 | | . | p1 | >= | |
672 | 0 0 1 | | | | 0 |
673 | | | p2 | | |
674 | 0 0 -1 | | -1 |
675 | | | |
676 |-1 -1 -1 | | -1 |
677 | | | |
678 | 1 1 1 | | 0 |
679
680 This example is for a 4-state model, the last probability pn is not included as this
681 parameter does not exist (because the sum of pc is equal to 1). The Euler angle parameters
682 have been excluded here but will be included in the returned A and b objects. These
683 parameters simply add columns of zero to the A matrix and have no effect on b. The last two
684 rows correspond to the inequality::
685
686 0 <= pN <= 1.
687
688 As::
689 N-1
690 \
691 pN = 1 - > pc,
692 /__
693 c=1
694
695 then::
696
697 -p1 - p2 - ... - p(N-1) >= -1,
698
699 p1 + p2 + ... + p(N-1) >= 0.
700
701
702 @keyword data_types: The base data types used in the optimisation. This list can
703 contain the elements 'rdc', 'pcs' or 'tensor'.
704 @type data_types: list of str
705 @keyword scaling_matrix: The diagonal scaling matrix.
706 @type scaling_matrix: numpy rank-2 square matrix
707 @return: The matrices A and b.
708 @rtype: tuple of len 2 of a numpy rank-2, size NxM matrix and numpy
709 rank-1, size N array
710 """
711
712
713 pop_start = 0
714 if ('rdc' in data_types or 'pcs' in data_types) and not all_tensors_fixed():
715
716 for i in range(len(cdp.align_tensors)):
717
718 if cdp.align_tensors[i].fixed:
719 continue
720
721
722 pop_start += 5
723
724
725 A = []
726 b = []
727 zero_array = zeros(self._param_num(), float64)
728 i = pop_start
729 j = 0
730
731
732 if cdp.model in ['2-domain', 'population']:
733
734 for k in xrange(cdp.N - 1):
735
736 A.append(zero_array * 0.0)
737 A.append(zero_array * 0.0)
738 A[j][i] = 1.0
739 A[j+1][i] = -1.0
740 b.append(0.0)
741 b.append(-1.0 / scaling_matrix[i, i])
742 j = j + 2
743
744
745 i = i + 1
746
747
748 A.append(zero_array * 0.0)
749 A.append(zero_array * 0.0)
750 for i in xrange(pop_start, self._param_num()):
751 A[-2][i] = -1.0
752 A[-1][i] = 1.0
753 b.append(-1.0 / scaling_matrix[i, i])
754 b.append(0.0)
755
756
757 A = array(A, float64)
758 b = array(b, float64)
759
760
761 return A, b
762
763
765 """Extract and unpack the back calculated data.
766
767 @param model: The instantiated class containing the target function.
768 @type model: class instance
769 """
770
771
772 if not hasattr(cdp, 'align_tensors'):
773 return
774
775
776 for i in xrange(len(cdp.align_ids)):
777
778 if cdp.align_tensors[i].fixed:
779 continue
780
781
782 align_id = cdp.align_ids[i]
783
784
785 rdc_flag = False
786 if hasattr(cdp, 'rdc_ids') and align_id in cdp.rdc_ids:
787 rdc_flag = True
788 pcs_flag = False
789 if hasattr(cdp, 'pcs_ids') and align_id in cdp.pcs_ids:
790 pcs_flag = True
791
792
793 data_index = 0
794 for spin in spin_loop():
795
796 if not spin.select:
797 continue
798
799
800 if pcs_flag and hasattr(spin, 'pcs'):
801
802 if not hasattr(spin, 'pcs_bc'):
803 spin.pcs_bc = {}
804
805
806 spin.pcs_bc[align_id] = model.deltaij_theta[i, data_index] * 1e6
807
808
809 if rdc_flag and hasattr(spin, 'rdc') and (hasattr(spin, 'xh_vect') or hasattr(spin, 'bond_vect')):
810
811 if not hasattr(spin, 'rdc_bc'):
812 spin.rdc_bc = {}
813
814
815 spin.rdc_bc[align_id] = model.Dij_theta[i, data_index]
816
817
818 if hasattr(spin, 'pcs') or (hasattr(spin, 'rdc') and (hasattr(spin, 'xh_vect') or hasattr(spin, 'bond_vect'))):
819 data_index = data_index + 1
820
821
823 """Set up the atomic position data structures for optimisation using PCSs and PREs as base data sets.
824
825 @return: The atomic positions (the first index is the spins, the second is the structures, and the third is the atomic coordinates) and the paramagnetic centre.
826 @rtype: numpy rank-3 array, numpy rank-1 array.
827 """
828
829
830 atomic_pos = []
831
832
833 for spin in spin_loop():
834
835 if not spin.select:
836 continue
837
838
839 if not hasattr(spin, 'pcs') and not hasattr(spin, 'rdc') and not hasattr(spin, 'pre'):
840 continue
841
842
843 if type(spin.pos[0]) in [float, float64]:
844 atomic_pos.append([spin.pos])
845 else:
846 atomic_pos.append(spin.pos)
847
848
849 atomic_pos = array(atomic_pos, float64)
850
851
852 if hasattr(cdp, 'paramagnetic_centre'):
853 paramag_centre = array(cdp.paramagnetic_centre)
854 else:
855 paramag_centre = zeros(3, float64)
856
857
858 return atomic_pos, paramag_centre
859
860
862 """Set up the data structures for optimisation using PCSs as base data sets.
863
864 @keyword sim_index: The index of the simulation to optimise. This should be None if normal optimisation is desired.
865 @type sim_index: None or int
866 @return: The assembled data structures for using PCSs as the base data for optimisation. These include:
867 - the PCS values.
868 - the unit vectors connecting the paramagnetic centre (the electron spin) to
869 - the PCS weight.
870 - the nuclear spin.
871 - the pseudocontact shift constants.
872 @rtype: tuple of (numpy rank-2 array, numpy rank-2 array, numpy rank-2 array, numpy rank-1 array, numpy rank-1 array)
873 """
874
875
876 if not hasattr(cdp, 'paramagnetic_centre') and (hasattr(cdp, 'paramag_centre_fixed') and cdp.paramag_centre_fixed):
877 raise RelaxError("The paramagnetic centre has not yet been specified.")
878 if not hasattr(cdp, 'temperature'):
879 raise RelaxError("The experimental temperatures have not been set.")
880 if not hasattr(cdp, 'frq'):
881 raise RelaxError("The spectrometer frequencies of the experiments have not been set.")
882
883
884 pcs = []
885 pcs_err = []
886 pcs_weight = []
887 temp = []
888 frq = []
889
890
891 for align_id in cdp.align_ids:
892
893 if (hasattr(cdp, 'rdc_ids') and not align_id in cdp.rdc_ids) and (hasattr(cdp, 'pcs_ids') and not align_id in cdp.pcs_ids):
894 continue
895
896
897 pcs.append([])
898 pcs_err.append([])
899 pcs_weight.append([])
900
901
902 if cdp.temperature.has_key(align_id):
903 temp.append(cdp.temperature[align_id])
904 else:
905 temp.append(0.0)
906
907
908 if cdp.frq.has_key(align_id):
909 frq.append(cdp.frq[align_id] * 2.0 * pi / g1H)
910 else:
911 frq.append(1e-10)
912
913
914 j = 0
915 for spin in spin_loop():
916
917 if not spin.select:
918 continue
919
920
921 if not hasattr(spin, 'pcs'):
922
923 if hasattr(spin, 'rdc'):
924 pcs[-1].append(None)
925 pcs_err[-1].append(None)
926 pcs_weight[-1].append(None)
927 j = j + 1
928
929
930 continue
931
932
933 if align_id in spin.pcs.keys():
934 if sim_index != None:
935 pcs[-1].append(spin.pcs_sim[align_id][sim_index])
936 else:
937 pcs[-1].append(spin.pcs[align_id])
938 else:
939 pcs[-1].append(None)
940
941
942 if hasattr(spin, 'pcs_err') and align_id in spin.pcs_err.keys():
943 pcs_err[-1].append(spin.pcs_err[align_id])
944 else:
945 pcs_err[-1].append(None)
946
947
948 if hasattr(spin, 'pcs_weight') and align_id in spin.pcs_weight.keys():
949 pcs_weight[-1].append(spin.pcs_weight[align_id])
950 else:
951 pcs_weight[-1].append(1.0)
952
953
954 j = j + 1
955
956
957 pcs = array(pcs, float64)
958 pcs_err = array(pcs_err, float64)
959 pcs_weight = array(pcs_weight, float64)
960
961
962 pcs = pcs * 1e-6
963 pcs_err = pcs_err * 1e-6
964
965
966 return pcs, pcs_err, pcs_weight, temp, frq
967
968
970 """Set up the data structures for optimisation using RDCs as base data sets.
971
972 @keyword sim_index: The index of the simulation to optimise. This should be None if normal optimisation is desired.
973 @type sim_index: None or int
974 @return: The assembled data structures for using RDCs as the base data for optimisation. These include:
975 - rdc, the RDC values.
976 - rdc_err, the RDC errors.
977 - rdc_weight, the RDC weights.
978 - vectors, the heteronucleus to proton vectors.
979 - rdc_const, the dipolar constants.
980 @rtype: tuple of (numpy rank-2 array, numpy rank-2 array, numpy rank-2 array)
981 """
982
983
984 rdc = []
985 rdc_err = []
986 rdc_weight = []
987 unit_vect = []
988 rdc_const = []
989
990
991 for spin, spin_id in spin_loop(return_id=True):
992
993 if not spin.select:
994 continue
995
996
997 if not hasattr(spin, 'rdc'):
998
999 if hasattr(spin, 'pcs'):
1000 unit_vect.append(None)
1001 rdc_const.append(None)
1002
1003
1004 continue
1005
1006
1007 if not hasattr(spin, 'members') and not hasattr(spin, 'xh_vect') and not hasattr(spin, 'bond_vect'):
1008
1009 warn(RelaxWarning("RDC data exists but the XH bond vectors are missing, skipping spin %s." % spin_id))
1010
1011
1012 if hasattr(spin, 'pcs'):
1013 unit_vect.append(None)
1014 rdc_const.append(None)
1015
1016
1017 continue
1018
1019
1020 if hasattr(spin, 'members'):
1021
1022 if len(spin.members) != 3:
1023 warn(RelaxWarning("Only methyl group pseudo atoms are supported due to their fast rotation, skipping spin %s." % spin_id))
1024 continue
1025
1026
1027 vect = zeros(3, float64)
1028 for i in range(3):
1029
1030 spin_i = return_spin(spin.members[i])
1031
1032
1033 if hasattr(spin_i, 'xh_vect'):
1034 obj = getattr(spin_i, 'xh_vect')
1035 else:
1036 obj = getattr(spin_i, 'bond_vect')
1037 vect = vect + obj
1038
1039
1040 vect = vect / norm(vect)
1041
1042
1043 else:
1044
1045 if hasattr(spin, 'xh_vect'):
1046 vect = getattr(spin, 'xh_vect')
1047 else:
1048 vect = getattr(spin, 'bond_vect')
1049
1050
1051 if isinstance(vect[0], float):
1052 unit_vect.append([vect])
1053 else:
1054 unit_vect.append(vect)
1055
1056
1057 if not hasattr(spin, 'heteronuc_type'):
1058 raise RelaxSpinTypeError
1059 if not hasattr(spin, 'proton_type'):
1060 raise RelaxProtonTypeError
1061 if not hasattr(spin, 'r'):
1062 raise RelaxNoValueError("bond length")
1063
1064
1065 gx = return_gyromagnetic_ratio(spin.heteronuc_type)
1066 gh = return_gyromagnetic_ratio(spin.proton_type)
1067
1068
1069 rdc_const.append(3.0/(2.0*pi) * dipolar_constant(gx, gh, spin.r))
1070
1071
1072 num = None
1073 for i in range(len(unit_vect)):
1074
1075 if num == None:
1076 if unit_vect[i] != None:
1077 num = len(unit_vect[i])
1078 continue
1079
1080
1081 if unit_vect[i] != None and len(unit_vect[i]) != num:
1082 raise RelaxError, "The number of bond vectors for all spins do no match:\n%s" % unit_vect
1083
1084
1085 if num == None:
1086 raise RelaxError, "No bond vectors could be found."
1087
1088
1089 for i in range(len(unit_vect)):
1090 if unit_vect[i] == None:
1091 unit_vect[i] = [[None, None, None]]*num
1092
1093
1094 for align_id in cdp.align_ids:
1095
1096 if (hasattr(cdp, 'rdc_ids') and not align_id in cdp.rdc_ids) and (hasattr(cdp, 'pcs_ids') and not align_id in cdp.pcs_ids):
1097 continue
1098
1099
1100 rdc.append([])
1101 rdc_err.append([])
1102 rdc_weight.append([])
1103
1104
1105 for spin in spin_loop():
1106
1107 if not spin.select:
1108 continue
1109
1110
1111 if not hasattr(spin, 'rdc') or (not hasattr(spin, 'members') and not hasattr(spin, 'xh_vect') and not hasattr(spin, 'bond_vect')):
1112
1113 if hasattr(spin, 'pcs'):
1114 rdc[-1].append(None)
1115 rdc_err[-1].append(None)
1116 rdc_weight[-1].append(None)
1117
1118
1119 continue
1120
1121
1122 value = None
1123 error = None
1124
1125
1126 if hasattr(spin, 'members') and align_id in spin.rdc.keys():
1127
1128 if len(spin.members) != 3:
1129 continue
1130
1131
1132
1133
1134 if sim_index != None:
1135 value = -3.0 * spin.rdc_sim[align_id][sim_index]
1136 else:
1137 value = -3.0 * spin.rdc[align_id]
1138
1139
1140 if hasattr(spin, 'rdc_err') and align_id in spin.rdc_err.keys():
1141 error = -3.0 * spin.rdc_err[align_id]
1142
1143
1144 elif align_id in spin.rdc.keys():
1145
1146 if sim_index != None:
1147 value = spin.rdc_sim[align_id][sim_index]
1148 else:
1149 value = spin.rdc[align_id]
1150
1151
1152 if hasattr(spin, 'rdc_err') and align_id in spin.rdc_err.keys():
1153 error = spin.rdc_err[align_id]
1154
1155
1156 rdc[-1].append(value)
1157
1158
1159 rdc_err[-1].append(error)
1160
1161
1162 if hasattr(spin, 'rdc_weight') and align_id in spin.rdc_weight.keys():
1163 rdc_weight[-1].append(spin.rdc_weight[align_id])
1164 else:
1165 rdc_weight[-1].append(1.0)
1166
1167
1168 rdc = array(rdc, float64)
1169 rdc_err = array(rdc_err, float64)
1170 rdc_weight = array(rdc_weight, float64)
1171 unit_vect = array(unit_vect, float64)
1172 rdc_const = array(rdc_const, float64)
1173
1174
1175 return rdc, rdc_err, rdc_weight, unit_vect, rdc_const
1176
1177
1179 """Set up the data structures for optimisation using alignment tensors as base data sets.
1180
1181 @keyword sim_index: The index of the simulation to optimise. This should be None if
1182 normal optimisation is desired.
1183 @type sim_index: None or int
1184 @return: The assembled data structures for using alignment tensors as the base
1185 data for optimisation. These include:
1186 - full_tensors, the data of the full alignment tensors.
1187 - red_tensor_elem, the tensors as concatenated rank-1 5D arrays.
1188 - red_tensor_err, the tensor errors as concatenated rank-1 5D
1189 arrays.
1190 - full_in_ref_frame, flags specifying if the tensor in the reference
1191 frame is the full or reduced tensor.
1192 @rtype: tuple of (list, numpy rank-1 array, numpy rank-1 array, numpy rank-1
1193 array)
1194 """
1195
1196
1197 n = len(cdp.align_tensors.reduction)
1198 full_tensors = zeros(n*5, float64)
1199 red_tensors = zeros(n*5, float64)
1200 red_err = ones(n*5, float64) * 1e-5
1201 full_in_ref_frame = zeros(n, float64)
1202
1203
1204 for i, tensor in self._tensor_loop(red=False):
1205
1206 full_tensors[5*i + 0] = tensor.Axx
1207 full_tensors[5*i + 1] = tensor.Ayy
1208 full_tensors[5*i + 2] = tensor.Axy
1209 full_tensors[5*i + 3] = tensor.Axz
1210 full_tensors[5*i + 4] = tensor.Ayz
1211
1212
1213 if cdp.ref_domain == tensor.domain:
1214 full_in_ref_frame[i] = 1
1215
1216
1217 for i, tensor in self._tensor_loop(red=True):
1218
1219 if sim_index != None:
1220 red_tensors[5*i + 0] = tensor.Axx_sim[sim_index]
1221 red_tensors[5*i + 1] = tensor.Ayy_sim[sim_index]
1222 red_tensors[5*i + 2] = tensor.Axy_sim[sim_index]
1223 red_tensors[5*i + 3] = tensor.Axz_sim[sim_index]
1224 red_tensors[5*i + 4] = tensor.Ayz_sim[sim_index]
1225
1226
1227 else:
1228 red_tensors[5*i + 0] = tensor.Axx
1229 red_tensors[5*i + 1] = tensor.Ayy
1230 red_tensors[5*i + 2] = tensor.Axy
1231 red_tensors[5*i + 3] = tensor.Axz
1232 red_tensors[5*i + 4] = tensor.Ayz
1233
1234
1235 if hasattr(tensor, 'Axx_err'):
1236 red_err[5*i + 0] = tensor.Axx_err
1237 red_err[5*i + 1] = tensor.Ayy_err
1238 red_err[5*i + 2] = tensor.Axy_err
1239 red_err[5*i + 3] = tensor.Axz_err
1240 red_err[5*i + 4] = tensor.Ayz_err
1241
1242
1243 return full_tensors, red_tensors, red_err, full_in_ref_frame
1244
1245
1247 """Set up the data structures for the fixed alignment tensors.
1248
1249 @return: The assembled data structures for the fixed alignment tensors.
1250 @rtype: numpy rank-1 array.
1251 """
1252
1253
1254 n = num_tensors(skip_fixed=False) - num_tensors(skip_fixed=True)
1255 tensors = zeros(n*5, float64)
1256
1257
1258 if n == 0:
1259 return None
1260
1261
1262 index = 0
1263 for i in range(len(cdp.align_tensors)):
1264
1265 if not cdp.align_tensors[i].fixed:
1266 continue
1267
1268
1269 tensors[5*index + 0] = cdp.align_tensors[i].Axx
1270 tensors[5*index + 1] = cdp.align_tensors[i].Ayy
1271 tensors[5*index + 2] = cdp.align_tensors[i].Axy
1272 tensors[5*index + 3] = cdp.align_tensors[i].Axz
1273 tensors[5*index + 4] = cdp.align_tensors[i].Ayz
1274
1275
1276 index += 1
1277
1278
1279 return tensors
1280
1281
1283 """Determine the number of data points used in the model.
1284
1285 @return: The number, n, of data points in the model.
1286 @rtype: int
1287 """
1288
1289
1290 data_types = self._base_data_types()
1291
1292
1293 n = 0
1294
1295
1296 for spin in spin_loop():
1297
1298 if not spin.select:
1299 continue
1300
1301
1302 if 'rdc' in data_types:
1303 if hasattr(spin, 'rdc'):
1304 for rdc in spin.rdc:
1305 if isinstance(rdc, float):
1306 n = n + 1
1307
1308
1309 if 'pcs' in data_types:
1310 if hasattr(spin, 'pcs'):
1311 for pcs in spin.pcs:
1312 if isinstance(pcs, float):
1313 n = n + 1
1314
1315
1316 if 'tensor' in data_types:
1317 n = n + 5*len(cdp.align_tensors)
1318
1319
1320 return n
1321
1322
1324 """Set the number of states in the N-state model.
1325
1326 @param N: The number of states.
1327 @type N: int
1328 """
1329
1330
1331 pipes.test()
1332
1333
1334 if not hasattr(cdp, 'model'):
1335 raise RelaxNoModelError('N-state')
1336
1337
1338 cdp.N = N
1339
1340
1341 self._update_model()
1342
1343
1345 """Return the N-state model index for the given parameter.
1346
1347 @param param: The N-state model parameter.
1348 @type param: str
1349 @return: The N-state model index.
1350 @rtype: str
1351 """
1352
1353
1354 if search('^p[0-9]*$', param):
1355 return int(param[1:])
1356
1357
1358 if search('^alpha', param):
1359 return int(param[5:])
1360
1361
1362 if search('^beta', param):
1363 return int(param[4:])
1364
1365
1366 if search('^gamma', param):
1367 return int(param[5:])
1368
1369
1370 return None
1371
1372
1374 """Determine the number of parameters in the model.
1375
1376 @return: The number of model parameters.
1377 @rtype: int
1378 """
1379
1380
1381 data_types = self._base_data_types()
1382
1383
1384 num = 0
1385
1386
1387 if ('rdc' in data_types or 'pcs' in data_types) and not all_tensors_fixed():
1388
1389 for i in xrange(len(cdp.align_tensors)):
1390
1391 if cdp.align_tensors[i].name not in cdp.align_ids:
1392 continue
1393
1394
1395 if cdp.align_tensors[i].fixed:
1396 continue
1397
1398
1399 num += 5
1400
1401
1402 if cdp.model in ['2-domain', 'population']:
1403 num = num + (cdp.N - 1)
1404
1405
1406 if cdp.model == '2-domain':
1407 num = num + 3*cdp.N
1408
1409
1410 if hasattr(cdp, 'paramag_centre_fixed') and not cdp.paramag_centre_fixed:
1411 num = num + 3
1412
1413
1414 return num
1415
1416
1417 - def _ref_domain(self, ref=None):
1418 """Set the reference domain for the '2-domain' N-state model.
1419
1420 @param ref: The reference domain.
1421 @type ref: str
1422 """
1423
1424
1425 pipes.test()
1426
1427
1428 if not hasattr(cdp, 'model'):
1429 raise RelaxNoModelError('N-state')
1430
1431
1432 if cdp.model != '2-domain':
1433 raise RelaxError("Setting the reference domain is only possible for the '2-domain' N-state model.")
1434
1435
1436 exists = False
1437 for tensor_cont in cdp.align_tensors:
1438 if tensor_cont.domain == ref:
1439 exists = True
1440 if not exists:
1441 raise RelaxError("The reference domain cannot be found within any of the loaded tensors.")
1442
1443
1444 cdp.ref_domain = ref
1445
1446
1447 self._update_model()
1448
1449
1451 """Select the N-state model type.
1452
1453 @param model: The N-state model type. Can be one of '2-domain', 'population', or 'fixed'.
1454 @type model: str
1455 """
1456
1457
1458 pipes.test()
1459
1460
1461 if not model in ['2-domain', 'population', 'fixed']:
1462 raise RelaxError("The model name " + repr(model) + " is invalid.")
1463
1464
1465 if hasattr(cdp, 'model'):
1466 warn(RelaxWarning("The N-state model has already been set up. Switching from model '%s' to '%s'." % (cdp.model, model)))
1467
1468
1469 cdp.model = model
1470
1471
1472 cdp.params = []
1473
1474
1475 self._update_model()
1476
1477
1479 """Initialise the target function for optimisation or direct calculation.
1480
1481 @param sim_index: The index of the simulation to optimise. This should be None if normal optimisation is desired.
1482 @type sim_index: None or int
1483 @param scaling: If True, diagonal scaling is enabled during optimisation to allow the problem to be better conditioned.
1484 @type scaling: bool
1485 """
1486
1487
1488 if not hasattr(cdp, 'model'):
1489 raise RelaxNoModelError('N-state')
1490
1491
1492 if cdp.model == '2-domain':
1493
1494 if not hasattr(cdp, 'N'):
1495 raise RelaxError("The number of states has not been set.")
1496
1497
1498 if not hasattr(cdp, 'ref_domain'):
1499 raise RelaxError("The reference domain has not been set.")
1500
1501
1502 self._update_model()
1503
1504
1505 param_vector = self._assemble_param_vector(sim_index=sim_index)
1506
1507
1508 data_types = self._base_data_types()
1509
1510
1511 probs = None
1512 if hasattr(cdp, 'probs'):
1513 probs = cdp.probs
1514
1515
1516 scaling_matrix = None
1517 if len(param_vector):
1518 scaling_matrix = self._assemble_scaling_matrix(data_types=data_types, scaling=scaling)
1519 param_vector = dot(inv(scaling_matrix), param_vector)
1520
1521
1522 full_tensors, red_tensor_elem, red_tensor_err, full_in_ref_frame = None, None, None, None
1523 if 'tensor' in data_types:
1524 full_tensors, red_tensor_elem, red_tensor_err, full_in_ref_frame = self._minimise_setup_tensors(sim_index=sim_index)
1525
1526
1527 pcs, pcs_err, pcs_weight, temp, frq = None, None, None, None, None
1528 if 'pcs' in data_types:
1529 pcs, pcs_err, pcs_weight, temp, frq = self._minimise_setup_pcs(sim_index=sim_index)
1530
1531
1532 rdcs, rdc_err, rdc_weight, xh_vect, rdc_dj = None, None, None, None, None
1533 if 'rdc' in data_types:
1534 rdcs, rdc_err, rdc_weight, xh_vect, rdc_dj = self._minimise_setup_rdcs(sim_index=sim_index)
1535
1536
1537 fixed_tensors = None
1538 if 'rdc' in data_types or 'pcs' in data_types:
1539 full_tensors = self._minimise_setup_fixed_tensors()
1540
1541
1542 fixed_tensors = []
1543 for i in range(len(cdp.align_tensors)):
1544 if cdp.align_tensors[i].fixed:
1545 fixed_tensors.append(True)
1546 else:
1547 fixed_tensors.append(False)
1548
1549
1550 atomic_pos, paramag_centre, centre_fixed = None, None, True
1551 if 'pcs' in data_types or 'pre' in data_types:
1552 atomic_pos, paramag_centre = self._minimise_setup_atomic_pos()
1553
1554
1555 if hasattr(cdp, 'paramag_centre_fixed'):
1556 centre_fixed = cdp.paramag_centre_fixed
1557
1558
1559 model = N_state_opt(model=cdp.model, N=cdp.N, init_params=param_vector, probs=probs, full_tensors=full_tensors, red_data=red_tensor_elem, red_errors=red_tensor_err, full_in_ref_frame=full_in_ref_frame, fixed_tensors=fixed_tensors, pcs=pcs, rdcs=rdcs, pcs_errors=pcs_err, rdc_errors=rdc_err, pcs_weights=pcs_weight, rdc_weights=rdc_weight, xh_vect=xh_vect, temp=temp, frq=frq, dip_const=rdc_dj, atomic_pos=atomic_pos, paramag_centre=paramag_centre, scaling_matrix=scaling_matrix, centre_fixed=centre_fixed)
1560
1561
1562 return model, param_vector, data_types, scaling_matrix
1563
1564
1566 """Generator method for looping over the full or reduced tensors.
1567
1568 @keyword red: A flag which if True causes the reduced tensors to be returned, and if False
1569 the full tensors are returned.
1570 @type red: bool
1571 @return: The tensor index and the tensor.
1572 @rtype: (int, AlignTensorData instance)
1573 """
1574
1575
1576 n = len(cdp.align_tensors.reduction)
1577
1578
1579 data = cdp.align_tensors
1580 list = data.reduction
1581
1582
1583 if red:
1584 index = 1
1585 else:
1586 index = 0
1587
1588
1589 for i in range(n):
1590 yield i, data[list[i][index]]
1591
1592
1594 """Update the model parameters as necessary."""
1595
1596
1597 if not hasattr(cdp, 'params'):
1598 cdp.params = []
1599
1600
1601 if not hasattr(cdp, 'N'):
1602
1603 if hasattr(cdp, 'structure'):
1604 cdp.N = cdp.structure.num_models()
1605
1606
1607 else:
1608 return
1609
1610
1611 if not cdp.params:
1612
1613 if cdp.model in ['2-domain', 'population']:
1614 for i in xrange(cdp.N-1):
1615 cdp.params.append('p' + repr(i))
1616
1617
1618 if cdp.model == '2-domain':
1619 for i in xrange(cdp.N):
1620 cdp.params.append('alpha' + repr(i))
1621 cdp.params.append('beta' + repr(i))
1622 cdp.params.append('gamma' + repr(i))
1623
1624
1625 if cdp.model in ['2-domain', 'population']:
1626 if not hasattr(cdp, 'probs'):
1627 cdp.probs = [None] * cdp.N
1628 if cdp.model == '2-domain':
1629 if not hasattr(cdp, 'alpha'):
1630 cdp.alpha = [None] * cdp.N
1631 if not hasattr(cdp, 'beta'):
1632 cdp.beta = [None] * cdp.N
1633 if not hasattr(cdp, 'gamma'):
1634 cdp.gamma = [None] * cdp.N
1635
1636
1637 data_types = self._base_data_types()
1638
1639
1640 if hasattr(cdp, 'align_ids'):
1641 for id in cdp.align_ids:
1642
1643 if not hasattr(cdp, 'align_tensors'):
1644 generic_fns.align_tensor.init(tensor=id, params=[0.0, 0.0, 0.0, 0.0, 0.0])
1645
1646
1647 exists = False
1648 for tensor in cdp.align_tensors:
1649 if id == tensor.name:
1650 exists = True
1651
1652
1653 if not exists:
1654 generic_fns.align_tensor.init(tensor=id, params=[0.0, 0.0, 0.0, 0.0, 0.0])
1655
1656
1658 """Loop over the base data of the spins - RDCs, PCSs, and NOESY data.
1659
1660 This loop iterates for each data point (RDC, PCS, NOESY) for each spin, returning the identification information.
1661
1662 @return: A list of the spin ID string, the data type ('rdc', 'pcs', 'noesy'), and the alignment ID if required.
1663 @rtype: list of str
1664 """
1665
1666
1667 for spin, spin_id in spin_loop(return_id=True):
1668
1669 base_ids = [spin_id, None, None]
1670
1671
1672 if not spin.select:
1673 continue
1674
1675
1676 if hasattr(spin, 'rdc'):
1677 base_ids[1] = 'rdc'
1678
1679
1680 for id in cdp.rdc_ids:
1681
1682 base_ids[2] = id
1683
1684
1685 yield base_ids
1686
1687
1688 if hasattr(spin, 'pcs'):
1689 base_ids[1] = 'pcs'
1690
1691
1692 for id in cdp.pcs_ids:
1693
1694 base_ids[2] = id
1695
1696
1697 yield base_ids
1698
1699
1700 if hasattr(spin, 'noesy'):
1701 base_ids[1] = 'noesy'
1702
1703
1704 for id in cdp.noesy_ids:
1705
1706 base_ids[2] = id
1707
1708
1709 yield base_ids
1710
1711
1712 - def calculate(self, spin_id=None, verbosity=1, sim_index=None):
1713 """Calculation function.
1714
1715 Currently this function simply calculates the NOESY flat-bottom quadratic energy potential,
1716 if NOE restraints are available.
1717
1718 @keyword spin_id: The spin identification string (unused).
1719 @type spin_id: None or str
1720 @keyword verbosity: The amount of information to print. The higher the value, the greater the verbosity.
1721 @type verbosity: int
1722 @keyword sim_index: The MC simulation index (unused).
1723 @type sim_index: None
1724 """
1725
1726
1727 model, param_vector, data_types, scaling_matrix = self._target_fn_setup()
1728
1729
1730 if model:
1731
1732 chi2 = model.func(param_vector)
1733
1734
1735 cdp.chi2 = chi2
1736
1737
1738 self._minimise_bc_data(model)
1739
1740
1741 if 'rdc' in data_types:
1742 rdc.q_factors()
1743
1744
1745 if 'pcs' in data_types:
1746 pcs.q_factors()
1747
1748
1749 if hasattr(cdp, 'noe_restraints'):
1750
1751 num_restraints = len(cdp.noe_restraints)
1752 dist = zeros(num_restraints, float64)
1753 pot = zeros(num_restraints, float64)
1754 lower = zeros(num_restraints, float64)
1755 upper = zeros(num_restraints, float64)
1756
1757
1758 for i in range(num_restraints):
1759
1760 lower[i] = cdp.noe_restraints[i][2]
1761 upper[i] = cdp.noe_restraints[i][3]
1762
1763
1764 dist[i] = self._calc_ave_dist(cdp.noe_restraints[i][0], cdp.noe_restraints[i][1], exp=-6)
1765
1766
1767 quad_pot(dist, pot, lower, upper)
1768
1769
1770 cdp.ave_dist = []
1771 cdp.quad_pot = []
1772 for i in range(num_restraints):
1773 cdp.ave_dist.append([cdp.noe_restraints[i][0], cdp.noe_restraints[i][1], dist[i]])
1774 cdp.quad_pot.append([cdp.noe_restraints[i][0], cdp.noe_restraints[i][1], pot[i]])
1775
1776
1778 """Create the Monte Carlo data by back-calculation.
1779
1780 @keyword data_id: The list of spin ID, data type, and alignment ID, as yielded by the base_data_loop() generator method.
1781 @type data_id: str
1782 @return: The Monte Carlo Ri data.
1783 @rtype: list of floats
1784 """
1785
1786
1787 mc_data = []
1788
1789
1790 spin = return_spin(data_id[0])
1791
1792
1793 if data_id[1] == 'rdc' and hasattr(spin, 'rdc'):
1794
1795 if not hasattr(spin, 'rdc_bc'):
1796 self.calculate()
1797
1798
1799 if not hasattr(spin, 'rdc_bc') or not spin.rdc_bc.has_key(data_id[2]):
1800 data = None
1801 else:
1802 data = spin.rdc_bc[data_id[2]]
1803
1804
1805 mc_data.append(data)
1806
1807
1808 elif data_id[1] == 'pcs' and hasattr(spin, 'pcs'):
1809
1810 if not hasattr(spin, 'pcs_bc'):
1811 self.calculate()
1812
1813
1814 if not hasattr(spin, 'pcs_bc') or not spin.pcs_bc.has_key(data_id[2]):
1815 data = None
1816 else:
1817 data = spin.pcs_bc[data_id[2]]
1818
1819
1820 mc_data.append(data)
1821
1822
1823 elif data_id[1] == 'noesy' and hasattr(spin, 'noesy'):
1824
1825 if not hasattr(spin, 'noesy_bc'):
1826 self.calculate()
1827
1828
1829 mc_data.append(spin.noesy_bc)
1830
1831
1832 return mc_data
1833
1834
1835 default_value_doc = Desc_container("N-state model default values")
1836 _table = uf_tables.add_table(label="table: N-state default values", caption="N-state model default values.")
1837 _table.add_headings(["Data type", "Object name", "Value"])
1838 _table.add_row(["Probabilities", "'p0', 'p1', 'p2', ..., 'pN'", "1/N"])
1839 _table.add_row(["Euler angle alpha", "'alpha0', 'alpha1', ...", "(c+1) * pi / (N+1)"])
1840 _table.add_row(["Euler angle beta", "'beta0', 'beta1', ...", "(c+1) * pi / (N+1)"])
1841 _table.add_row(["Euler angle gamma", "'gamma0', 'gamma1', ...", "(c+1) * pi / (N+1)"])
1842 default_value_doc.add_table(_table.label)
1843 default_value_doc.add_paragraph("In this table, N is the total number of states and c is the index of a given state ranging from 0 to N-1. The default probabilities are all set to be equal whereas the angles are given a range of values so that no 2 states are equal at the start of optimisation.")
1844 default_value_doc.add_paragraph("Note that setting the probability for state N will do nothing as it is equal to one minus all the other probabilities.")
1845
1847 """The default N-state model parameter values.
1848
1849 @param param: The N-state model parameter.
1850 @type param: str
1851 @return: The default value.
1852 @rtype: float
1853 """
1854
1855
1856 name = self.return_data_name(param)
1857 index = self._param_model_index(param)
1858
1859
1860 N = float(cdp.N)
1861
1862
1863 if name == 'probs':
1864 return 1.0 / N
1865
1866
1867 elif name == 'alpha' or name == 'beta' or name == 'gamma':
1868 return (float(index)+1) * pi / (N+1.0)
1869
1870
1871 - def grid_search(self, lower=None, upper=None, inc=None, constraints=False, verbosity=0, sim_index=None):
1872 """The grid search function.
1873
1874 @param lower: The lower bounds of the grid search which must be equal to the number of
1875 parameters in the model.
1876 @type lower: array of numbers
1877 @param upper: The upper bounds of the grid search which must be equal to the number of
1878 parameters in the model.
1879 @type upper: array of numbers
1880 @param inc: The increments for each dimension of the space for the grid search. The
1881 number of elements in the array must equal to the number of parameters
1882 in the model.
1883 @type inc: array of int
1884 @param constraints: If True, constraints are applied during the grid search (elinating parts
1885 of the grid). If False, no constraints are used.
1886 @type constraints: bool
1887 @param verbosity: A flag specifying the amount of information to print. The higher the
1888 value, the greater the verbosity.
1889 @type verbosity: int
1890 """
1891
1892
1893 if not hasattr(cdp, 'model'):
1894 raise RelaxNoModelError('N-state')
1895
1896
1897 n = self._param_num()
1898
1899
1900 if n == 0:
1901 print("Cannot run a grid search on a model with zero parameters, skipping the grid search.")
1902 return
1903
1904
1905 self.test_grid_ops(lower=lower, upper=upper, inc=inc, n=n)
1906
1907
1908 if isinstance(inc, int):
1909 inc = [inc]*n
1910
1911
1912 if not lower:
1913
1914 lower = []
1915 upper = []
1916
1917
1918 for i in range(n):
1919
1920 if i < len(cdp.params):
1921
1922 if search('^p', cdp.params[i]):
1923 lower.append(0.0)
1924 upper.append(1.0)
1925
1926
1927 if search('^alpha', cdp.params[i]) or search('^gamma', cdp.params[i]):
1928 lower.append(0.0)
1929 upper.append(2*pi)
1930 elif search('^beta', cdp.params[i]):
1931 lower.append(0.0)
1932 upper.append(pi)
1933
1934
1935 elif hasattr(cdp, 'paramag_centre_fixed') and not cdp.paramag_centre_fixed and (n - i) <= 3:
1936 lower.append(-100)
1937 upper.append(100)
1938
1939
1940 else:
1941 lower.append(-1e-3)
1942 upper.append(1e-3)
1943
1944
1945 self.minimise(min_algor='grid', lower=lower, upper=upper, inc=inc, constraints=constraints, verbosity=verbosity, sim_index=sim_index)
1946
1947
1949 """Determine whether the given parameter is spin specific.
1950
1951 @param name: The name of the parameter.
1952 @type name: str
1953 @return: False
1954 @rtype: bool
1955 """
1956
1957
1958 if name in ['r', 'heteronuc_type', 'proton_type']:
1959 return True
1960
1961
1962 return False
1963
1964
1966 """Create bounds for the OpenDX mapping function.
1967
1968 @param param: The name of the parameter to return the lower and upper bounds of.
1969 @type param: str
1970 @param spin_id: The spin identification string (unused).
1971 @type spin_id: None
1972 @return: The upper and lower bounds of the parameter.
1973 @rtype: list of float
1974 """
1975
1976
1977 if search('^paramag_[xyz]$', param):
1978 return [-100.0, 100.0]
1979
1980
1981 - def minimise(self, min_algor=None, min_options=None, func_tol=None, grad_tol=None, max_iterations=None, constraints=False, scaling=True, verbosity=0, sim_index=None, lower=None, upper=None, inc=None):
1982 """Minimisation function.
1983
1984 @param min_algor: The minimisation algorithm to use.
1985 @type min_algor: str
1986 @param min_options: An array of options to be used by the minimisation algorithm.
1987 @type min_options: array of str
1988 @param func_tol: The function tolerance which, when reached, terminates optimisation. Setting this to None turns of the check.
1989 @type func_tol: None or float
1990 @param grad_tol: The gradient tolerance which, when reached, terminates optimisation. Setting this to None turns of the check.
1991 @type grad_tol: None or float
1992 @param max_iterations: The maximum number of iterations for the algorithm.
1993 @type max_iterations: int
1994 @param constraints: If True, constraints are used during optimisation.
1995 @type constraints: bool
1996 @param scaling: If True, diagonal scaling is enabled during optimisation to allow the problem to be better conditioned.
1997 @type scaling: bool
1998 @param verbosity: A flag specifying the amount of information to print. The higher the value, the greater the verbosity.
1999 @type verbosity: int
2000 @param sim_index: The index of the simulation to optimise. This should be None if normal optimisation is desired.
2001 @type sim_index: None or int
2002 @keyword lower: The lower bounds of the grid search which must be equal to the number of parameters in the model. This optional argument is only used when doing a grid search.
2003 @type lower: array of numbers
2004 @keyword upper: The upper bounds of the grid search which must be equal to the number of parameters in the model. This optional argument is only used when doing a grid search.
2005 @type upper: array of numbers
2006 @keyword inc: The increments for each dimension of the space for the grid search. The number of elements in the array must equal to the number of parameters in the model. This argument is only used when doing a grid search.
2007 @type inc: array of int
2008 """
2009
2010
2011 model, param_vector, data_types, scaling_matrix = self._target_fn_setup(sim_index=sim_index, scaling=scaling)
2012
2013
2014 if not len(param_vector):
2015 warn(RelaxWarning("The model has no parameters, minimisation cannot be performed."))
2016 return
2017
2018
2019 if constraints and cdp.model == 'fixed':
2020 warn(RelaxWarning("Turning constraints off. These cannot be used for the 'fixed' model."))
2021 constraints = False
2022
2023
2024 if min_algor == 'Method of Multipliers':
2025 min_algor = min_options[0]
2026 min_options = min_options[1:]
2027
2028
2029 if not constraints and cdp.model == 'population':
2030 warn(RelaxWarning("Turning constraints on. These absolutely must be used for the 'population' model."))
2031 constraints = True
2032
2033
2034 min_options = (min_algor,) + min_options
2035 min_algor = 'Method of Multipliers'
2036
2037
2038 if constraints:
2039 A, b = self._linear_constraints(data_types=data_types, scaling_matrix=scaling_matrix)
2040 else:
2041 A, b = None, None
2042
2043
2044 if search('^[Gg]rid', min_algor):
2045
2046 if scaling:
2047 for i in xrange(len(param_vector)):
2048 lower[i] = lower[i] / scaling_matrix[i, i]
2049 upper[i] = upper[i] / scaling_matrix[i, i]
2050
2051
2052 results = grid(func=model.func, args=(), num_incs=inc, lower=lower, upper=upper, A=A, b=b, verbosity=verbosity)
2053
2054
2055 param_vector, func, iter_count, warning = results
2056 f_count = iter_count
2057 g_count = 0.0
2058 h_count = 0.0
2059
2060
2061 else:
2062 results = generic_minimise(func=model.func, dfunc=model.dfunc, d2func=model.d2func, args=(), x0=param_vector, min_algor=min_algor, min_options=min_options, func_tol=func_tol, grad_tol=grad_tol, maxiter=max_iterations, A=A, b=b, full_output=1, print_flag=verbosity)
2063
2064
2065 if results == None:
2066 return
2067 param_vector, func, iter_count, f_count, g_count, h_count, warning = results
2068
2069
2070 if isInf(func):
2071 raise RelaxInfError('chi-squared')
2072
2073
2074 if isNaN(func):
2075 raise RelaxNaNError('chi-squared')
2076
2077
2078 chi2 = model.func(param_vector)
2079
2080
2081 if scaling:
2082 param_vector = dot(scaling_matrix, param_vector)
2083
2084
2085 self._disassemble_param_vector(param_vector=param_vector, data_types=data_types, sim_index=sim_index)
2086
2087
2088 if sim_index != None:
2089
2090 cdp.chi2_sim[sim_index] = func
2091
2092
2093 cdp.iter_sim[sim_index] = iter_count
2094
2095
2096 cdp.f_count_sim[sim_index] = f_count
2097
2098
2099 cdp.g_count_sim[sim_index] = g_count
2100
2101
2102 cdp.h_count_sim[sim_index] = h_count
2103
2104
2105 cdp.warning_sim[sim_index] = warning
2106
2107
2108 else:
2109
2110 cdp.chi2 = func
2111
2112
2113 cdp.iter = iter_count
2114
2115
2116 cdp.f_count = f_count
2117
2118
2119 cdp.g_count = g_count
2120
2121
2122 cdp.h_count = h_count
2123
2124
2125 cdp.warning = warning
2126
2127
2128 if sim_index == None and ('rdc' in data_types or 'pcs' in data_types):
2129
2130 self._minimise_bc_data(model)
2131
2132
2133 if 'rdc' in data_types:
2134 rdc.q_factors()
2135
2136
2137 if 'pcs' in data_types:
2138 pcs.q_factors()
2139
2140
2142 """Return the k, n, and chi2 model statistics.
2143
2144 k - number of parameters.
2145 n - number of data points.
2146 chi2 - the chi-squared value.
2147
2148
2149 @keyword model_info: The data returned from model_loop() (unused).
2150 @type model_info: None
2151 @keyword spin_id: The spin identification string. This is ignored in the N-state model.
2152 @type spin_id: None or str
2153 @keyword global_stats: A parameter which determines if global or local statistics are returned. For the N-state model, this argument is ignored.
2154 @type global_stats: None or bool
2155 @return: The optimisation statistics, in tuple format, of the number of parameters (k), the number of data points (n), and the chi-squared value (chi2).
2156 @rtype: tuple of (int, int, float)
2157 """
2158
2159
2160 return self._param_num(), self._num_data_points(), cdp.chi2
2161
2162
2163 return_data_name_doc = Desc_container("N-state model data type string matching patterns")
2164 _table = uf_tables.add_table(label="table: N-state data type patterns", caption="N-state model data type string matching patterns.")
2165 _table.add_headings(["Data type", "Object name", "Patterns"])
2166 _table.add_row(["Probabilities", "'probs'", "'p0', 'p1', 'p2', ..., 'pN'"])
2167 _table.add_row(["Euler angle alpha", "'alpha'", "'alpha0', 'alpha1', ..."])
2168 _table.add_row(["Euler angle beta", "'beta'", "'beta0', 'beta1', ..."])
2169 _table.add_row(["Euler angle gamma", "'gamma'", "'gamma0', 'gamma1', ..."])
2170 _table.add_row(["Bond length", "'r'", "'^r$' or '[Bb]ond[ -_][Ll]ength'"])
2171 _table.add_row(["Heteronucleus type", "'heteronuc_type'", "'^[Hh]eteronucleus$'"])
2172 _table.add_row(["Proton type", "'proton_type'", "'^[Pp]roton$'"])
2173 return_data_name_doc.add_table(_table.label)
2174 return_data_name_doc.add_paragraph("The objects corresponding to the object names are lists (or arrays) with each element corrsponding to each state.")
2175
2177 """Return a unique identifying string for the N-state model parameter.
2178
2179 @param param: The N-state model parameter.
2180 @type param: str
2181 @return: The unique parameter identifying string.
2182 @rtype: str
2183 """
2184
2185
2186 if search('^p[0-9]*$', param):
2187 return 'probs'
2188
2189
2190 if search('^alpha', param):
2191 return 'alpha'
2192
2193
2194 if search('^beta', param):
2195 return 'beta'
2196
2197
2198 if search('^gamma', param):
2199 return 'gamma'
2200
2201
2202 if search('^r$', param) or search('[Bb]ond[ -_][Ll]ength', param):
2203 return 'r'
2204
2205
2206 if param == 'heteronuc_type':
2207 return 'heteronuc_type'
2208
2209
2210 if param == 'proton_type':
2211 return 'proton_type'
2212
2213
2214 if search('^paramag_[xyz]$', param):
2215 return param
2216
2217
2219 """Create and return the spin specific Monte Carlo Ri error structure.
2220
2221 @keyword data_id: The list of spin ID, data type, and alignment ID, as yielded by the base_data_loop() generator method.
2222 @type data_id: str
2223 @return: The Monte Carlo simulation data errors.
2224 @rtype: list of floats
2225 """
2226
2227
2228 mc_errors = []
2229
2230
2231 spin = return_spin(data_id[0])
2232
2233
2234 if not spin.select:
2235 return
2236
2237
2238 if data_id[1] == 'rdc' and hasattr(spin, 'rdc'):
2239
2240 if not hasattr(spin, 'rdc_err'):
2241 raise RelaxError("The RDC errors are missing for spin '%s'." % data_id[0])
2242
2243
2244 mc_errors.append(spin.rdc_err[data_id[2]])
2245
2246
2247 elif data_id[1] == 'pcs' and hasattr(spin, 'pcs'):
2248
2249 if not hasattr(spin, 'pcs_err'):
2250 raise RelaxError("The PCS errors are missing for spin '%s'." % data_id[0])
2251
2252
2253 mc_errors.append(spin.pcs_err[data_id[2]])
2254
2255
2256 elif hasattr(spin, 'noesy'):
2257
2258 if not hasattr(spin, 'noesy_err'):
2259 raise RelaxError("The NOESY errors are missing for spin '%s'." % data_id[0])
2260
2261
2262 mc_errors.append(spin.noesy_err)
2263
2264
2265 return mc_errors
2266
2267
2269 """Return the Grace string representation of the parameter.
2270
2271 This is used for axis labelling.
2272
2273 @param param: The specific analysis parameter.
2274 @type param: str
2275 @return: The Grace string representation of the parameter.
2276 @rtype: str
2277 """
2278
2279
2280 if param == 'pcs':
2281 return "Measured PCS"
2282
2283
2284 if param == 'pcs_bc':
2285 return "Back-calculated PCS"
2286
2287
2288 if param == 'rdc':
2289 return "Measured RDC"
2290
2291
2292 if param == 'rdc_bc':
2293 return "Back-calculated RDC"
2294
2295
2297 """Return a string representing the parameters units.
2298
2299 @param param: The name of the parameter to return the units string for.
2300 @type param: str
2301 @return: The parameter units string.
2302 @rtype: str
2303 """
2304
2305
2306 if param == 'pcs' or param == 'pcs_bc':
2307 return 'ppm'
2308
2309
2310 if param == 'rdc' or param == 'rdc_bc':
2311 return 'Hz'
2312
2313
2314 set_doc = Desc_container("N-state model set details")
2315 set_doc.add_paragraph("Setting parameters for the N-state model is a little different from the other type of analyses as each state has a set of parameters with the same names as the other states. To set the parameters for a specific state c (ranging from 0 for the first to N-1 for the last, the number c should be added to the end of the parameter name. So the Euler angle gamma of the third state is specified using the string 'gamma2'.")
2316
2317
2318 - def set_error(self, model_info, index, error):
2319 """Set the parameter errors.
2320
2321 @param model_info: The global model index originating from model_loop().
2322 @type model_info: int
2323 @param index: The index of the parameter to set the errors for.
2324 @type index: int
2325 @param error: The error value.
2326 @type error: float
2327 """
2328
2329
2330 names = ['Axx', 'Ayy', 'Axy', 'Axz', 'Ayz']
2331
2332
2333 if index < len(cdp.align_ids)*5:
2334
2335 param_index = index % 5
2336 tensor_index = (index - index % 5) / 5
2337
2338
2339 tensor = return_tensor(index=tensor_index, skip_fixed=True)
2340 return setattr(tensor, names[param_index]+'_err', error)
2341
2342
2344 """Set the N-state model parameter values.
2345
2346 @keyword param: The parameter name list.
2347 @type param: list of str
2348 @keyword value: The parameter value list.
2349 @type value: list
2350 @keyword spin_id: The spin identification string (unused).
2351 @type spin_id: None
2352 @keyword force: A flag which if True will cause current values to be overwritten. If False, a RelaxError will raised if the parameter value is already set.
2353 @type force: bool
2354 """
2355
2356
2357 arg_check.is_str_list(param, 'parameter name')
2358 arg_check.is_list(value, 'parameter value')
2359
2360
2361 for i in range(len(param)):
2362
2363 obj_name = self.return_data_name(param[i])
2364
2365
2366 if not obj_name:
2367 raise RelaxError("The parameter '%s' is not valid for this data pipe type." % param[i])
2368
2369
2370 if obj_name in ['probs', 'alpha', 'beta', 'gamma']:
2371
2372 index = self._param_model_index(param[i])
2373
2374
2375 obj = getattr(cdp, obj_name)
2376 obj[index] = value[i]
2377
2378
2379 if search('^paramag_[xyz]$', obj_name):
2380
2381 if not hasattr(cdp, 'paramagnetic_centre'):
2382 cdp.paramagnetic_centre = zeros(3, float64)
2383
2384
2385 if obj_name == 'paramag_x':
2386 index = 0
2387 elif obj_name == 'paramag_y':
2388 index = 1
2389 else:
2390 index = 2
2391
2392
2393 cdp.paramagnetic_centre[index] = value[i]
2394
2395
2396 else:
2397 for spin in spin_loop(spin_id):
2398 setattr(spin, obj_name, value[i])
2399
2400
2402 """Initialise the Monte Carlo parameter values."""
2403
2404
2405 min_names = self.data_names(set='min')
2406
2407
2408 if hasattr(cdp, 'align_tensors'):
2409
2410 names = ['Axx', 'Ayy', 'Axy', 'Axz', 'Ayz']
2411
2412
2413 for i in xrange(len(cdp.align_tensors)):
2414
2415 if cdp.align_tensors[i].fixed:
2416 continue
2417
2418
2419 for object_name in names:
2420
2421 sim_object_name = object_name + '_sim'
2422
2423
2424 setattr(cdp.align_tensors[i], sim_object_name, [])
2425
2426
2427 sim_object = getattr(cdp.align_tensors[i], sim_object_name)
2428
2429
2430 for j in xrange(cdp.sim_number):
2431 sim_object.append(getattr(cdp.align_tensors[i], object_name))
2432
2433
2434 for object_name in min_names:
2435
2436 sim_object_name = object_name + '_sim'
2437
2438
2439 setattr(cdp, sim_object_name, [])
2440
2441
2442 sim_object = getattr(cdp, sim_object_name)
2443
2444
2445 for j in xrange(cdp.sim_number):
2446
2447 sim_object.append(None)
2448
2449
2451 """Pack the Monte Carlo simulation data.
2452
2453 @keyword data_id: The list of spin ID, data type, and alignment ID, as yielded by the base_data_loop() generator method.
2454 @type data_id: list of str
2455 @param sim_data: The Monte Carlo simulation data.
2456 @type sim_data: list of float
2457 """
2458
2459
2460 spin = return_spin(data_id[0])
2461
2462
2463 if data_id[1] == 'rdc' and hasattr(spin, 'rdc'):
2464
2465 if not hasattr(spin, 'rdc_sim'):
2466 spin.rdc_sim = {}
2467
2468
2469 spin.rdc_sim[data_id[2]] = []
2470 for i in range(cdp.sim_number):
2471 spin.rdc_sim[data_id[2]].append(sim_data[i][0])
2472
2473
2474 if data_id[1] == 'pcs' and hasattr(spin, 'pcs'):
2475
2476 if not hasattr(spin, 'pcs_sim'):
2477 spin.pcs_sim = {}
2478
2479
2480 spin.pcs_sim[data_id[2]] = []
2481 for i in range(cdp.sim_number):
2482 spin.pcs_sim[data_id[2]].append(sim_data[i][0])
2483
2484
2485 if data_id[1] == 'noesy' and hasattr(spin, 'noesy'):
2486
2487 spin.noesy_sim = []
2488 for i in range(cdp.sim_number):
2489 spin.noesy_sim[data_id[2]].append(sim_data[i][0])
2490
2491
2493 """Return the array of simulation parameter values.
2494
2495 @param model_info: The global model index originating from model_loop().
2496 @type model_info: int
2497 @param index: The index of the parameter to return the array of values for.
2498 @type index: int
2499 @return: The array of simulation parameter values.
2500 @rtype: list of float
2501 """
2502
2503
2504 names = ['Axx', 'Ayy', 'Axy', 'Axz', 'Ayz']
2505
2506
2507 if index < num_tensors(skip_fixed=True)*5:
2508
2509 param_index = index % 5
2510 tensor_index = (index - index % 5) / 5
2511
2512
2513 tensor = return_tensor(index=tensor_index, skip_fixed=True)
2514 return getattr(tensor, names[param_index]+'_sim')
2515