1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 from math import pi
26 from os import F_OK, R_OK, X_OK, access, getcwd, listdir, sep
27 from os.path import isdir
28 from re import search
29 import sys
30 from time import sleep, time
31
32
33 from info import Info_box; info = Info_box()
34 from lib.errors import RelaxError, RelaxNoSequenceError, RelaxNoValueError
35 from lib.float import floatAsByteArray
36 from lib.text.sectioning import title, subtitle
37 from lib.text.string import LIST, PARAGRAPH, SECTION, SUBSECTION, TITLE, to_docstring
38 from lib.timing import print_elapsed_time
39 from pipe_control.interatomic import interatomic_loop
40 from pipe_control.mol_res_spin import exists_mol_res_spin_data, return_spin, spin_loop
41 from pipe_control.pipes import cdp_name, get_pipe, has_pipe, pipe_names, switch
42 from pipe_control.spectrometer import get_frequencies
43 from prompt.interpreter import Interpreter
44 from status import Status; status = Status()
45
46
47 doc = [
48 [TITLE, "Automatic analysis for black-box model-free results."],
49 [PARAGRAPH, "The dauvergne_protocol auto-analysis is designed for those who appreciate black-boxes or those who appreciate complex code. Importantly, data at multiple magnetic field strengths is essential for this analysis. If you would like to change how model-free analysis is performed, the code in the file auto_analyses/dauvergne_protocol.py in the base relax directory can be copied and modified as needed and used with the relax script interface. This file is simply a complex relax script. For a description of object-oriented coding in python using classes, functions/methods, self, etc., please see the python tutorial."],
50
51 [SECTION, "References"],
52
53 [SUBSECTION, "Auto-analysis primary reference"],
54 [PARAGRAPH, "The model-free optimisation methodology herein is that of:"],
55 [LIST, info.bib['dAuvergneGooley08b'].cite_short()],
56
57 [SUBSECTION, "Techniques used in the auto-analysis"],
58 [PARAGRAPH, "Other references for features of this dauvergne_protocol auto-analysis include model-free model selection using Akaike's Information Criterion:"],
59 [LIST, info.bib['dAuvergneGooley03'].cite_short()],
60 [PARAGRAPH, "The elimination of failed model-free models and Monte Carlo simulations:"],
61 [LIST, info.bib['dAuvergneGooley06'].cite_short()],
62 [PARAGRAPH, "Significant model-free optimisation improvements:"],
63 [LIST, info.bib['dAuvergneGooley08a'].cite_short()],
64 [PARAGRAPH, "Rather than searching for the lowest chi-squared value, this auto-analysis searches for the model with the lowest AIC criterion. This complex multi-universe, multi-dimensional problem is formulated, using set theory, as the universal solution:"],
65 [LIST, info.bib['dAuvergneGooley07'].cite_short()],
66 [PARAGRAPH, "The basic three references for the original and extended model-free theories are:"],
67 [LIST, info.bib['LipariSzabo82a'].cite_short()],
68 [LIST, info.bib['LipariSzabo82b'].cite_short()],
69 [LIST, info.bib['Clore90'].cite_short()],
70
71 [SECTION, "How to use this auto-analysis"],
72 [PARAGRAPH, "The five diffusion models used in this auto-analysis are:"],
73 [LIST, "Model I (MI) - Local tm."],
74 [LIST, "Model II (MII) - Sphere."],
75 [LIST, "Model III (MIII) - Prolate spheroid."],
76 [LIST, "Model IV (MIV) - Oblate spheroid."],
77 [LIST, "Model V (MV) - Ellipsoid."],
78 [PARAGRAPH, "If using the script-based user interface (UI), changing the value of the variable diff_model will determine the behaviour of this auto-analysis. Model I must be optimised prior to any of the other diffusion models, while the Models II to V can be optimised in any order. To select the various models, set the variable diff_model to the following strings:"],
79 [LIST, "MI - 'local_tm'"],
80 [LIST, "MII - 'sphere'"],
81 [LIST, "MIII - 'prolate'"],
82 [LIST, "MIV - 'oblate'"],
83 [LIST, "MV - 'ellipsoid'"],
84 [PARAGRAPH, "This approach has the advantage of eliminating the need for an initial estimate of a global diffusion tensor and removing all the problems associated with the initial estimate."],
85 [PARAGRAPH, "It is important that the number of parameters in a model does not exceed the number of relaxation data sets for that spin. If this is the case, the list of models in the mf_models and local_tm_models variables will need to be trimmed."],
86
87 [SUBSECTION, "Model I - Local tm"],
88 [PARAGRAPH, "This will optimise the diffusion model whereby all spin of the molecule have a local tm value, i.e. there is no global diffusion tensor. This model needs to be optimised prior to optimising any of the other diffusion models. Each spin is fitted to the multiple model-free models separately, where the parameter tm is included in each model."],
89 [PARAGRAPH, "AIC model selection is used to select the models for each spin."],
90
91 [SUBSECTION, "Model II - Sphere"],
92 [PARAGRAPH, "This will optimise the isotropic diffusion model. Multiple steps are required, an initial optimisation of the diffusion tensor, followed by a repetitive optimisation until convergence of the diffusion tensor. In the relax script UI each of these steps requires this script to be rerun, unless the conv_loop flag is True. In the GUI (graphical user interface), the procedure is repeated automatically until convergence. For the initial optimisation, which will be placed in the directory './sphere/init/', the following steps are used:"],
93 [PARAGRAPH, "The model-free models and parameter values for each spin are set to those of diffusion model MI."],
94 [PARAGRAPH, "The local tm parameter is removed from the models."],
95 [PARAGRAPH, "The model-free parameters are fixed and a global spherical diffusion tensor is minimised."],
96 [PARAGRAPH, "For the repetitive optimisation, each minimisation is named from 'round_1' onwards. The initial 'round_1' optimisation will extract the diffusion tensor from the results file in './sphere/init/', and the results will be placed in the directory './sphere/round_1/'. Each successive round will take the diffusion tensor from the previous round. The following steps are used:"],
97 [PARAGRAPH, "The global diffusion tensor is fixed and the multiple model-free models are fitted to each spin."],
98 [PARAGRAPH, "AIC model selection is used to select the models for each spin."],
99 [PARAGRAPH, "All model-free and diffusion parameters are allowed to vary and a global optimisation of all parameters is carried out."],
100
101 [SUBSECTION, "Model III - Prolate spheroid"],
102 [PARAGRAPH, "The methods used are identical to those of diffusion model MII, except that an axially symmetric diffusion tensor with Da >= 0 is used. The base directory containing all the results is './prolate/'."],
103
104 [SUBSECTION, "Model IV - Oblate spheroid"],
105 [PARAGRAPH, "The methods used are identical to those of diffusion model MII, except that an axially symmetric diffusion tensor with Da <= 0 is used. The base directory containing all the results is './oblate/'."],
106
107 [SUBSECTION, "Model V - Ellipsoid"],
108 [PARAGRAPH, "The methods used are identical to those of diffusion model MII, except that a fully anisotropic diffusion tensor is used (also known as rhombic or asymmetric diffusion). The base directory is './ellipsoid/'."],
109
110 [SUBSECTION, "Final run"],
111 [PARAGRAPH, "Once all the diffusion models have converged, the final run can be executed. This is done by setting the variable diff_model to 'final'. This consists of two steps, diffusion tensor model selection, and Monte Carlo simulations. Firstly AIC model selection is used to select between the diffusion tensor models. Monte Carlo simulations are then run solely on this selected diffusion model. Minimisation of the model is bypassed as it is assumed that the model is already fully optimised (if this is not the case the final run is not yet appropriate)."],
112 [PARAGRAPH, "The final black-box model-free results will be placed in the file 'final/results'."]
113 ]
114
115
116
117 __doc__ = to_docstring(doc)
118
119
120
122 """The model-free auto-analysis."""
123
124
125 opt_func_tol = 1e-25
126 opt_max_iterations = int(1e7)
127
128 - def __init__(self, pipe_name=None, pipe_bundle=None, results_dir=None, write_results_dir=None, diff_model=None, mf_models=['m0', 'm1', 'm2', 'm3', 'm4', 'm5', 'm6', 'm7', 'm8', 'm9'], local_tm_models=['tm0', 'tm1', 'tm2', 'tm3', 'tm4', 'tm5', 'tm6', 'tm7', 'tm8', 'tm9'], grid_inc=11, diff_tensor_grid_inc={'sphere': 11, 'prolate': 11, 'oblate': 11, 'ellipsoid': 6}, min_algor='newton', mc_sim_num=500, max_iter=None, user_fns=None, conv_loop=True):
129 """Perform the full model-free analysis protocol of d'Auvergne and Gooley, 2008b.
130
131 @keyword pipe_name: The name of the data pipe containing the sequence info. This data pipe should have all values set including the CSA value, the bond length, the heteronucleus name and proton name. It should also have all relaxation data loaded.
132 @type pipe_name: str
133 @keyword pipe_bundle: The data pipe bundle to associate all spawned data pipes with.
134 @type pipe_bundle: str
135 @keyword results_dir: The directory where optimisation results will read from. Results will also be saved to this directory if the write_results_dir argument is not given.
136 @type results_dir: str
137 @keyword write_results_dir: The directory where optimisation results will be saved in. If None, it will default to the value of the results_dir argument. This is mainly used for debugging.
138 @type write_results_dir: str or None
139 @keyword diff_model: The global diffusion model to optimise. This can be one of 'local_tm', 'sphere', 'oblate', 'prolate', 'ellipsoid', or 'final'. If all or a subset of these are supplied as a list, then these will be automatically looped over and calculated.
140 @type diff_model: str or list of str
141 @keyword mf_models: The model-free models.
142 @type mf_models: list of str
143 @keyword local_tm_models: The model-free models.
144 @type local_tm_models: list of str
145 @keyword grid_inc: The grid search size (the number of increments per dimension).
146 @type grid_inc: int
147 @keyword diff_tensor_grid_inc: A list of grid search sizes for the optimisation of the sphere, prolate spheroid, oblate spheroid, and ellipsoid.
148 @type diff_tensor_grid_inc: list of int
149 @keyword min_algor: The minimisation algorithm (in most cases this should not be changed).
150 @type min_algor: str
151 @keyword mc_sim_num: The number of Monte Carlo simulations to be used for error analysis at the end of the analysis.
152 @type mc_sim_num: int
153 @keyword max_iter: The maximum number of iterations for the global iteration. Set to None, then the algorithm iterates until convergence.
154 @type max_iter: int or None.
155 @keyword user_fns: A dictionary of replacement user functions. These will overwrite the standard user functions. The key should be the name of the user function or user function class and the value should be the function or class instance.
156 @type user_fns: dict
157 @keyword conv_loop: Automatic looping over all rounds until convergence.
158 @type conv_loop: bool
159 """
160
161
162 title(file=sys.stdout, text="d'Auvergne protocol model-free auto-analysis")
163
164
165 try:
166
167 status.exec_lock.acquire(pipe_bundle, mode='auto-analysis')
168
169
170 self.pipe_name = pipe_name
171 self.pipe_bundle = pipe_bundle
172 self.mf_models = mf_models
173 self.local_tm_models = local_tm_models
174 self.grid_inc = grid_inc
175 self.diff_tensor_grid_inc = diff_tensor_grid_inc
176 self.min_algor = min_algor
177 self.mc_sim_num = mc_sim_num
178 self.max_iter = max_iter
179 self.conv_loop = conv_loop
180
181
182 self.mf_model_pipes = []
183 for i in range(len(self.mf_models)):
184 self.mf_model_pipes.append(self.name_pipe(self.mf_models[i]))
185 self.local_tm_model_pipes = []
186 for i in range(len(self.local_tm_models)):
187 self.local_tm_model_pipes.append(self.name_pipe(self.local_tm_models[i]))
188
189
190 if isinstance(diff_model, list):
191 self.diff_model_list = diff_model
192 else:
193 self.diff_model_list = [diff_model]
194
195
196 if results_dir:
197 self.results_dir = results_dir + sep
198 else:
199 self.results_dir = getcwd() + sep
200 if write_results_dir:
201 self.write_results_dir = write_results_dir + sep
202 else:
203 self.write_results_dir = self.results_dir
204
205
206 self.check_vars()
207
208
209 if self.pipe_name != cdp_name():
210 switch(self.pipe_name)
211
212
213 self.status_setup()
214
215
216 self.interpreter = Interpreter(show_script=False, raise_relax_error=True)
217 self.interpreter.populate_self()
218 self.interpreter.on(verbose=False)
219
220
221 if user_fns:
222 for name in user_fns:
223 setattr(self.interpreter, name, user_fns[name])
224
225
226 for self.diff_model in self.diff_model_list:
227
228 sleep(1)
229
230
231 status.auto_analysis[self.pipe_bundle].diff_model = self.diff_model
232
233
234 self.conv_data = Container()
235 self.conv_data.chi2 = []
236 self.conv_data.models = []
237 self.conv_data.diff_vals = []
238 if self.diff_model == 'sphere':
239 self.conv_data.diff_params = ['tm']
240 elif self.diff_model == 'oblate' or self.diff_model == 'prolate':
241 self.conv_data.diff_params = ['tm', 'Da', 'theta', 'phi']
242 elif self.diff_model == 'ellipsoid':
243 self.conv_data.diff_params = ['tm', 'Da', 'Dr', 'alpha', 'beta', 'gamma']
244 self.conv_data.spin_ids = []
245 self.conv_data.mf_params = []
246 self.conv_data.mf_vals = []
247
248
249 self.execute()
250
251
252 finally:
253
254 title(file=sys.stdout, text="Completion of the d'Auvergne protocol model-free auto-analysis")
255 print_elapsed_time(time() - status.start_time)
256
257
258 status.auto_analysis[self.pipe_bundle].fin = True
259 status.current_analysis = None
260 status.exec_lock.release()
261
262
264 """Check that the user has set the variables correctly."""
265
266
267 subtitle(file=sys.stdout, text="Auto-analysis variable checking")
268
269
270 if not isinstance(self.pipe_bundle, str):
271 raise RelaxError("The pipe bundle name '%s' is invalid." % self.pipe_bundle)
272
273
274 valid_models = ['local_tm', 'sphere', 'oblate', 'prolate', 'ellipsoid', 'final']
275 for i in range(len(self.diff_model_list)):
276 if self.diff_model_list[i] not in valid_models:
277 raise RelaxError("The diff_model value '%s' is incorrectly set. It must be one of %s." % (self.diff_model_list[i], valid_models))
278
279
280 mf_models = ['m0', 'm1', 'm2', 'm3', 'm4', 'm5', 'm6', 'm7', 'm8', 'm9']
281 local_tm_models = ['tm0', 'tm1', 'tm2', 'tm3', 'tm4', 'tm5', 'tm6', 'tm7', 'tm8', 'tm9']
282 if not isinstance(self.mf_models, list):
283 raise RelaxError("The self.mf_models user variable must be a list.")
284 if not isinstance(self.local_tm_models, list):
285 raise RelaxError("The self.local_tm_models user variable must be a list.")
286 for i in range(len(self.mf_models)):
287 if self.mf_models[i] not in mf_models:
288 raise RelaxError("The self.mf_models user variable '%s' is incorrectly set. It must be one of %s." % (self.mf_models, mf_models))
289 for i in range(len(self.local_tm_models)):
290 if self.local_tm_models[i] not in local_tm_models:
291 raise RelaxError("The self.local_tm_models user variable '%s' is incorrectly set. It must be one of %s." % (self.local_tm_models, local_tm_models))
292
293
294 if not exists_mol_res_spin_data():
295 raise RelaxNoSequenceError(self.pipe_name)
296
297
298 if not hasattr(cdp, 'ri_ids') or len(cdp.ri_ids) == 0:
299 raise RelaxNoRiError(ri_id)
300
301
302 if len(cdp.ri_ids) <= 3:
303 raise RelaxError("Insufficient relaxation data, 4 or more data sets are essential for the execution of this script.")
304
305
306 for spin, spin_id in spin_loop(return_id=True):
307
308 if not spin.select:
309 continue
310
311
312 if not hasattr(spin, 'isotope') or spin.isotope == None:
313 raise RelaxNoValueError("nuclear isotope type", spin_id=spin_id)
314
315
316 if not hasattr(spin, 'ri_data') or spin.ri_data == None:
317 continue
318
319
320 if not hasattr(spin, 'csa') or spin.csa == None:
321 raise RelaxNoValueError("CSA", spin_id=spin_id)
322
323
324 for interatom in interatomic_loop():
325
326 spin1 = return_spin(spin_hash=interatom._spin_hash1)
327 spin2 = return_spin(spin_hash=interatom._spin_hash2)
328
329
330 if not spin1.select or not spin2.select:
331 continue
332
333
334 if not hasattr(interatom, 'r') or interatom.r == None:
335 raise RelaxNoValueError("interatomic distance", spin_id=interatom.spin_id1, spin_id2=interatom.spin_id2)
336
337
338 if not isinstance(self.grid_inc, int):
339 raise RelaxError("The grid_inc user variable '%s' is incorrectly set. It should be an integer." % self.grid_inc)
340 if not isinstance(self.diff_tensor_grid_inc, dict):
341 raise RelaxError("The diff_tensor_grid_inc user variable '%s' is incorrectly set. It should be a dictionary." % self.diff_tensor_grid_inc)
342 for tensor in ['sphere', 'prolate', 'oblate', 'ellipsoid']:
343 if not tensor in self.diff_tensor_grid_inc:
344 raise RelaxError("The diff_tensor_grid_inc user variable '%s' is incorrectly set. It should contain the '%s' key." % (self.diff_tensor_grid_inc, tensor))
345 if not isinstance(self.diff_tensor_grid_inc[tensor], int):
346 raise RelaxError("The diff_tensor_grid_inc user variable '%s' is incorrectly set. The value corresponding to the key '%s' should be an integer." % (self.diff_tensor_grid_inc, tensor))
347 if not isinstance(self.min_algor, str):
348 raise RelaxError("The min_algor user variable '%s' is incorrectly set. It should be a string." % self.min_algor)
349 if not isinstance(self.mc_sim_num, int):
350 raise RelaxError("The mc_sim_num user variable '%s' is incorrectly set. It should be an integer." % self.mc_sim_num)
351
352
353 if not isinstance(self.conv_loop, bool):
354 raise RelaxError("The conv_loop user variable '%s' is incorrectly set. It should be one of the booleans True or False." % self.conv_loop)
355
356
358 """Test for the convergence of the global model."""
359
360
361 title(file=sys.stdout, text="Convergence testing")
362
363
364 if self.max_iter and self.round > self.max_iter:
365 print("Maximum number of global iterations reached. Terminating the protocol before convergence has been reached.")
366 return True
367
368
369 self.conv_data.chi2.append(cdp.chi2)
370
371
372 curr_models = ''
373 for spin in spin_loop():
374 if hasattr(spin, 'model'):
375 if not spin.model == 'None':
376 curr_models = curr_models + spin.model
377 self.conv_data.models.append(curr_models)
378
379
380 self.conv_data.diff_vals.append([])
381 for param in self.conv_data.diff_params:
382
383 self.conv_data.diff_vals[-1].append(getattr(cdp.diff_tensor, param))
384
385
386 self.conv_data.mf_vals.append([])
387 self.conv_data.mf_params.append([])
388 self.conv_data.spin_ids.append([])
389 for spin, spin_id in spin_loop(return_id=True):
390
391 if not hasattr(spin, 'params'):
392 continue
393
394
395 self.conv_data.spin_ids[-1].append(spin_id)
396 self.conv_data.mf_params[-1].append([])
397 self.conv_data.mf_vals[-1].append([])
398
399
400 for j in range(len(spin.params)):
401
402 self.conv_data.mf_params[-1][-1].append(spin.params[j])
403 self.conv_data.mf_vals[-1][-1].append(getattr(spin, spin.params[j].lower()))
404
405
406 if self.round == 1:
407 print("First round of optimisation, skipping the convergence tests.\n\n\n")
408 return False
409
410
411 converged = False
412 for i in range(self.start_round, self.round - 1):
413
414 print("\n\n\n# Comparing the current iteration to iteration %i.\n" % (i+1))
415
416
417 index = i - self.start_round
418
419
420 print("Chi-squared test:")
421 print(" chi2 (iter %i): %s" % (i+1, self.conv_data.chi2[index]))
422 print(" (as an IEEE-754 byte array: %s)" % floatAsByteArray(self.conv_data.chi2[index]))
423 print(" chi2 (iter %i): %s" % (self.round, self.conv_data.chi2[-1]))
424 print(" (as an IEEE-754 byte array: %s)" % floatAsByteArray(self.conv_data.chi2[-1]))
425 print(" chi2 (difference): %s" % (self.conv_data.chi2[index] - self.conv_data.chi2[-1]))
426 if self.conv_data.chi2[index] == self.conv_data.chi2[-1]:
427 print(" The chi-squared value has converged.\n")
428 else:
429 print(" The chi-squared value has not converged.\n")
430 continue
431
432
433 print("Identical model-free models test:")
434 if self.conv_data.models[index] == self.conv_data.models[-1]:
435 print(" The model-free models have converged.\n")
436 else:
437 print(" The model-free models have not converged.\n")
438 continue
439
440
441 print("Identical diffusion tensor parameter test:")
442 params_converged = True
443 for k in range(len(self.conv_data.diff_params)):
444
445 if self.conv_data.diff_vals[index][k] != self.conv_data.diff_vals[-1][k]:
446 print(" Parameter: %s" % param)
447 print(" Value (iter %i): %s" % (i+1, self.conv_data.diff_vals[index][k]))
448 print(" (as an IEEE-754 byte array: %s)" % floatAsByteArray(self.conv_data.diff_vals[index][k]))
449 print(" Value (iter %i): %s" % (self.round, self.conv_data.diff_vals[-1][k]))
450 print(" (as an IEEE-754 byte array: %s)" % floatAsByteArray(self.conv_data.diff_vals[-1][k]))
451 print(" The diffusion parameters have not converged.\n")
452 params_converged = False
453 break
454 if not params_converged:
455 continue
456 print(" The diffusion tensor parameters have converged.\n")
457
458
459 print("\nIdentical model-free parameter test:")
460 if len(self.conv_data.spin_ids[index]) != len(self.conv_data.spin_ids[-1]):
461 print(" Different number of spins.")
462 continue
463 for j in range(len(self.conv_data.spin_ids[-1])):
464
465 for k in range(len(self.conv_data.mf_params[-1][j])):
466
467 if self.conv_data.mf_vals[index][j][k] != self.conv_data.mf_vals[-1][j][k]:
468 print(" Spin ID: %s" % self.conv_data.spin_ids[-1][j])
469 print(" Parameter: %s" % self.conv_data.mf_params[-1][j][k])
470 print(" Value (iter %i): %s" % (i+1, self.conv_data.mf_vals[index][j][k]))
471 print(" (as an IEEE-754 byte array: %s)" % floatAsByteArray(self.conv_data.mf_vals[index][j][k]))
472 print(" Value (iter %i): %s" % (self.round, self.conv_data.mf_vals[-1][j][k]))
473 print(" (as an IEEE-754 byte array: %s)" % floatAsByteArray(self.conv_data.mf_vals[index][j][k]))
474 print(" The model-free parameters have not converged.\n")
475 params_converged = False
476 break
477 if not params_converged:
478 continue
479 print(" The model-free parameters have converged.\n")
480
481
482 converged = True
483 break
484
485
486
487
488
489 print("\nConvergence:")
490 if converged:
491
492 status.auto_analysis[self.pipe_bundle].convergence = True
493
494
495 print(" [ Yes ]")
496
497
498 return True
499 else:
500
501 print(" [ No ]")
502
503
504 return False
505
506
508 """Function for returning the name of next round of optimisation."""
509
510
511 base_dir = self.results_dir+sep+model
512
513
514 subtitle(file=sys.stdout, text="Determining the next round of optimisation")
515 print("%-30s %s" % ("Base model directory:", base_dir))
516
517
518 if not isdir(base_dir) and access(base_dir, F_OK):
519 raise RelaxError("The base model directory '%s' is not usable as a file with the same name already exists." % base_dir)
520
521
522 if not isdir(base_dir):
523 print("%-30s %i" % ("Round:", 0))
524 print("The base directory does not exist.")
525 return 0
526
527
528 if not access(base_dir, R_OK):
529 raise RelaxError("The base model directory '%s' is not readable." % base_dir)
530 if not access(base_dir, X_OK):
531 raise RelaxError("The base model directory '%s' is not executable." % base_dir)
532
533
534 dir_list = listdir(base_dir)
535
536
537 if 'init' not in dir_list:
538 print("%-30s %i" % ("Round:", 0))
539 print("No 'init' directory present in the base directory.")
540 return 0
541
542
543 rnd_dirs = []
544 for file in dir_list:
545 if search('^round_', file):
546 rnd_dirs.append(file)
547
548
549 numbers = []
550 for dir in rnd_dirs:
551 try:
552 numbers.append(int(dir[6:]))
553 except:
554 pass
555 numbers.sort()
556
557
558 if not len(numbers):
559 print("%-30s %i" % ("Round:", 1))
560 print("No directories beginning with 'round_' exist.")
561 return 1
562
563
564 max_round = numbers[-1]
565
566
567 for i in range(max_round, -1, -1):
568
569 complete_round = i
570
571
572 file_root = base_dir + sep + "round_%i" % i + sep + 'opt' + sep + 'results'
573
574
575 if access(file_root + '.bz2', F_OK):
576 break
577 if access(file_root + '.gz', F_OK):
578 break
579 if access(file_root, F_OK):
580 break
581
582
583 if complete_round == 0:
584 print("%-30s %i" % ("Round:", 0))
585 print("No opt/results files can be found.")
586 return 0
587
588
589 print("%-30s %i" % ("Round:", complete_round + 1))
590 return complete_round + 1
591
592
594 """Execute the protocol."""
595
596
597
598
599 if self.diff_model == 'local_tm':
600
601 title(file=sys.stdout, text="Model MI - Local tm")
602
603
604 self.base_dir = self.results_dir+'local_tm'+sep
605
606
607 self.multi_model(local_tm=True)
608
609
610 self.model_selection(modsel_pipe=self.name_pipe('aic'), dir=self.base_dir + 'aic')
611
612
613
614
615
616 elif self.diff_model == 'sphere' or self.diff_model == 'prolate' or self.diff_model == 'oblate' or self.diff_model == 'ellipsoid':
617
618 if self.diff_model == 'sphere':
619 title(file=sys.stdout, text="Model MII - Spherical diffusion")
620 elif self.diff_model == 'prolate':
621 title(file=sys.stdout, text="Model MIII - Prolate spheroidal diffusion")
622 elif self.diff_model == 'oblate':
623 title(file=sys.stdout, text="Model MIV - Oblate spheroidal diffusion")
624 elif self.diff_model == 'ellipsoid':
625 title(file=sys.stdout, text="Model MV - Ellipsoidal diffusion")
626
627
628 dir_list = listdir(self.results_dir)
629 if 'local_tm' not in dir_list:
630 raise RelaxError("The local_tm model must be optimised first.")
631
632
633 self.start_round = self.determine_rnd(model=self.diff_model)
634
635
636
637 while True:
638
639 self.round = self.determine_rnd(model=self.diff_model)
640 status.auto_analysis[self.pipe_bundle].round = self.round
641
642
643 if self.round == 0:
644
645 subtitle(file=sys.stdout, text="Initial round of optimisation")
646
647
648 self.base_dir = self.results_dir+self.diff_model+sep+'init'+sep
649
650
651 name = self.name_pipe(self.diff_model)
652
653
654 if has_pipe(name):
655 self.interpreter.pipe.delete(name)
656 self.interpreter.pipe.create(name, 'mf', bundle=self.pipe_bundle)
657
658
659 self.interpreter.results.read(file='results', dir=self.results_dir+'local_tm'+sep+'aic')
660
661
662 self.interpreter.model_free.remove_tm()
663
664
665 if self.diff_model == 'sphere':
666 self.interpreter.diffusion_tensor.init(None, fixed=False)
667 inc = self.diff_tensor_grid_inc['sphere']
668 elif self.diff_model == 'prolate':
669 self.interpreter.diffusion_tensor.init((None, None, None, None), spheroid_type='prolate', fixed=False)
670 inc = self.diff_tensor_grid_inc['prolate']
671 elif self.diff_model == 'oblate':
672 self.interpreter.diffusion_tensor.init((None, None, None, None), spheroid_type='oblate', fixed=False)
673 inc = self.diff_tensor_grid_inc['oblate']
674 elif self.diff_model == 'ellipsoid':
675 self.interpreter.diffusion_tensor.init((None, None, None, None, None, None), fixed=False)
676 inc = self.diff_tensor_grid_inc['ellipsoid']
677
678
679 self.interpreter.fix('all_spins')
680 self.interpreter.minimise.grid_search(inc=inc)
681 self.interpreter.minimise.execute(self.min_algor, func_tol=self.opt_func_tol, max_iter=self.opt_max_iterations)
682
683
684 self.interpreter.results.write(file='results', dir=self.base_dir, force=True)
685
686
687
688 else:
689
690 subtitle(file=sys.stdout, text="Round %i of optimisation" % self.round)
691
692
693 self.base_dir = self.results_dir+self.diff_model + sep+'round_'+repr(self.round)+sep
694
695
696 self.load_tensor()
697
698
699 self.multi_model()
700
701
702 self.model_selection(modsel_pipe=self.name_pipe('aic'), dir=self.base_dir + 'aic')
703
704
705 self.interpreter.fix('all', fixed=False)
706
707
708 self.interpreter.minimise.execute(self.min_algor, func_tol=self.opt_func_tol, max_iter=self.opt_max_iterations)
709
710
711 dir = self.base_dir + 'opt'
712 self.interpreter.results.write(file='results', dir=dir, force=True)
713
714
715 converged = self.convergence()
716
717
718 if converged or not self.conv_loop:
719 break
720
721
722 status.auto_analysis[self.pipe_bundle].round = None
723
724
725
726
727
728 elif self.diff_model == 'final':
729
730 title(file=sys.stdout, text="Final run")
731
732
733
734
735
736 subtitle(file=sys.stdout, text="Diffusion model selection")
737
738
739 dir_list = listdir(self.results_dir)
740
741
742 min_models = ['local_tm', 'sphere']
743 for model in min_models:
744 if model not in dir_list:
745 raise RelaxError("The minimum set of global diffusion models required for the protocol have not been optimised, the '%s' model results cannot be found." % model)
746
747
748 all_models = ['local_tm', 'sphere', 'prolate', 'oblate', 'ellipsoid']
749 self.opt_models = []
750 self.pipes = []
751 for model in all_models:
752 if model in dir_list:
753 self.opt_models.append(model)
754 self.pipes.append(self.name_pipe(model))
755
756
757 for name in pipe_names(bundle=self.pipe_bundle):
758 if name in self.pipes + self.mf_model_pipes + self.local_tm_model_pipes + [self.name_pipe('aic'), self.name_pipe('previous')]:
759 self.interpreter.pipe.delete(name)
760
761
762 self.interpreter.pipe.create(self.name_pipe('local_tm'), 'mf', bundle=self.pipe_bundle)
763
764
765 self.interpreter.results.read(file='results', dir=self.results_dir+'local_tm'+sep+'aic')
766
767
768 for model in ['sphere', 'prolate', 'oblate', 'ellipsoid']:
769
770 if model not in self.opt_models:
771 continue
772
773
774 self.round = self.determine_rnd(model=model) - 1
775
776
777 if self.round < 1:
778
779 name = model
780 if model == 'prolate' or model == 'oblate':
781 name = name + ' spheroid'
782
783
784 raise RelaxError("Multiple rounds of optimisation of the " + name + " (between 8 to 15) are required for the proper execution of this script.")
785
786
787 self.interpreter.pipe.create(self.name_pipe(model), 'mf', bundle=self.pipe_bundle)
788
789
790 self.interpreter.results.read(file='results', dir=self.results_dir+model + sep+'round_'+repr(self.round)+sep+'opt')
791
792
793 self.model_selection(modsel_pipe=self.name_pipe('final'), write_flag=False)
794
795
796
797
798
799
800 subtitle(file=sys.stdout, text="Monte Carlo simulations")
801
802
803 if hasattr(get_pipe(self.name_pipe('final')), 'diff_tensor'):
804 self.interpreter.fix('diff')
805
806
807 self.interpreter.monte_carlo.setup(number=self.mc_sim_num)
808 self.interpreter.monte_carlo.create_data()
809 self.interpreter.monte_carlo.initial_values()
810 self.interpreter.minimise.execute(self.min_algor, func_tol=self.opt_func_tol, max_iter=self.opt_max_iterations)
811 self.interpreter.eliminate()
812 self.interpreter.monte_carlo.error_analysis()
813
814
815
816
817
818
819 subtitle(file=sys.stdout, text="Writing the final results")
820
821
822 self.write_results()
823
824
825
826
827
828 else:
829 raise RelaxError("Unknown diffusion model, change the value of 'self.diff_model'")
830
831
847
848
860
861
907
908
910 """Generate a unique name for the data pipe.
911
912 @param prefix: The prefix of the data pipe name.
913 @type prefix: str
914 """
915
916
917 name = "%s - %s" % (prefix, self.pipe_bundle)
918
919
920 return name
921
922
950
951
953 """Create Grace plots of the final model-free results."""
954
955
956 dir = self.write_results_dir + 'final'
957 self.interpreter.results.write(file='results', dir=dir, force=True)
958
959
960 dir = self.write_results_dir + 'final' + sep + 'grace'
961 self.interpreter.grace.write(x_data_type='res_num', y_data_type='s2', file='s2.agr', dir=dir, force=True)
962 self.interpreter.grace.write(x_data_type='res_num', y_data_type='s2f', file='s2f.agr', dir=dir, force=True)
963 self.interpreter.grace.write(x_data_type='res_num', y_data_type='s2s', file='s2s.agr', dir=dir, force=True)
964 self.interpreter.grace.write(x_data_type='res_num', y_data_type='te', file='te.agr', dir=dir, force=True)
965 self.interpreter.grace.write(x_data_type='res_num', y_data_type='tf', file='tf.agr', dir=dir, force=True)
966 self.interpreter.grace.write(x_data_type='res_num', y_data_type='ts', file='ts.agr', dir=dir, force=True)
967 self.interpreter.grace.write(x_data_type='res_num', y_data_type='rex', file='rex.agr', dir=dir, force=True)
968 self.interpreter.grace.write(x_data_type='s2', y_data_type='te', file='s2_vs_te.agr', dir=dir, force=True)
969 self.interpreter.grace.write(x_data_type='s2', y_data_type='rex', file='s2_vs_rex.agr', dir=dir, force=True)
970 self.interpreter.grace.write(x_data_type='te', y_data_type='rex', file='te_vs_rex.agr', dir=dir, force=True)
971
972
973 dir = self.write_results_dir + 'final'
974 self.interpreter.value.write(param='s2', file='s2.txt', dir=dir, force=True)
975 self.interpreter.value.write(param='s2f', file='s2f.txt', dir=dir, force=True)
976 self.interpreter.value.write(param='s2s', file='s2s.txt', dir=dir, force=True)
977 self.interpreter.value.write(param='te', file='te.txt', dir=dir, force=True)
978 self.interpreter.value.write(param='tf', file='tf.txt', dir=dir, force=True)
979 self.interpreter.value.write(param='ts', file='ts.txt', dir=dir, force=True)
980 self.interpreter.value.write(param='rex', file='rex.txt', dir=dir, force=True)
981 self.interpreter.value.write(param='local_tm', file='local_tm.txt', dir=dir, force=True)
982 frqs = get_frequencies()
983 for i in range(len(frqs)):
984 comment = "This is the Rex value with units rad.s^-1 scaled to a magnetic field strength of %s MHz." % (frqs[i]/1e6)
985 self.interpreter.value.write(param='rex', file='rex_%s.txt'%int(frqs[i]/1e6), dir=dir, scaling=(2.0*pi*frqs[i])**2, comment=comment, force=True)
986
987
988 dir = self.write_results_dir + 'final' + sep + 'pymol'
989 self.interpreter.pymol.macro_write(data_type='s2', dir=dir, force=True)
990 self.interpreter.pymol.macro_write(data_type='s2f', dir=dir, force=True)
991 self.interpreter.pymol.macro_write(data_type='s2s', dir=dir, force=True)
992 self.interpreter.pymol.macro_write(data_type='amp_fast', dir=dir, force=True)
993 self.interpreter.pymol.macro_write(data_type='amp_slow', dir=dir, force=True)
994 self.interpreter.pymol.macro_write(data_type='te', dir=dir, force=True)
995 self.interpreter.pymol.macro_write(data_type='tf', dir=dir, force=True)
996 self.interpreter.pymol.macro_write(data_type='ts', dir=dir, force=True)
997 self.interpreter.pymol.macro_write(data_type='time_fast', dir=dir, force=True)
998 self.interpreter.pymol.macro_write(data_type='time_slow', dir=dir, force=True)
999 self.interpreter.pymol.macro_write(data_type='rex', dir=dir, force=True)
1000
1001
1002 dir = self.write_results_dir + 'final' + sep + 'molmol'
1003 self.interpreter.molmol.macro_write(data_type='s2', dir=dir, force=True)
1004 self.interpreter.molmol.macro_write(data_type='s2f', dir=dir, force=True)
1005 self.interpreter.molmol.macro_write(data_type='s2s', dir=dir, force=True)
1006 self.interpreter.molmol.macro_write(data_type='amp_fast', dir=dir, force=True)
1007 self.interpreter.molmol.macro_write(data_type='amp_slow', dir=dir, force=True)
1008 self.interpreter.molmol.macro_write(data_type='te', dir=dir, force=True)
1009 self.interpreter.molmol.macro_write(data_type='tf', dir=dir, force=True)
1010 self.interpreter.molmol.macro_write(data_type='ts', dir=dir, force=True)
1011 self.interpreter.molmol.macro_write(data_type='time_fast', dir=dir, force=True)
1012 self.interpreter.molmol.macro_write(data_type='time_slow', dir=dir, force=True)
1013 self.interpreter.molmol.macro_write(data_type='rex', dir=dir, force=True)
1014
1015
1016 if hasattr(cdp, 'structure') and hasattr(cdp, 'diff_tensor'):
1017 dir = self.write_results_dir + 'final'
1018 self.interpreter.structure.create_diff_tensor_pdb(file="tensor.pdb", dir=dir, force=True)
1019
1020
1021
1023 """Empty container for data storage."""
1024