Package gui :: Package analyses :: Module auto_rx_base
[hide private]
[frames] | no frames]

Source Code for Module gui.analyses.auto_rx_base

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2009-2011 Michael Bieri                                       # 
  4  # Copyright (C) 2010-2012 Edward d'Auvergne                                   # 
  5  #                                                                             # 
  6  # This file is part of the program relax (http://www.nmr-relax.com).          # 
  7  #                                                                             # 
  8  # This program is free software: you can redistribute it and/or modify        # 
  9  # it under the terms of the GNU General Public License as published by        # 
 10  # the Free Software Foundation, either version 3 of the License, or           # 
 11  # (at your option) any later version.                                         # 
 12  #                                                                             # 
 13  # This program is distributed in the hope that it will be useful,             # 
 14  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 16  # GNU General Public License for more details.                                # 
 17  #                                                                             # 
 18  # You should have received a copy of the GNU General Public License           # 
 19  # along with this program.  If not, see <http://www.gnu.org/licenses/>.       # 
 20  #                                                                             # 
 21  ############################################################################### 
 22   
 23  # Module docstring. 
 24  """Module containing the base class for the automatic R1 and R2 analysis frames.""" 
 25   
 26  # Python module imports. 
 27  from os import sep 
 28  import sys 
 29  import wx 
 30   
 31  # relax module imports. 
 32  from auto_analyses.relax_fit import Relax_fit 
 33  from data import Relax_data_store; ds = Relax_data_store() 
 34  from generic_fns.mol_res_spin import are_spins_named, exists_mol_res_spin_data 
 35  from generic_fns.pipes import has_bundle, has_pipe 
 36  from status import Status; status = Status() 
 37   
 38  # relax GUI module imports. 
 39  from gui.analyses.base import Base_analysis, Spectral_error_type_page 
 40  from gui.analyses.elements import Spin_ctrl, Text_ctrl 
 41  from gui.analyses.execute import Execute 
 42  from gui.base_classes import Container 
 43  from gui.components.spectrum import Spectra_list 
 44  from gui.filedialog import RelaxDirDialog 
 45  from gui.message import error_message, Missing_data, Question 
 46  from gui.misc import protected_exec 
 47  from gui import paths 
 48  from gui.string_conv import gui_to_int, gui_to_str, int_to_gui, str_to_gui 
 49  from gui.uf_objects import Uf_storage; uf_store = Uf_storage() 
 50  from gui.wizard import Wiz_window 
 51   
 52   
 53   
54 -class Auto_rx(Base_analysis):
55 """The base class for the R1 and R2 frames.""" 56 57 # Hardcoded variables. 58 analysis_type = None 59 bitmap = None 60 label = None 61
62 - def __init__(self, parent, id=-1, pos=wx.Point(-1, -1), size=wx.Size(-1, -1), style=524288, name='scrolledpanel', gui=None, analysis_name=None, pipe_name=None, pipe_bundle=None, data_index=None):
63 """Build the automatic R1 and R2 analysis GUI frame elements. 64 65 @param parent: The parent wx element. 66 @type parent: wx object 67 @keyword id: The unique ID number. 68 @type id: int 69 @keyword pos: The position. 70 @type pos: wx.Size object 71 @keyword size: The size. 72 @type size: wx.Size object 73 @keyword style: The style. 74 @type style: int 75 @keyword name: The name for the panel. 76 @type name: unicode 77 @keyword gui: The main GUI class. 78 @type gui: gui.relax_gui.Main instance 79 @keyword analysis_name: The name of the analysis (the name in the tab part of the notebook). 80 @type analysis_name: str 81 @keyword pipe_name: The name of the data pipe associated with this analysis. 82 @type pipe_name: str 83 @keyword pipe_bundle: The name of the data pipe bundle associated with this analysis. 84 @type pipe_bundle: str 85 @keyword data_index: The index of the analysis in the relax data store (set to None if no data currently exists). 86 @type data_index: None or int 87 """ 88 89 # Store the GUI main class. 90 self.gui = gui 91 92 # Init. 93 self.init_flag = True 94 95 # New data container. 96 if data_index == None: 97 # First create the data pipe if not already in existence. 98 if not has_pipe(pipe_name): 99 self.gui.interpreter.apply('pipe.create', pipe_name=pipe_name, pipe_type='relax_fit', bundle=pipe_bundle) 100 101 # Create the data pipe bundle if needed. 102 if not has_bundle(pipe_bundle): 103 self.gui.interpreter.apply('pipe.bundle', bundle=pipe_bundle, pipe=pipe_name) 104 105 # Generate a storage container in the relax data store, and alias it for easy access. 106 data_index = ds.relax_gui.analyses.add(self.label) 107 108 # Store the analysis and pipe names. 109 ds.relax_gui.analyses[data_index].analysis_name = analysis_name 110 ds.relax_gui.analyses[data_index].pipe_name = pipe_name 111 ds.relax_gui.analyses[data_index].pipe_bundle = pipe_bundle 112 113 # Initialise the variables. 114 ds.relax_gui.analyses[data_index].frq = '' 115 ds.relax_gui.analyses[data_index].grid_inc = None 116 ds.relax_gui.analyses[data_index].mc_sim_num = None 117 ds.relax_gui.analyses[data_index].save_dir = self.gui.launch_dir 118 119 # Alias the data. 120 self.data = ds.relax_gui.analyses[data_index] 121 self.data_index = data_index 122 123 # Register the method for updating the spin count for the completion of user functions. 124 self.observer_register() 125 126 # Execute the base class method to build the panel. 127 super(Auto_rx, self).__init__(parent, id=id, pos=pos, size=size, style=style, name=name)
128 129
130 - def activate(self):
131 """Activate or deactivate certain elements of the analysis in response to the execution lock.""" 132 133 # Flag for enabling or disabling the elements. 134 enable = False 135 if not status.exec_lock.locked(): 136 enable = True 137 138 # Activate or deactivate the elements. 139 wx.CallAfter(self.field_nmr_frq.Enable, enable) 140 wx.CallAfter(self.field_results_dir.Enable, enable) 141 wx.CallAfter(self.spin_systems.Enable, enable) 142 wx.CallAfter(self.peak_intensity.Enable, enable) 143 wx.CallAfter(self.grid_inc.Enable, enable) 144 wx.CallAfter(self.mc_sim_num.Enable, enable) 145 wx.CallAfter(self.button_exec_relax.Enable, enable)
146 147
148 - def assemble_data(self):
149 """Assemble the data required for the auto-analysis. 150 151 See the docstring for auto_analyses.relax_fit for details. All data is taken from the relax data store, so data upload from the GUI to there must have been previously performed. 152 153 @return: A container with all the data required for the auto-analysis. 154 @rtype: class instance, list of str 155 """ 156 157 # The data container. 158 data = Container() 159 missing = [] 160 161 # The pipe name and bundle. 162 data.pipe_name = self.data.pipe_name 163 data.pipe_bundle = self.data.pipe_bundle 164 165 # The frequency. 166 frq = gui_to_str(self.field_nmr_frq.GetValue()) 167 if frq == None: 168 missing.append('NMR frequency') 169 170 # File root. 171 data.file_root = '%s.%s' % (self.analysis_type, frq) 172 173 # Check if sequence data is loaded 174 if not exists_mol_res_spin_data(): 175 missing.append("Sequence data") 176 177 # Spectral data. 178 if not hasattr(cdp, 'spectrum_ids') or len(cdp.spectrum_ids) < 3: 179 missing.append("Spectral data") 180 181 # Increment size. 182 data.inc = gui_to_int(self.grid_inc.GetValue()) 183 184 # The number of Monte Carlo simulations to be used for error analysis at the end of the analysis. 185 data.mc_sim_num = gui_to_int(self.mc_sim_num.GetValue()) 186 187 # Results directory. 188 data.save_dir = self.data.save_dir 189 190 # Return the container and list of missing data. 191 return data, missing
192 193
194 - def build_right_box(self):
195 """Construct the right hand box to pack into the main Rx box. 196 197 @return: The right hand box element containing all Rx GUI elements (excluding the bitmap) to pack into the main Rx box. 198 @rtype: wx.BoxSizer instance 199 """ 200 201 # Use a vertical packing of elements. 202 box = wx.BoxSizer(wx.VERTICAL) 203 204 # Add the frame title. 205 self.add_title(box, "Setup for %s relaxation analysis" % self.label) 206 207 # Display the data pipe. 208 Text_ctrl(box, self, text="The data pipe bundle:", default=self.data.pipe_bundle, tooltip="This is the data pipe bundle associated with this analysis.", editable=False, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal) 209 210 # Add the frequency selection GUI element. 211 self.field_nmr_frq = Text_ctrl(box, self, text="NMR frequency label [MHz]", default=self.data.frq, tooltip="This label is added to the output files. For example if the label is '600', the %s values will be located in the file '%s.600.out'." % (self.label, self.label.lower()), width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal) 212 213 # Add the results directory GUI element. 214 self.field_results_dir = Text_ctrl(box, self, text="Results directory", icon=paths.icon_16x16.open_folder, default=self.data.save_dir, fn=self.results_directory, button=True, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal) 215 216 # Add the spin GUI element. 217 self.add_spin_systems(box, self) 218 219 # Add the peak list selection GUI element, with spacing. 220 box.AddSpacer(20) 221 self.peak_intensity = Spectra_list(gui=self.gui, parent=self, box=box, id=str(self.data_index), fn_add=self.peak_wizard) 222 box.AddSpacer(10) 223 224 # The optimisation settings. 225 self.grid_inc = Spin_ctrl(box, self, text="Grid search increments:", default=21, min=1, max=100, tooltip="This is the number of increments per dimension of the grid search performed prior to numerical optimisation.", width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal) 226 self.mc_sim_num = Spin_ctrl(box, self, text="Monte Carlo simulation number:", default=500, min=1, max=100000, tooltip="This is the number of Monte Carlo simulations performed for error propagation and analysis. For best results, at least 500 is recommended.", width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal) 227 228 # Stretchable spacing (with a minimal space). 229 box.AddSpacer(30) 230 box.AddStretchSpacer() 231 232 # Add the execution GUI element. 233 self.button_exec_relax = self.add_execute_relax(box, self.execute) 234 235 # Return the box. 236 return box
237 238
239 - def delete(self):
240 """Unregister the spin count from the user functions.""" 241 242 # Unregister the observer methods. 243 self.observer_register(remove=True) 244 245 # Clean up the peak intensity object. 246 self.peak_intensity.delete()
247 248
249 - def execute(self, event):
250 """Set up, execute, and process the automatic Rx analysis. 251 252 @param event: The wx event. 253 @type event: wx event 254 """ 255 256 # Flush the GUI interpreter internal queue to make sure all user functions are complete. 257 self.gui.interpreter.flush() 258 259 # relax execution lock. 260 if status.exec_lock.locked(): 261 error_message("relax is currently executing.", "relax execution lock") 262 event.Skip() 263 return 264 265 # User warning to close windows. 266 self.gui.close_windows() 267 268 # Synchronise the frame data to the relax data store. 269 self.sync_ds(upload=True) 270 271 # Assemble all the data needed for the auto-analysis. 272 data, missing = self.assemble_data() 273 274 # Missing data. 275 if len(missing): 276 Missing_data(missing) 277 return 278 279 # Display the relax controller, and go to the end of the log window. 280 self.gui.show_controller(None) 281 self.gui.controller.log_panel.on_goto_end(None) 282 283 # Start the thread. 284 self.thread = Execute_rx(self.gui, data, self.data_index) 285 self.thread.start() 286 287 # Terminate the event. 288 event.Skip()
289 290
291 - def observer_register(self, remove=False):
292 """Register and unregister methods with the observer objects. 293 294 @keyword remove: If set to True, then the methods will be unregistered. 295 @type remove: False 296 """ 297 298 # Register. 299 if not remove: 300 status.observers.gui_uf.register(self.data.pipe_bundle, self.update_spin_count, method_name='update_spin_count') 301 status.observers.exec_lock.register(self.data.pipe_bundle, self.activate, method_name='activate') 302 303 # Unregister. 304 else: 305 # The model-free methods. 306 status.observers.gui_uf.unregister(self.data.pipe_bundle) 307 status.observers.exec_lock.unregister(self.data.pipe_bundle) 308 309 # The embedded objects methods. 310 self.peak_intensity.observer_register(remove=True)
311 312
313 - def peak_wizard(self, event):
314 """Launch the Rx peak loading wizard. 315 316 @param event: The wx event. 317 @type event: wx event 318 """ 319 320 # Change the cursor to busy. 321 wx.BeginBusyCursor() 322 323 # Initialise a wizard. 324 self.wizard = Wiz_window(parent=self.gui, size_x=1000, size_y=750, title="Set up the %s peak intensities" % self.label) 325 self.page_indices = {} 326 327 # First check that at least a single spin is named! 328 if not are_spins_named(): 329 # The message. 330 msg = "No spins have been named. Please use the spin.name user function first, otherwise it is unlikely that any data will be loaded from the peak intensity file.\n\nThis message can be ignored if the generic file format is used and spin names have not been specified. Would you like to name the spins already loaded into the relax data store?" 331 332 # Ask about naming spins, and add the spin.name user function page. 333 if status.show_gui and Question(msg, title="Incomplete setup", size=(450, 250), default=True).ShowModal() == wx.ID_YES: 334 page = uf_store['spin.name'].create_page(self.wizard, sync=True) 335 self.page_indices['read'] = self.wizard.add_page(page, proceed_on_error=False) 336 337 338 # The spectrum.read_intensities page. 339 self.page_intensity = uf_store['spectrum.read_intensities'].create_page(self.wizard, sync=True) 340 self.page_indices['read'] = self.wizard.add_page(self.page_intensity, skip_button=True, proceed_on_error=False) 341 342 # Error type selection page. 343 self.page_error_type = Spectral_error_type_page(parent=self.wizard, height_desc=520) 344 self.page_indices['err_type'] = self.wizard.add_page(self.page_error_type, apply_button=False) 345 self.wizard.set_seq_next_fn(self.page_indices['err_type'], self.wizard_page_after_error_type) 346 347 # The spectrum.replicated page. 348 page = uf_store['spectrum.replicated'].create_page(self.wizard, sync=True) 349 self.page_indices['repl'] = self.wizard.add_page(page, skip_button=True, proceed_on_error=False) 350 self.wizard.set_seq_next_fn(self.page_indices['repl'], self.wizard_page_after_repl) 351 page.on_init = self.wizard_update_repl 352 353 # The spectrum.baseplane_rmsd page. 354 page = uf_store['spectrum.baseplane_rmsd'].create_page(self.wizard, sync=True) 355 self.page_indices['rmsd'] = self.wizard.add_page(page, skip_button=True, proceed_on_error=False) 356 self.wizard.set_seq_next_fn(self.page_indices['rmsd'], self.wizard_page_after_rmsd) 357 page.on_init = self.wizard_update_rmsd 358 359 # The spectrum.integration_points page. 360 page = uf_store['spectrum.integration_points'].create_page(self.wizard, sync=True) 361 self.page_indices['pts'] = self.wizard.add_page(page, skip_button=True, proceed_on_error=False) 362 page.on_init = self.wizard_update_pts 363 364 # The relax_fit.relax_time page. 365 page = uf_store['relax_fit.relax_time'].create_page(self.wizard, sync=True) 366 self.page_indices['relax_time'] = self.wizard.add_page(page, skip_button=False, proceed_on_error=False) 367 page.on_init = self.wizard_update_relax_time 368 369 # Reset the cursor. 370 if wx.IsBusy(): 371 wx.EndBusyCursor() 372 373 # Run the wizard. 374 self.wizard.run()
375 376
377 - def results_directory(self, event):
378 """The results directory selection. 379 380 @param event: The wx event. 381 @type event: wx event 382 """ 383 384 # The dialog. 385 dialog = RelaxDirDialog(parent=self, message='Select the results directory', defaultPath=self.field_results_dir.GetValue()) 386 387 # Show the dialog and catch if no file has been selected. 388 if status.show_gui and dialog.ShowModal() != wx.ID_OK: 389 # Don't do anything. 390 return 391 392 # The path (don't do anything if not set). 393 path = gui_to_str(dialog.get_path()) 394 if not path: 395 return 396 397 # Store the path. 398 self.data.save_dir = path 399 400 # Place the path in the text box. 401 self.field_results_dir.SetValue(str_to_gui(path))
402 403
404 - def sync_ds(self, upload=False):
405 """Synchronise the analysis frame and the relax data store, both ways. 406 407 This method allows the frame information to be uploaded into the relax data store, or for the information in the relax data store to be downloaded by the frame. 408 409 @keyword upload: A flag which if True will cause the frame to send data to the relax data store. If False, data will be downloaded from the relax data store to update the frame. 410 @type upload: bool 411 """ 412 413 # The frequency. 414 if upload: 415 self.data.frq = gui_to_str(self.field_nmr_frq.GetValue()) 416 else: 417 self.field_nmr_frq.SetValue(str_to_gui(self.data.frq)) 418 419 # The grid incs. 420 if upload: 421 self.data.grid_inc = gui_to_int(self.grid_inc.GetValue()) 422 elif hasattr(self.data, 'grid_inc'): 423 self.grid_inc.SetValue(int(self.data.grid_inc)) 424 425 # The MC sim number. 426 if upload: 427 self.data.mc_sim_num = gui_to_int(self.mc_sim_num.GetValue()) 428 elif hasattr(self.data, 'mc_sim_num'): 429 self.mc_sim_num.SetValue(int(self.data.mc_sim_num)) 430 431 # The results directory. 432 if upload: 433 self.data.save_dir = gui_to_str(self.field_results_dir.GetValue()) 434 else: 435 self.field_results_dir.SetValue(str_to_gui(self.data.save_dir))
436 437
439 """Set the page after the error type choice. 440 441 @return: The index of the next page, which is the current page index plus one. 442 @rtype: int 443 """ 444 445 # Go to the spectrum.baseplane_rmsd page. 446 if self.page_error_type.selection == 'rmsd': 447 return self.page_indices['rmsd'] 448 449 # Go to the spectrum.replicated page. 450 elif self.page_error_type.selection == 'repl': 451 return self.page_indices['repl']
452 453
454 - def wizard_page_after_repl(self):
455 """Set the page that comes after the spectrum.replicated page. 456 457 @return: The index of the next page. 458 @rtype: int 459 """ 460 461 # Go to the spectrum.integration_points page. 462 int_method = gui_to_str(self.page_intensity.uf_args['int_method'].GetValue()) 463 if int_method != 'height': 464 return self.page_indices['pts'] 465 466 # Skip to the relax_fit.relax_time page. 467 else: 468 return self.page_indices['relax_time']
469 470
471 - def wizard_page_after_rmsd(self):
472 """Set the page that comes after the spectrum.baseplane_rmsd page. 473 474 @return: The index of the next page. 475 @rtype: int 476 """ 477 478 # Go to the spectrum.integration_points page. 479 int_method = gui_to_str(self.page_intensity.uf_args['int_method'].GetValue()) 480 if int_method != 'height': 481 return self.page_indices['pts'] 482 483 # Skip to the relax_fit.relax_time page. 484 else: 485 return self.page_indices['relax_time']
486 487
488 - def wizard_update_pts(self):
489 """Update the spectrum.replicated page based on previous data.""" 490 491 # The spectrum.read_intensities page. 492 page = self.wizard.get_page(self.page_indices['read']) 493 494 # Set the spectrum ID. 495 id = page.uf_args['spectrum_id'].GetValue() 496 497 # Set the ID in the spectrum.replicated page. 498 page = self.wizard.get_page(self.page_indices['pts']) 499 page.uf_args['spectrum_id'].SetValue(id)
500 501
502 - def wizard_update_repl(self):
503 """Update the spectrum.replicated page based on previous data.""" 504 505 # The spectrum.read_intensities page. 506 page = self.wizard.get_page(self.page_indices['read']) 507 508 # Set the spectrum ID. 509 id = page.uf_args['spectrum_id'].GetValue() 510 511 # Set the ID in the spectrum.replicated page. 512 page = self.wizard.get_page(self.page_indices['repl']) 513 page.uf_args['spectrum_ids'].SetValue(value=id, index=0)
514 515
516 - def wizard_update_rmsd(self):
517 """Update the spectrum.baseplane_rmsd page based on previous data.""" 518 519 # The spectrum.read_intensities page. 520 page = self.wizard.get_page(self.page_indices['read']) 521 522 # Set the spectrum ID. 523 id = page.uf_args['spectrum_id'].GetValue() 524 525 # Set the ID in the spectrum.baseplane_rmsd page. 526 page = self.wizard.get_page(self.page_indices['rmsd']) 527 page.uf_args['spectrum_id'].SetValue(id)
528 529
530 - def wizard_update_relax_time(self):
531 """Update the relax_fit.relax_time page based on previous data.""" 532 533 # The spectrum.read_intensities page. 534 page = self.wizard.get_page(self.page_indices['read']) 535 536 # Set the spectrum ID. 537 id = page.uf_args['spectrum_id'].GetValue() 538 539 # Set the ID in the relax_fit.relax_time page. 540 page = self.wizard.get_page(self.page_indices['relax_time']) 541 page.uf_args['spectrum_id'].SetValue(id)
542 543 544
545 -class Execute_rx(Execute):
546 """The Rx analysis execution object.""" 547
548 - def run_analysis(self):
549 """Execute the calculation.""" 550 551 # Execute. 552 Relax_fit(pipe_name=self.data.pipe_name, pipe_bundle=self.data.pipe_bundle, file_root=self.data.file_root, results_dir=self.data.save_dir, grid_inc=self.data.inc, mc_sim_num=self.data.mc_sim_num, view_plots=False) 553 554 # Alias the relax data store data. 555 data = ds.relax_gui.analyses[self.data_index]
556