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