"Fossies" - the Fresh Open Source Software Archive

Member "relax-5.0.0/gui/analyses/auto_rx_base.py" (2 Dec 2019, 19071 Bytes) of package /linux/privat/relax-5.0.0.src.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "auto_rx_base.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.1.3_vs_5.0.0.

    1 ###############################################################################
    2 #                                                                             #
    3 # Copyright (C) 2009-2011 Michael Bieri                                       #
    4 # Copyright (C) 2009-2015 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 import wx
   28 
   29 # relax module imports.
   30 from auto_analyses.relax_fit import Relax_fit
   31 from data_store import Relax_data_store; ds = Relax_data_store()
   32 from graphics import fetch_icon
   33 from gui.analyses.base import Base_analysis
   34 from gui.analyses.elements.spin_element import Spin_ctrl
   35 from gui.analyses.elements.text_element import Text_ctrl
   36 from gui.analyses.execute import Execute
   37 from gui.base_classes import Container
   38 from gui.components.spectrum import Spectra_list
   39 from gui.filedialog import RelaxDirDialog
   40 from gui.fonts import font
   41 from gui.message import error_message, Missing_data
   42 from gui.string_conv import gui_to_int, gui_to_str, str_to_gui
   43 from gui.uf_objects import Uf_storage; uf_store = Uf_storage()
   44 from gui.wizards.peak_intensity import Peak_intensity_wizard
   45 from pipe_control.mol_res_spin import exists_mol_res_spin_data
   46 from pipe_control.pipes import has_bundle, has_pipe
   47 from status import Status; status = Status()
   48 
   49 
   50 class Auto_rx(Base_analysis):
   51     """The base class for the R1 and R2 frames."""
   52 
   53     # Hardcoded variables.
   54     analysis_type = None
   55     bitmap = None
   56     label = None
   57 
   58     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, uf_exec=[], data_index=None):
   59         """Build the automatic R1 and R2 analysis GUI frame elements.
   60 
   61         @param parent:          The parent wx element.
   62         @type parent:           wx object
   63         @keyword id:            The unique ID number.
   64         @type id:               int
   65         @keyword pos:           The position.
   66         @type pos:              wx.Size object
   67         @keyword size:          The size.
   68         @type size:             wx.Size object
   69         @keyword style:         The style.
   70         @type style:            int
   71         @keyword name:          The name for the panel.
   72         @type name:             unicode
   73         @keyword gui:           The main GUI class.
   74         @type gui:              gui.relax_gui.Main instance
   75         @keyword analysis_name: The name of the analysis (the name in the tab part of the notebook).
   76         @type analysis_name:    str
   77         @keyword pipe_name:     The name of the data pipe associated with this analysis.
   78         @type pipe_name:        str
   79         @keyword pipe_bundle:   The name of the data pipe bundle associated with this analysis.
   80         @type pipe_bundle:      str
   81         @keyword uf_exec:       The list of user function on_execute methods returned from the new analysis wizard.
   82         @type uf_exec:          list of methods
   83         @keyword data_index:    The index of the analysis in the relax data store (set to None if no data currently exists).
   84         @type data_index:       None or int
   85         """
   86 
   87         # Store the GUI main class.
   88         self.gui = gui
   89 
   90         # Init.
   91         self.init_flag = True
   92 
   93         # New data container.
   94         if data_index == None:
   95             # First create the data pipe if not already in existence.
   96             if not has_pipe(pipe_name):
   97                 self.gui.interpreter.apply('pipe.create', pipe_name=pipe_name, pipe_type='relax_fit', bundle=pipe_bundle)
   98 
   99             # Create the data pipe bundle if needed.
  100             if not has_bundle(pipe_bundle):
  101                 self.gui.interpreter.apply('pipe.bundle', bundle=pipe_bundle, pipe=pipe_name)
  102 
  103             # Generate a storage container in the relax data store, and alias it for easy access.
  104             data_index = ds.relax_gui.analyses.add(self.label)
  105 
  106             # Store the analysis and pipe names.
  107             ds.relax_gui.analyses[data_index].analysis_name = analysis_name
  108             ds.relax_gui.analyses[data_index].pipe_name = pipe_name
  109             ds.relax_gui.analyses[data_index].pipe_bundle = pipe_bundle
  110 
  111             # Initialise the variables.
  112             ds.relax_gui.analyses[data_index].frq = ''
  113             ds.relax_gui.analyses[data_index].grid_inc = None
  114             ds.relax_gui.analyses[data_index].mc_sim_num = None
  115             ds.relax_gui.analyses[data_index].save_dir = self.gui.system_cwd_path
  116 
  117         # Alias the data.
  118         self.data = ds.relax_gui.analyses[data_index]
  119         self.data_index = data_index
  120 
  121         # Register the method for updating the spin count for the completion of user functions.
  122         self.observer_register()
  123 
  124         # Execute the base class method to build the panel.
  125         super(Auto_rx, self).__init__(parent, id=id, pos=pos, size=size, style=style, name=name)
  126 
  127 
  128     def activate(self):
  129         """Activate or deactivate certain elements of the analysis in response to the execution lock."""
  130 
  131         # Flag for enabling or disabling the elements.
  132         enable = False
  133         if not status.exec_lock.locked():
  134             enable = True
  135 
  136         # Activate or deactivate the elements.
  137         wx.CallAfter(self.field_nmr_frq.Enable, enable)
  138         wx.CallAfter(self.field_results_dir.Enable, enable)
  139         wx.CallAfter(self.spin_systems.Enable, enable)
  140         wx.CallAfter(self.peak_intensity.Enable, enable)
  141         wx.CallAfter(self.grid_inc.Enable, enable)
  142         wx.CallAfter(self.mc_sim_num.Enable, enable)
  143         wx.CallAfter(self.button_exec_relax.Enable, enable)
  144 
  145 
  146     def add_buttons(self, box):
  147         """Add all of the buttons.
  148 
  149         @param box:     The box element to pack the GUI element into.
  150         @type box:      wx.BoxSizer instance
  151         """
  152 
  153         # Sizer.
  154         sizer = wx.BoxSizer(wx.HORIZONTAL)
  155 
  156         # Isotope type button.
  157         self.button_select_model = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, " Exponential curve model")
  158         self.button_select_model.SetBitmapLabel(wx.Bitmap(fetch_icon("oxygen.actions.list-add", "22x22"), wx.BITMAP_TYPE_ANY))
  159         self.button_select_model.SetFont(font.normal)
  160         self.button_select_model.SetSize((-1, 25))
  161         self.button_select_model.SetToolTip(wx.ToolTip("Select the model for the exponential curve via the relax_fit.select_model user function."))
  162         self.gui.Bind(wx.EVT_BUTTON, self.select_model, self.button_select_model)
  163         sizer.Add(self.button_select_model, 1, wx.ALL|wx.EXPAND, 0)
  164 
  165         # 3 invisible 'buttons' for better button layout.
  166         sizer.AddStretchSpacer()
  167         sizer.AddStretchSpacer()
  168         sizer.AddStretchSpacer()
  169 
  170         # Add the element to the box.
  171         box.Add(sizer, 0, wx.ALL|wx.EXPAND, 0)
  172 
  173 
  174     def assemble_data(self):
  175         """Assemble the data required for the auto-analysis.
  176 
  177         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.
  178 
  179         @return:    A container with all the data required for the auto-analysis.
  180         @rtype:     class instance, list of str
  181         """
  182 
  183         # The data container.
  184         data = Container()
  185         missing = []
  186 
  187         # The pipe name and bundle.
  188         data.pipe_name = self.data.pipe_name
  189         data.pipe_bundle = self.data.pipe_bundle
  190 
  191         # The frequency.
  192         frq = gui_to_str(self.field_nmr_frq.GetValue())
  193         if frq == None:
  194             missing.append('NMR frequency')
  195 
  196         # File root.
  197         data.file_root = '%s.%s' % (self.analysis_type, frq)
  198 
  199         # Check if sequence data is loaded
  200         if not exists_mol_res_spin_data():
  201             missing.append("Sequence data")
  202 
  203         # Spectral data.
  204         if not hasattr(cdp, 'spectrum_ids') or len(cdp.spectrum_ids) < 3:
  205             missing.append("Spectral data")
  206 
  207         # Increment size.
  208         data.inc = gui_to_int(self.grid_inc.GetValue())
  209 
  210         # The number of Monte Carlo simulations to be used for error analysis at the end of the analysis.
  211         data.mc_sim_num = gui_to_int(self.mc_sim_num.GetValue())
  212 
  213         # Results directory.
  214         data.save_dir = self.data.save_dir
  215 
  216         # Return the container and list of missing data.
  217         return data, missing
  218 
  219 
  220     def build_right_box(self):
  221         """Construct the right hand box to pack into the main Rx box.
  222 
  223         @return:    The right hand box element containing all Rx GUI elements (excluding the bitmap) to pack into the main Rx box.
  224         @rtype:     wx.BoxSizer instance
  225         """
  226 
  227         # Use a vertical packing of elements.
  228         box = wx.BoxSizer(wx.VERTICAL)
  229 
  230         # Add the frame title.
  231         self.add_title(box, "%s relaxation analysis" % self.gui_label)
  232 
  233         # Display the data pipe.
  234         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)
  235 
  236         # Add the frequency selection GUI element.
  237         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.gui_label, self.label.lower()), width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)
  238 
  239         # Add the results directory GUI element.
  240         self.field_results_dir = Text_ctrl(box, self, text="Results directory:", icon=fetch_icon('oxygen.actions.document-open-folder', "16x16"), default=self.data.save_dir, tooltip="The directory in which all automatically created files will be saved.", tooltip_button="Select the results directory.", fn=self.results_directory, button=True, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)
  241 
  242         # Add the spin GUI element.
  243         self.add_spin_systems(box, self)
  244 
  245         # Add the peak list wizard and selection GUI element, with spacing.
  246         box.AddSpacer(20)
  247         self.peak_intensity = Spectra_list(gui=self.gui, parent=self, box=box, id=str(self.data_index), fn_add=self.peak_wizard_launch, relax_fit_flag=True)
  248         box.AddSpacer(10)
  249 
  250         # Add the buttons, with spacing.
  251         box.AddSpacer(10)
  252         self.add_buttons(box=box)
  253         box.AddSpacer(10)
  254 
  255         # The optimisation settings.
  256         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)
  257         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)
  258 
  259         # Stretchable spacing (with a minimal space).
  260         box.AddSpacer(30)
  261         box.AddStretchSpacer()
  262 
  263         # Add the execution GUI element.
  264         self.button_exec_relax = self.add_execute_analysis(box, self.execute)
  265 
  266         # Return the box.
  267         return box
  268 
  269 
  270     def delete(self):
  271         """Unregister the spin count from the user functions."""
  272 
  273         # Unregister the observer methods.
  274         self.observer_register(remove=True)
  275 
  276         # Clean up the peak intensity object.
  277         self.peak_intensity.delete()
  278 
  279         # Destroy the peak intensity wizard, if it exists.
  280         if hasattr(self, 'peak_wizard'):
  281             self.peak_wizard.Destroy()
  282             del self.peak_wizard
  283 
  284         # Destroy the missing data dialog, if present.
  285         if hasattr(self, 'missing_data'):
  286             self.missing_data.Destroy()
  287             del self.missing_data
  288 
  289 
  290     def execute(self, event):
  291         """Set up, execute, and process the automatic Rx analysis.
  292 
  293         @param event:   The wx event.
  294         @type event:    wx event
  295         """
  296 
  297         # Flush the GUI interpreter internal queue to make sure all user functions are complete.
  298         self.gui.interpreter.flush()
  299 
  300         # relax execution lock.
  301         if status.exec_lock.locked():
  302             error_message("relax is currently executing.", "relax execution lock")
  303             event.Skip()
  304             return
  305 
  306         # User warning to close windows.
  307         self.gui.close_windows()
  308 
  309         # Synchronise the frame data to the relax data store.
  310         self.sync_ds(upload=True)
  311 
  312         # Assemble all the data needed for the auto-analysis.
  313         data, missing = self.assemble_data()
  314 
  315         # Missing data.
  316         if len(missing):
  317             self.missing_data = Missing_data(missing)
  318             return
  319 
  320         # Display the relax controller, and go to the end of the log window.
  321         self.gui.show_controller(None)
  322         self.gui.controller.log_panel.on_goto_end(None)
  323 
  324         # Start the thread.
  325         self.thread = Execute_rx(self.gui, data, self.data_index)
  326         self.thread.start()
  327 
  328         # Terminate the event.
  329         event.Skip()
  330 
  331 
  332     def observer_register(self, remove=False):
  333         """Register and unregister methods with the observer objects.
  334 
  335         @keyword remove:    If set to True, then the methods will be unregistered.
  336         @type remove:       False
  337         """
  338 
  339         # Register.
  340         if not remove:
  341             status.observers.gui_uf.register(self.data.pipe_bundle, self.update_spin_count, method_name='update_spin_count')
  342             status.observers.exec_lock.register(self.data.pipe_bundle, self.activate, method_name='activate')
  343 
  344         # Unregister.
  345         else:
  346             # The model-free methods.
  347             status.observers.gui_uf.unregister(self.data.pipe_bundle)
  348             status.observers.exec_lock.unregister(self.data.pipe_bundle)
  349 
  350             # The embedded objects methods.
  351             self.peak_intensity.observer_register(remove=True)
  352 
  353 
  354     def peak_wizard_launch(self, event):
  355         """Launch the peak loading wizard.
  356 
  357         @param event:   The wx event.
  358         @type event:    wx event
  359         """
  360 
  361         # Destroy the peak intensity wizard, if it exists.
  362         if hasattr(self, 'peak_wizard'):
  363             self.peak_wizard.Destroy()
  364 
  365         # A new wizard instance.
  366         self.peak_wizard = Peak_intensity_wizard(relax_fit=True)
  367 
  368 
  369     def results_directory(self, event):
  370         """The results directory selection.
  371 
  372         @param event:   The wx event.
  373         @type event:    wx event
  374         """
  375 
  376         # The dialog.
  377         dialog = RelaxDirDialog(parent=self, message='Select the results directory', defaultPath=self.field_results_dir.GetValue())
  378 
  379         # Show the dialog and catch if no file has been selected.
  380         if status.show_gui and dialog.ShowModal() != wx.ID_OK:
  381             # Don't do anything.
  382             return
  383 
  384         # The path (don't do anything if not set).
  385         path = gui_to_str(dialog.get_path())
  386         if not path:
  387             return
  388 
  389         # Store the path.
  390         self.data.save_dir = path
  391 
  392         # Place the path in the text box.
  393         self.field_results_dir.SetValue(str_to_gui(path))
  394 
  395 
  396     def select_model(self, event=None):
  397         """Launch the relax_fit.select_model user function.
  398 
  399         @keyword event: The wx event.
  400         @type event:    wx event
  401         """
  402 
  403         # Call the user function.
  404         uf_store['relax_fit.select_model'](wx_wizard_modal=True)
  405 
  406 
  407     def sync_ds(self, upload=False):
  408         """Synchronise the analysis frame and the relax data store, both ways.
  409 
  410         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.
  411 
  412         @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.
  413         @type upload:       bool
  414         """
  415 
  416         # The frequency.
  417         if upload:
  418             self.data.frq = gui_to_str(self.field_nmr_frq.GetValue())
  419         else:
  420             self.field_nmr_frq.SetValue(str_to_gui(self.data.frq))
  421 
  422         # The grid incs.
  423         if upload:
  424             self.data.grid_inc = gui_to_int(self.grid_inc.GetValue())
  425         elif hasattr(self.data, 'grid_inc'):
  426             self.grid_inc.SetValue(int(self.data.grid_inc))
  427 
  428         # The MC sim number.
  429         if upload:
  430             self.data.mc_sim_num = gui_to_int(self.mc_sim_num.GetValue())
  431         elif hasattr(self.data, 'mc_sim_num'):
  432             self.mc_sim_num.SetValue(int(self.data.mc_sim_num))
  433 
  434         # The results directory.
  435         if upload:
  436             self.data.save_dir = gui_to_str(self.field_results_dir.GetValue())
  437         else:
  438             self.field_results_dir.SetValue(str_to_gui(self.data.save_dir))
  439 
  440 
  441 
  442 class Execute_rx(Execute):
  443     """The Rx analysis execution object."""
  444 
  445     def run_analysis(self):
  446         """Execute the calculation."""
  447 
  448         # Execute.
  449         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)
  450 
  451         # Alias the relax data store data.
  452         data = ds.relax_gui.analyses[self.data_index]