1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """The parameter list object base class for the specific analyses.
24
25 This provides a uniform interface for defining and handling parameters - either optimised or fixed - of the specific analyses.
26 """
27
28
29 from math import pi
30 from re import search
31 from types import FunctionType, MethodType
32
33
34 from lib.errors import RelaxError
35 from user_functions.data import Uf_tables; uf_tables = Uf_tables()
36 from user_functions.objects import Desc_container
37
38
40 """A special object for handling global and spin parameters."""
41
43 """Set up the class.
44
45 @keyword spin_data: A flag which if True indicates that the specific analysis operates with spins.
46 @type spin_data: bool
47 """
48
49
50 self.spin_data = spin_data
51
52
53 self._names = []
54 self._scope = {}
55 self._string = {}
56 self._defaults = {}
57 self._units = {}
58 self._desc = {}
59 self._py_types = {}
60 self._conv_factor = {}
61 self._grace_string = {}
62 self._set = {}
63 self._err = {}
64 self._sim = {}
65
66
67 if self.spin_data:
68 self._add('select', scope='spin', desc='The spin selection flag', py_type=bool, sim=True)
69 self._add('fixed', scope='spin', desc='The fixed flag', py_type=bool)
70
71
72 self._uf_title = "Parameters"
73 self._uf_table_caption = "Parameters"
74 self._uf_docs = {}
75
76
77 self._initialised = True
78
79
93
94
95 - def _add(self, name, scope=None, string=None, default=None, units=None, desc=None, py_type=None, set='all', conv_factor=None, grace_string=None, err=False, sim=False):
96 """Add a parameter to the list.
97
98 @param name: The name of the parameter. This will be used as the variable name.
99 @type name: str
100 @keyword scope: The parameter scope. This can be set to 'global' for parameters located within the global scope of the current data pipe. Or set to 'spin' for spin specific parameters. Alternatively the value 'both' indicates that there are both global and specific versions of this parameter.
101 @type scope: str
102 @keyword string: The string representation of the parameter.
103 @type string: None or str
104 @keyword default: The default value of the parameter.
105 @type default: anything
106 @keyword units: A string representing the parameters units.
107 @type units: None or str
108 @keyword desc: The text description of the parameter.
109 @type desc: None or str
110 @keyword py_type: The Python type that this parameter should be.
111 @type py_type: Python type object
112 @keyword set: The set of object names. This can be set to 'all' for all names, to 'fixed' for parameter of the model which are permanently fixed, to 'params' for parameter of the model which are optimised or calculated, or to 'min' for minimisation specific object names.
113 @type set: str
114 @keyword conv_factor: The factor of conversion between different parameter units.
115 @type conv_factor: None, float or func
116 @keyword grace_string: The string used for the axes in Grace plots of the data.
117 @type grace_string: None or str
118 @keyword err: A flag which if True indicates that the parameter name + '_err' error data structure can exist.
119 @type err: bool
120 @keyword sim: A flag which if True indicates that the parameter name + '_sim' Monte Carlo simulation data structure can exist.
121 @type sim: bool
122 """
123
124
125 if scope == None:
126 raise RelaxError("The parameter scope must be set.")
127 if py_type == None:
128 raise RelaxError("The parameter type must be set.")
129 allowed_sets = ['all', 'fixed', 'params', 'min']
130 if set not in allowed_sets:
131 raise RelaxError("The parameter set '%s' must be one of %s." % (set, allowed_sets))
132
133
134 self._names.append(name)
135 self._scope[name] = scope
136 self._defaults[name] = default
137 self._units[name] = units
138 self._desc[name] = desc
139 self._py_types[name] = py_type
140 self._set[name] = set
141 self._conv_factor[name] = conv_factor
142 self._err[name] = err
143 self._sim[name] = sim
144
145
146 if string:
147 self._string[name] = string
148 else:
149 self._string[name] = name
150
151
152 if grace_string:
153 self._grace_string[name] = grace_string
154 else:
155 self._grace_string[name] = name
156
157
159 """Add the PCS and RDC data."""
160
161
162 self._add('pcs', scope='spin', grace_string='Pseudo-contact shift', units='ppm', desc='The pseudo-contact shift (PCS)', py_type=float)
163 self._add('rdc', scope='spin', grace_string='Residual dipolar coupling', units='Hz', desc='The residual dipolar coupling (RDC)', py_type=float)
164
165
166 - def _add_csa(self, default=None, set='fixed', err=False, sim=False):
167 """Add the CSA parameter 'csa'.
168
169 @keyword default: The default CSA value.
170 @type default: float
171 @keyword set: The set of object names. This can be set to 'all' for all names, to 'fixed' for parameter of the model which are permanently fixed, to 'params' for parameter of the model which are optimised or calculated, or to 'min' for minimisation specific object names.
172 @type set: str
173 @keyword err: A flag which if True indicates that the 'csa_err' error data structure can exist.
174 @type err: bool
175 @keyword sim: A flag which if True indicates that the 'csa_sim' Monte Carlo simulation data structure can exist.
176 @type sim: bool
177 """
178
179
180 self._add('csa', scope='spin', default=default, units='ppm', desc='Chemical shift anisotropy (unitless)', py_type=float, set=set, conv_factor=1e-6, grace_string='\\qCSA\\Q', err=err, sim=sim)
181
182
184 """Add the Brownian rotational diffusion parameters to the list."""
185
186
187 self._add('tm', scope='global', default=10.0 * 1e-9, grace_string='\\xt\\f{}\\sm', units='ns', desc='Global correlation time', py_type=float, set='params', conv_factor=1e-9, err=True, sim=True)
188 self._add('Diso', scope='global', default=1.666 * 1e7, units='1e6 1/s', desc='Isotropic component of the diffusion tensor', py_type=float, set='params', conv_factor=1e6, err=True, sim=True)
189 self._add('Dx', scope='global', default=1.666 * 1e7, units='1e6 1/s', desc='Eigenvalue associated with the x-axis of the diffusion tensor', py_type=float, set='params', conv_factor=1e6, err=True, sim=True)
190 self._add('Dy', scope='global', default=1.666 * 1e7, units='1e6 1/s', desc='Eigenvalue associated with the y-axis of the diffusion tensor', py_type=float, set='params', conv_factor=1e6, err=True, sim=True)
191 self._add('Dz', scope='global', default=1.666 * 1e7, units='1e6 1/s', desc='Eigenvalue associated with the z-axis of the diffusion tensor', py_type=float, set='params', conv_factor=1e6, err=True, sim=True)
192 self._add('Dpar', scope='global', default=1.666 * 1e7, units='1e6 1/s', desc='Diffusion coefficient parallel to the major axis of the spheroid diffusion tensor', py_type=float, set='params', conv_factor=1e6, err=True, sim=True)
193 self._add('Dper', scope='global', default=1.666 * 1e7, units='1e6 1/s', desc='Diffusion coefficient perpendicular to the major axis of the spheroid diffusion tensor', py_type=float, set='params', conv_factor=1e6, err=True, sim=True)
194 self._add('Da', scope='global', default=0.0, units='1e6 1/s', desc='Anisotropic component of the diffusion tensor', py_type=float, set='params', conv_factor=1e6, err=True, sim=True)
195 self._add('Dr', scope='global', default=0.0, desc='Rhombic component of the diffusion tensor', py_type=float, set='params', err=True, sim=True)
196 self._add('Dratio', scope='global', default=1.0, desc='Ratio of the parallel and perpendicular components of the spheroid diffusion tensor', py_type=float, set='params', err=True, sim=True)
197 self._add('alpha', scope='global', default=0.0, units='deg', desc='The first Euler angle of the ellipsoid diffusion tensor', py_type=float, set='params', conv_factor=(2.0*pi) / 360.0, err=True, sim=True)
198 self._add('beta', scope='global', default=0.0, units='deg', desc='The second Euler angle of the ellipsoid diffusion tensor', py_type=float, set='params', conv_factor=(2.0*pi) / 360.0, err=True, sim=True)
199 self._add('gamma', scope='global', default=0.0, units='deg', desc='The third Euler angle of the ellipsoid diffusion tensor', py_type=float, set='params', conv_factor=(2.0*pi) / 360.0, err=True, sim=True)
200 self._add('theta', scope='global', default=0.0, units='deg', desc='The polar angle defining the major axis of the spheroid diffusion tensor', py_type=float, set='params', conv_factor=(2.0*pi) / 360.0, err=True, sim=True)
201 self._add('phi', scope='global', default=0.0, units='deg', desc='The azimuthal angle defining the major axis of the spheroid diffusion tensor', py_type=float, set='params', conv_factor=(2.0*pi) / 360.0, err=True, sim=True)
202
203
204 - def _add_min_data(self, min_stats_global=False, min_stats_spin=False):
205 """Add minimisation specific objects.
206
207 The parameter scope is defined by the keyword arguments.
208
209
210 @keyword min_stats_global: A flag which if True will cause the parameters to be global.
211 @type min_stats_global: bool
212 @keyword min_stats_spin: A flag which if True will cause the parameters to be spin specific.
213 @type min_stats_spin: bool
214 """
215
216
217 self.min_stats_global = min_stats_global
218 self.min_stats_spin = min_stats_spin
219
220
221 if self.min_stats_global or self.min_stats_spin:
222
223 if self.min_stats_global and self.min_stats_spin:
224 scope = 'both'
225 elif self.min_stats_global:
226 scope = 'global'
227 else:
228 scope = 'spin'
229
230
231 self._add('chi2', scope=scope, desc='Chi-squared value', py_type=float, set='min', grace_string='\\xc\\S2', err=False, sim=True)
232 self._add('iter', scope=scope, desc='Optimisation iterations', py_type=int, set='min', grace_string='Iteration count', err=False, sim=True)
233 self._add('f_count', scope=scope, desc='Number of function calls', py_type=int, set='min', grace_string='Function call count', err=False, sim=True)
234 self._add('g_count', scope=scope, desc='Number of gradient calls', py_type=int, set='min', grace_string='Gradient call count', err=False, sim=True)
235 self._add('h_count', scope=scope, desc='Number of Hessian calls', py_type=int, set='min', grace_string='Hessian call count', err=False, sim=True)
236 self._add('warning', scope=scope, desc='Optimisation warning', py_type=str, set='min', err=False, sim=True)
237
238
239 - def _add_model_info(self, scope='spin', model_flag=True, equation_flag=False):
240 """Add model specific objects 'model' and 'params'.
241
242 @keyword scope: The parameter scope. This can be set to 'global' for parameters located within the global scope of the current data pipe. Or set to 'spin' for spin specific parameters. Alternatively the value 'both' indicates that there are both global and specific versions of this parameter.
243 @type scope: str
244 @keyword model_flag: A flag which if True will cause the 'model' structure to be added.
245 @type model_flag: bool
246 """
247
248
249 if model_flag:
250 self._add('model', scope=scope, desc='The model name', py_type=str)
251
252
253 if equation_flag:
254 self._add('equation', scope=scope, desc='The model equation', py_type=str)
255
256
257 self._add('params', scope=scope, desc='The parameters of the model', py_type=list)
258
259
261 """Add the peak intensity structure 'peak_intensity'."""
262
263
264 self._add('peak_intensity', scope='spin', desc='The peak intensities', py_type=dict, grace_string='\\qPeak intensities\\Q')
265
266
268 """Set the title for the user function documentation.
269
270 @param title: The title to use in the user function docstrings.
271 @type title: str
272 """
273
274
275 self._uf_title = title
276
277
278 - def _uf_param_table(self, label=None, caption=None, scope='spin', sets=['params', 'fixed'], default=False, units=False, type=False):
279 """"Create the parameter documentation for the user function docstrings.
280
281 @keyword label: The label of the table. This is used to identify replicated tables, and is also used in the table referencing in the LaTeX compilation of the user manual. If this label is already used, the corresponding pre-constructed documentation object will be returned.
282 @type label: str
283 @keyword caption: The caption for the table.
284 @type caption: str
285 @keyword scope: The parameter scope to restrict the table to, defaulting to 'spin'.
286 @type scope: str or None
287 @keyword sets: The parameter sets to restrict the table to. If not given, then all parameters of the 'params' and 'fixed' sets will be added. This can be set to 'all' for all names, to 'fixed' for parameter of the model which are permanently fixed, to 'params' for parameter of the model which are optimised or calculated, or to 'min' for minimisation specific object names.
288 @type sets: list of str
289 @keyword default: A flag which if True will cause the default parameter value to be included in the table.
290 @type default: bool
291 @keyword units: A flag which if True will cause the units to be included in the table.
292 @type units: bool
293 @keyword type: A flag which if True will cause the parameter type to be included in the table.
294 @type type: bool
295 @return: The parameter documentation.
296 @rtype: Desc_container instance
297 """
298
299
300 if label == None:
301 raise RelaxError("The table identifying label must be supplied.")
302 if label in self._uf_docs:
303 raise RelaxError("The table identifying label '%s' already exists." % label)
304
305
306 self._uf_docs[label] = Desc_container(self._uf_title)
307
308
309 table = uf_tables.add_table(label=label, caption=caption)
310
311
312 headings = ["Name", "Description"]
313 if default:
314 headings.append("Default")
315 if units:
316 headings.append("Units")
317 if type:
318 headings.append("Type")
319 table.add_headings(headings)
320
321
322 for set in sets:
323 for param in self.loop(set=set):
324
325 if scope and self.scope(param) != scope:
326 continue
327
328 row = []
329 row.append(param)
330 row.append(self.description(param))
331 if default:
332 row.append("%s" % self.default_value(param))
333 if units:
334 row.append("%s" % self.units(param))
335 if type:
336 row.append("%s" % self.type_string(param))
337 table.add_row(row)
338
339
340 self._uf_docs[label].add_table(table.label)
341
342
343 return self._uf_docs[label]
344
345
347 """Generator method for looping over and yielding the user function parameter documentation.
348
349 @keyword tables: The list of tables to loop over. If None, then all tables will be yielded.
350 @type tables: list of str or None
351 @return: The user function documentation for each table.
352 @rtype: Desc_container instance
353 """
354
355
356 if tables == None:
357 tables = self._uf_docs.keys()
358
359
360 for table in tables:
361 yield self._uf_docs[table]
362
363
365 """An iterator method for looping over all the base parameters.
366
367 @keyword set: The set of object names. This can be set to 'all' for all names, to 'fixed' for parameter of the model which are permanently fixed, to 'params' for parameter of the model which are optimised or calculated, or to 'min' for minimisation specific object names.
368 @type set: str
369 @keyword scope: The scope of the parameter to return. If not set, then all will be returned. If set to 'global' or 'spin', then only the parameters within that scope will be returned.
370 @type scope: str or None
371 @returns: The parameter names.
372 @rtype: str
373 """
374
375
376 for name in self._names:
377
378 if set == 'fixed' and self._set[name] != 'fixed':
379 continue
380 if set == 'params' and self._set[name] != 'params':
381 continue
382 if set == 'min' and self._set[name] != 'min':
383 continue
384
385
386 if scope == 'global' and self._scope[name] == 'spin':
387 continue
388 if scope == 'spin' and self._scope[name] == 'global':
389 continue
390
391
392 yield name
393
394
396 """Check if the parameter exists.
397
398 @param name: The name of the parameter to search for.
399 @type name: str
400 @raises RelaxError: If the parameter does not exist.
401 """
402
403
404 if name not in self._names:
405 raise RelaxError("The parameter '%s' does not exist." % name)
406
407
409 """Determine if the given name is within the parameter list.
410
411 @param name: The name of the parameter to search for.
412 @type name: str
413 @return: True if the parameter is within the list, False otherwise.
414 @rtype: bool
415 """
416
417
418 if name in self._names:
419 return True
420
421
422 return False
423
424
426 """Return the conversion factor.
427
428 @param name: The name of the parameter.
429 @type name: str
430 @return: The conversion factor.
431 @rtype: float
432 """
433
434
435 self.check_param(name)
436
437
438 if self._conv_factor[name] == None:
439 return 1.0
440
441
442 if isinstance(self._conv_factor[name], FunctionType) or isinstance(self._conv_factor[name], MethodType):
443 return self._conv_factor[name]()
444
445
446 return self._conv_factor[name]
447
448
449 - def data_names(self, set='all', scope=None, error_names=False, sim_names=False):
450 """Return a list of names of data structures.
451
452 @keyword set: The set of object names. This can be set to 'all' for all names, to 'fixed' for parameter of the model which are permanently fixed, to 'params' for parameter of the model which are optimised or calculated, or to 'min' for minimisation specific object names.
453 @type set: str
454 @keyword scope: The scope of the parameter to return. If not set, then all will be returned. If set to 'global' or 'spin', then only the parameters within that scope will be returned.
455 @type scope: str or None
456 @keyword error_names: A flag which if True will add the error object names as well.
457 @type error_names: bool
458 @keyword sim_names: A flag which if True will add the Monte Carlo simulation object names as well.
459 @type sim_names: bool
460 @return: The list of object names.
461 @rtype: list of str
462 """
463
464
465 names = []
466
467
468 for name in self.loop(set=set, scope=scope, error_names=error_names, sim_names=sim_names):
469 names.append(name)
470
471
472 return names
473
474
476 """Return the default value of the parameter.
477
478 @param name: The name of the parameter.
479 @type name: str
480 @return: The default value.
481 @rtype: None or str
482 """
483
484
485 self.check_param(name)
486
487
488 return self._defaults[name]
489
490
492 """Return the description of the parameter.
493
494 @param name: The name of the parameter.
495 @type name: str
496 @return: The description.
497 @rtype: None or str
498 """
499
500
501 if name not in ['ri_data_err'] and (search('_err$', name) or search('_sim$', name)):
502 return None
503
504
505 self.check_param(name)
506
507
508 return self._desc[name]
509
510
512 """Return the error flag for the parameter.
513
514 @param name: The name of the parameter.
515 @type name: str
516 @return: The error flag for the parameter.
517 @rtype: bool
518 """
519
520
521 self.check_param(name)
522
523
524 return self._err[name]
525
526
528 """Return the Grace string for the parameter.
529
530 @param name: The name of the parameter.
531 @type name: str
532 @return: The Grace string.
533 @rtype: str
534 """
535
536
537 self.check_param(name)
538
539
540 return self._grace_string[name]
541
542
544 """Determine whether the given parameter is spin specific.
545
546 @param name: The name of the parameter.
547 @type name: str
548 @return: True if the parameter is spin specific, False otherwise.
549 @rtype: bool
550 """
551
552
553 if self.scope(name) == 'spin':
554 return True
555 else:
556 return False
557
558
559 - def loop(self, set=None, scope=None, error_names=False, sim_names=False):
560 """An iterator method for looping over all the parameters.
561
562 @keyword set: The set of object names. This can be set to 'all' for all names, to 'fixed' for parameter of the model which are permanently fixed, to 'params' for parameter of the model which are optimised or calculated, or to 'min' for minimisation specific object names.
563 @type set: str
564 @keyword scope: The scope of the parameter to return. If not set, then all will be returned. If set to 'global' or 'spin', then only the parameters within that scope will be returned.
565 @type scope: str or None
566 @keyword error_names: A flag which if True will add the error object names as well.
567 @type error_names: bool
568 @keyword sim_names: A flag which if True will add the Monte Carlo simulation object names as well.
569 @type sim_names: bool
570 @returns: The parameter names.
571 @rtype: str
572 """
573
574
575 for name in self.base_loop(set=set, scope=scope):
576 yield name
577
578
579 if error_names:
580 for name in self.base_loop(set=set):
581 if self.error_flag(name):
582 yield name + '_err'
583
584
585 if sim_names:
586 for name in self.base_loop(set=set):
587 if self.simulation_flag(name):
588 yield name + '_sim'
589
590
592 """Return the parameter scope.
593
594 @param name: The name of the parameter.
595 @type name: str
596 @return: The scope. This is 'global' for parameters located within the global scope of the current data pipe. Or 'spin' for spin specific parameters. Alternatively the value 'both' indicates that there are both global and specific versions of this parameter.
597 @rtype: str
598 """
599
600
601 self.check_param(name)
602
603
604 return self._scope[name]
605
606
607 - def set(self, name):
608 """Return the parameter set that the parameter belongs to.
609
610 @param name: The name of the parameter.
611 @type name: str
612 @return: The parameter set.
613 @rtype: str
614 """
615
616
617 self.check_param(name)
618
619
620 return self._set[name]
621
622
624 """Return the Monte Carlo simulation flag for the parameter.
625
626 @param name: The name of the parameter.
627 @type name: str
628 @return: The Monte Carlo simulation flag for the parameter.
629 @rtype: bool
630 """
631
632
633 self.check_param(name)
634
635
636 return self._sim[name]
637
638
639 - def type(self, name):
640 """Return the Python type for the parameter.
641
642 @param name: The name of the parameter.
643 @type name: str
644 @return: The Python type.
645 @rtype: Python type object
646 """
647
648
649 self.check_param(name)
650
651
652 return self._py_types[name]
653
654
656 """Return the Python type for the parameter as a string representation.
657
658 @param name: The name of the parameter.
659 @type name: str
660 @return: The Python type.
661 @rtype: Python type object
662 """
663
664
665 self.check_param(name)
666
667
668 text = repr(self._py_types[name])
669
670
671 return text.split("'")[1]
672
673
674 - def uf_doc(self, label=None, caption=None, scope='spin', default=False, units=False, type=False):
675 """"Create the parameter documentation for the user function docstrings.
676
677 @keyword label: The label of the table to return.
678 @type label: str
679 @return: The parameter documentation.
680 @rtype: Desc_container instance
681 """
682
683
684 if label == None:
685 raise RelaxError("The table identifying label must be supplied.")
686 if label not in self._uf_docs:
687 raise RelaxError("The table identifying label '%s' does not exist." % label)
688
689
690 return self._uf_docs[label]
691
692
694 """Return the units string for the parameter.
695
696 @param name: The name of the parameter.
697 @type name: str
698 @return: The units string.
699 @rtype: str
700 """
701
702
703 self.check_param(name)
704
705
706 if isinstance(self._conv_factor[name], FunctionType) or isinstance(self._conv_factor[name], MethodType):
707 return self._units[name]()
708
709
710 return self._units[name]
711