1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """Module for the manipulation of parameter and constant values."""
24
25
26 from numpy import ndarray
27 import sys
28
29
30 from lib.check_types import is_num
31 from lib.errors import RelaxError, RelaxNoSequenceError, RelaxParamSetError, RelaxValueError
32 from lib.io import get_file_path, open_write_file, read_spin_data, write_spin_data
33 from pipe_control import minimise, pipes
34 from pipe_control.mol_res_spin import exists_mol_res_spin_data, generate_spin_id_unique, spin_loop
35 from pipe_control.result_files import add_result_file
36 import specific_analyses
37 from status import Status; status = Status()
38
39
40 -def copy(pipe_from=None, pipe_to=None, param=None):
41 """Copy spin specific data values from pipe_from to pipe_to.
42
43 @param pipe_from: The data pipe to copy the value from. This defaults to the current data
44 pipe.
45 @type pipe_from: str
46 @param pipe_to: The data pipe to copy the value to. This defaults to the current data pipe.
47 @type pipe_to: str
48 @param param: The name of the parameter to copy the values of.
49 @type param: str
50 """
51
52
53 if pipe_from == None:
54 pipe_from = pipes.cdp_name()
55 if pipe_to == None:
56 pipe_to = pipes.cdp_name()
57 pipe_orig = pipes.cdp_name()
58
59
60 pipes.test(pipe_to)
61
62
63 if not exists_mol_res_spin_data(pipe_from):
64 raise RelaxNoSequenceError(pipe_from)
65
66
67 if not exists_mol_res_spin_data(pipe_to):
68 raise RelaxNoSequenceError(pipe_to)
69
70
71 return_value = specific_analyses.setup.get_specific_fn('return_value', pipes.get_type(pipe_from))
72
73
74 for spin in spin_loop(pipe=pipe_to):
75
76 value, error = return_value(spin, param)
77
78
79 if value != None or error != None:
80 raise RelaxValueError(param, pipe_to)
81
82
83 pipes.switch(pipe_to)
84
85
86 for spin, spin_id in spin_loop(pipe=pipe_from, return_id=True):
87
88 value, error = return_value(spin, param)
89
90
91 if value != None:
92 set(spin_id=spin_id, val=value, param=param, pipe=pipe_to)
93 if error != None:
94 set(spin_id=spin_id, val=error, param=param, pipe=pipe_to, error=True)
95
96
97 minimise.reset_min_stats(pipe_to)
98
99
100 pipes.switch(pipe_orig)
101
102
103 -def display(param=None, scaling=1.0):
104 """Display spin specific data values.
105
106 @keyword param: The name of the parameter to display.
107 @type param: str
108 @keyword scaling: The value to scale the parameter by.
109 @type scaling: float
110 """
111
112
113 pipes.test()
114
115
116 if not exists_mol_res_spin_data():
117 raise RelaxNoSequenceError
118
119
120 write_data(param=param, file=sys.stdout, scaling=scaling)
121
122
157
158
160 """Function for sorting and partitioning the parameters and their values.
161
162 The two major partitions are the tensor parameters and the spin specific parameters.
163
164 @param val: The parameter values.
165 @type val: None, number, or list of numbers
166 @param param: The parameter names.
167 @type param: None, str, or list of str
168 @return: A tuple, of length 4, of lists. The first and second elements are the lists of
169 spin specific parameters and values respectively. The third and forth elements
170 are the lists of all other parameters and their values.
171 @rtype: tuple of 4 lists
172 """
173
174
175 is_spin_param = specific_analyses.setup.get_specific_fn('is_spin_param', pipes.get_type())
176
177
178 spin_params = []
179 spin_values = []
180 other_params = []
181 other_values = []
182
183
184 if isinstance(param, str):
185
186 if is_spin_param(param):
187 params = spin_params
188 values = spin_values
189
190
191 else:
192 params = other_params
193 values = other_values
194
195
196 if isinstance(val, list) or isinstance(val, ndarray):
197
198 for i in range(len(val)):
199 params.append(param)
200
201
202 values = val
203
204
205 else:
206
207 params.append(param)
208
209
210 values.append(val)
211
212
213 elif isinstance(param, list):
214
215 for i in range(len(param)):
216
217 if is_spin_param(param[i]):
218 params = spin_params
219 values = spin_values
220
221
222 else:
223 params = other_params
224 values = other_values
225
226
227 params.append(param[i])
228
229
230 if isinstance(val, list) or isinstance(val, ndarray):
231 values.append(val[i])
232 else:
233 values.append(val)
234
235
236
237 return spin_params, spin_values, other_params, other_values
238
239
240 -def read(param=None, scaling=1.0, file=None, dir=None, file_data=None, spin_id_col=None, mol_name_col=None, res_num_col=None, res_name_col=None, spin_num_col=None, spin_name_col=None, data_col=None, error_col=None, sep=None, spin_id=None):
241 """Read spin specific data values from a file.
242
243 @keyword param: The name of the parameter to read.
244 @type param: str
245 @keyword scaling: A scaling factor by which all read values are multiplied by.
246 @type scaling: float
247 @keyword file: The name of the file to open.
248 @type file: str
249 @keyword dir: The directory containing the file (defaults to the current directory if None).
250 @type dir: str or None
251 @keyword file_data: An alternative to opening a file, if the data already exists in the correct format. The format is a list of lists where the first index corresponds to the row and the second the column.
252 @type file_data: list of lists
253 @keyword spin_id_col: The column containing the spin ID strings. If supplied, the mol_name_col, res_name_col, res_num_col, spin_name_col, and spin_num_col arguments must be none.
254 @type spin_id_col: int or None
255 @keyword mol_name_col: The column containing the molecule name information. If supplied, spin_id_col must be None.
256 @type mol_name_col: int or None
257 @keyword res_name_col: The column containing the residue name information. If supplied, spin_id_col must be None.
258 @type res_name_col: int or None
259 @keyword res_num_col: The column containing the residue number information. If supplied, spin_id_col must be None.
260 @type res_num_col: int or None
261 @keyword spin_name_col: The column containing the spin name information. If supplied, spin_id_col must be None.
262 @type spin_name_col: int or None
263 @keyword spin_num_col: The column containing the spin number information. If supplied, spin_id_col must be None.
264 @type spin_num_col: int or None
265 @keyword data_col: The column containing the RDC data in Hz.
266 @type data_col: int or None
267 @keyword error_col: The column containing the RDC errors.
268 @type error_col: int or None
269 @keyword sep: The column separator which, if None, defaults to whitespace.
270 @type sep: str or None
271 @keyword spin_id: The spin ID string.
272 @type spin_id: None or str
273 """
274
275
276 pipes.test()
277
278
279 if not exists_mol_res_spin_data():
280 raise RelaxNoSequenceError
281
282
283 if minimise.return_data_name(param):
284
285 min_stat = True
286
287
288 return_value = minimise.return_value
289
290
291 set_fn = minimise.set
292
293
294 else:
295
296 min_stat = False
297
298
299 return_value = specific_analyses.setup.get_specific_fn('return_value', pipes.get_type())
300
301
302 set_fn = set
303
304
305 for spin in spin_loop():
306
307 if not spin.select:
308 continue
309
310
311 value, error = return_value(spin, param)
312
313
314 if value != None or error != None:
315 raise RelaxValueError(param)
316
317
318 mol_names = []
319 res_nums = []
320 res_names = []
321 spin_nums = []
322 spin_names = []
323 values = []
324 errors = []
325 for data in read_spin_data(file=file, dir=dir, file_data=file_data, spin_id_col=spin_id_col, mol_name_col=mol_name_col, res_num_col=res_num_col, res_name_col=res_name_col, spin_num_col=spin_num_col, spin_name_col=spin_name_col, data_col=data_col, error_col=error_col, sep=sep, spin_id=spin_id):
326
327 if data_col and error_col:
328 mol_name, res_num, res_name, spin_num, spin_name, value, error = data
329 elif data_col:
330 mol_name, res_num, res_name, spin_num, spin_name, value = data
331 error = None
332 else:
333 mol_name, res_num, res_name, spin_num, spin_name, error = data
334 value = None
335
336
337 id = generate_spin_id_unique(mol_name=mol_name, res_num=res_num, res_name=res_name, spin_num=spin_num, spin_name=spin_name)
338 set_fn(val=value, error=error, param=param, spin_id=id)
339
340
341 mol_names.append(mol_name)
342 res_nums.append(res_num)
343 res_names.append(res_name)
344 spin_nums.append(spin_num)
345 spin_names.append(spin_name)
346 values.append(value)
347 errors.append(error)
348
349
350 write_spin_data(file=sys.stdout, mol_names=mol_names, res_nums=res_nums, res_names=res_names, spin_nums=spin_nums, spin_names=spin_names, data=values, data_name=param, error=errors, error_name='%s_error'%param)
351
352
353 if not min_stat:
354 minimise.reset_min_stats()
355
356
357 -def set(val=None, param=None, pipe=None, spin_id=None, error=False, force=True, reset=True):
358 """Set global or spin specific data values.
359
360 @keyword val: The parameter values.
361 @type val: None or list
362 @keyword param: The parameter names.
363 @type param: None, str, or list of str
364 @keyword pipe: The data pipe the values should be placed in.
365 @type pipe: None or str
366 @keyword spin_id: The spin identification string.
367 @type spin_id: str
368 @keyword error: A flag which if True will allow the parameter errors to be set instead of the values.
369 @type error: bool
370 @keyword force: A flag forcing the overwriting of current values.
371 @type force: bool
372 @keyword reset: A flag which if True will cause all minimisation statistics to be reset.
373 @type reset: bool
374 """
375
376
377 if pipe:
378 orig_pipe = pipes.cdp_name()
379 pipes.switch(pipe)
380
381
382 pipes.test()
383
384
385 default_value = specific_analyses.setup.get_specific_fn('default_value', pipes.get_type())
386 get_param_names = specific_analyses.setup.get_specific_fn('get_param_names', pipes.get_type())
387 return_data_name = specific_analyses.setup.get_specific_fn('return_data_name', pipes.get_type())
388 set_param_values = specific_analyses.setup.get_specific_fn('set_param_values', pipes.get_type())
389
390
391 if isinstance(val, ndarray):
392 val = val.tolist()
393
394
395 if (isinstance(val, float) or isinstance(val, int)) and param == None:
396 raise RelaxError("The combination of a single value '%s' without specifying the parameter name is invalid." % val)
397 if isinstance(val, list) and isinstance(param, str):
398 raise RelaxError("Invalid combination: When multiple values '%s' are specified, either no parameters or a list of parameters must by supplied rather than the single parameter '%s'." % (val, param))
399
400
401 if isinstance(val, list) and isinstance(param, list) and len(val) != len(param):
402 raise RelaxError("Both the value array and parameter array must be of equal length.")
403
404
405 if param == None:
406 param = get_param_names()
407
408
409 if not isinstance(param, list):
410 param = [param]
411
412
413 if val != None and not isinstance(val, list):
414 val = [val] * len(param)
415
416
417 if val == None:
418
419 val = []
420 for i in range(len(param)):
421 val.append(default_value(return_data_name(param[i])))
422
423
424 if val[-1] == None:
425 raise RelaxParamSetError(param[i])
426
427
428 set_param_values(param=param, value=val, spin_id=spin_id, error=error, force=force)
429
430
431 if reset:
432 minimise.reset_min_stats()
433
434
435 if pipe:
436 pipes.switch(orig_pipe)
437
438
439 -def write(param=None, file=None, dir=None, scaling=1.0, return_value=None, return_data_desc=None, comment=None, bc=False, force=False):
440 """Write data to a file.
441
442 @keyword param: The name of the parameter to write to file.
443 @type param: str
444 @keyword file: The file to write the data to.
445 @type file: str
446 @keyword dir: The name of the directory to place the file into (defaults to the current directory).
447 @type dir: str
448 @keyword scaling: The value to scale the parameter by.
449 @type scaling: float
450 @keyword return_value: An optional function which if supplied will override the default value returning function.
451 @type return_value: None or func
452 @keyword return_data_desc: An optional function which if supplied will override the default parameter description returning function.
453 @type return_data_desc: None or func
454 @keyword comment: Text which will be added to the start of the file as comments. All lines will be prefixed by '# '.
455 @type comment: str
456 @keyword bc: A flag which if True will cause the back calculated values to be written.
457 @type bc: bool
458 @keyword force: A flag which if True will cause any pre-existing file to be overwritten.
459 @type force: bool
460 """
461
462
463 pipes.test()
464
465
466 if not exists_mol_res_spin_data():
467 raise RelaxNoSequenceError
468
469
470 file_path = get_file_path(file, dir)
471 file = open_write_file(file, dir, force)
472
473
474 write_data(param=param, file=file, scaling=scaling, return_value=return_value, return_data_desc=return_data_desc, comment=comment, bc=bc)
475
476
477 file.close()
478
479
480 add_result_file(type='text', label='Text', file=file_path)
481
482
483 -def write_data(param=None, file=None, scaling=1.0, bc=False, return_value=None, return_data_desc=None, comment=None):
484 """The function which actually writes the data.
485
486 @keyword param: The parameter to write.
487 @type param: str
488 @keyword file: The file to write the data to.
489 @type file: str
490 @keyword scaling: The value to scale the parameter by.
491 @type scaling: float
492 @keyword bc: A flag which if True will cause the back calculated values to be written.
493 @type bc: bool
494 @keyword return_value: An optional function which if supplied will override the default value returning function.
495 @type return_value: None or func
496 @keyword return_data_desc: An optional function which if supplied will override the default parameter description returning function.
497 @type return_data_desc: None or func
498 @keyword comment: Text which will be added to the start of the file as comments. All lines will be prefixed by '# '.
499 @type comment: str
500 """
501
502
503 if not return_value:
504 return_value = specific_analyses.setup.get_specific_fn('return_value', pipes.get_type())
505 if not return_data_desc:
506 return_data_desc = specific_analyses.setup.get_specific_fn('return_data_desc', pipes.get_type())
507
508
509 format = "%-30s%-30s"
510
511
512 mol_names = []
513 res_nums = []
514 res_names = []
515 spin_nums = []
516 spin_names = []
517 values = []
518 errors = []
519
520
521 desc = return_data_desc(param)
522 if desc:
523 file.write("# Parameter description: %s.\n" % desc)
524 file.write("#\n")
525
526
527 if comment:
528
529 lines = comment.splitlines()
530
531
532 for line in lines:
533 file.write("# %s\n" % line)
534 file.write("#\n")
535
536
537 data_names = 'value'
538 error_names = 'error'
539 data_type = None
540 for spin, mol_name, res_num, res_name in spin_loop(full_info=True):
541
542 value, error = return_value(spin, param, bc=bc)
543
544
545 if isinstance(value, dict):
546
547 if not data_type in [None, 'dict']:
548 raise RelaxError("Mixed data types.")
549 data_type = 'dict'
550
551
552 if not isinstance(data_names, list):
553 data_names = []
554 error_names = []
555
556
557 keys = sorted(value.keys())
558
559
560 for key in keys:
561
562 if key not in data_names:
563 data_names.append(key)
564 error_names.append('sd(%s)' % key)
565
566
567 elif isinstance(value, list):
568
569 if not data_type in [None, 'list']:
570 raise RelaxError("Mixed data types.")
571 data_type = 'list'
572
573
574 if not isinstance(data_names, list):
575 data_names = []
576 error_names = []
577
578
579 elif len(data_names) != len(value):
580 raise RelaxError("The list type data has an inconsistent number of elements between different spin systems.")
581
582
583 for i in range(len(value)):
584
585 data_names.append('value_%s' % i)
586 error_names.append('error_%s' % i)
587
588
589 elif value == None:
590 pass
591
592
593 else:
594
595 if not data_type in [None, 'value']:
596 raise RelaxError("Mixed data types.")
597 data_type = 'value'
598
599
600 for spin, mol_name, res_num, res_name in spin_loop(full_info=True):
601
602 value, error = return_value(spin, param, bc=bc)
603
604
605 mol_names.append(mol_name)
606 res_nums.append(res_num)
607 res_names.append(res_name)
608 spin_nums.append(spin.num)
609 spin_names.append(spin.name)
610
611
612 if data_type == 'dict':
613
614 values.append([])
615 errors.append([])
616
617
618 for key in data_names:
619
620 if value == None or key not in value:
621 values[-1].append(None)
622 else:
623 values[-1].append(scale(value[key], scaling))
624 if error == None or key not in error:
625 errors[-1].append(None)
626 else:
627 errors[-1].append(scale(error[key], scaling))
628
629
630 elif data_type == 'list':
631
632 values.append([])
633 errors.append([])
634
635
636 for i in range(len(data_names)):
637
638 if value == None:
639 values[-1].append(None)
640 else:
641 values[-1].append(scale(value[i], scaling))
642 if error == None:
643 errors[-1].append(None)
644 else:
645 errors[-1].append(scale(error[i], scaling))
646
647
648 else:
649
650 values.append(scale(value, scaling))
651 errors.append(scale(error, scaling))
652
653
654 write_spin_data(file, mol_names=mol_names, res_nums=res_nums, res_names=res_names, spin_nums=spin_nums, spin_names=spin_names, data=values, data_name=data_names, error=errors, error_name=error_names)
655
656
657 -def scale(value, scaling):
658 """Scale the given value by the scaling factor, handling all input value types.
659
660 @param value: The value to scale.
661 @type value: anything
662 @param scaling: The scaling factor.
663 @type scaling: float
664 """
665
666
667 if not is_num(value):
668 return value
669
670
671 return value * scaling
672