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