Ejemplo n.º 1
0
    def build_right_box(self):
        """Construct the right hand box to pack into the main model-free box.

        @return:    The right hand box element containing all model-free GUI elements (excluding the bitmap) to pack into the main model-free box.
        @rtype:     wx.BoxSizer instance
        """

        # Use a vertical packing of elements.
        box = wx.BoxSizer(wx.VERTICAL)

        # Add the frame title.
        self.add_title(box, "Model-free analysis")

        # Display the data pipe.
        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)

        # Add the results directory GUI element.
        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)

        # Add the spin GUI element.
        self.add_spin_systems(box, self)

        # Add the relaxation data list GUI element, with spacing.
        box.AddSpacer(10)
        self.relax_data = Relax_data_list(gui=self.gui, parent=self, box=box, id=str(self.data_index))
        box.AddSpacer(10)

        # Add the value.set buttons.
        self.add_values(box)
        box.AddSpacer(10)

        # Add the local tau_m models GUI element, with spacing.
        self.local_tm_model_field = Local_tm_list(self, box)
        self.local_tm_model_field.set_value(self.data.local_tm_models)

        # Add the model-free models GUI element, with spacing.
        self.mf_model_field = Mf_list(self, box)
        self.mf_model_field.set_value(self.data.mf_models)

        # The optimisation settings.
        self.grid_inc = Spin_ctrl(box, self, text="Grid search increments:", default=11, 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)
        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.", width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)

        # Add maximum iteration selector.
        self.max_iter = Spin_ctrl(box, self, text="Maximum iterations:", default=self.data.max_iter, tooltip="The maximum number of iterations for the protocol.  This is the limit for the global looping over the optimisation of the model-free models, model elimination, model selection and then optimisation of the diffusion tensor.", min=25, max=100, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)

        # The calculation mode.
        self.mode = Text_ctrl(box, self, text="Protocol mode:", default='Fully automated', tooltip="Select if the dauvergne_protocol analysis will be fully automated or whether the individual global models will be optimised separately.", tooltip_button="Open the protocol mode selection window.", icon=fetch_icon('oxygen.actions.system-run', "16x16"), fn=self.mode_dialog, editable=False, button=True, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)

        # Stretchable spacing (with a minimal space).
        box.AddSpacer(30)
        box.AddStretchSpacer()

        # Add the execution GUI element.
        self.button_exec_relax = self.add_execute_analysis(box, self.execute)

        # Return the box.
        return box
Ejemplo n.º 2
0
    def add_spin_systems(self, box, parent):
        """Add a special control for spin systems.

        Only one of these per analysis are allowed.

        @param box:         The box element to pack the control into.
        @type box:          wx.BoxSizer instance
        @param parent:      The parent GUI element.
        @type parent:       wx object
        """

        # Add the element.
        self.spin_systems = Text_ctrl(
            box,
            self,
            text="Spin systems:",
            button_text=" Spin editor",
            default=self.spin_count(),
            tooltip="The currently loaded molecule, residue and spin sequence.",
            tooltip_button=
            "Launch the spin editor window for modifying the molecule, residue and spin sequence.",
            icon=fetch_icon('relax.spin', "16x16"),
            fn=self.launch_spin_editor,
            editable=False,
            button=True,
            width_text=self.width_text,
            width_button=self.width_button,
            spacer=self.spacer_horizontal)
Ejemplo n.º 3
0
class Auto_rx(Base_analysis):
    """The base class for the R1 and R2 frames."""

    # Hardcoded variables.
    analysis_type = None
    bitmap = None
    label = None

    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):
        """Build the automatic R1 and R2 analysis GUI frame elements.

        @param parent:          The parent wx element.
        @type parent:           wx object
        @keyword id:            The unique ID number.
        @type id:               int
        @keyword pos:           The position.
        @type pos:              wx.Size object
        @keyword size:          The size.
        @type size:             wx.Size object
        @keyword style:         The style.
        @type style:            int
        @keyword name:          The name for the panel.
        @type name:             unicode
        @keyword gui:           The main GUI class.
        @type gui:              gui.relax_gui.Main instance
        @keyword analysis_name: The name of the analysis (the name in the tab part of the notebook).
        @type analysis_name:    str
        @keyword pipe_name:     The name of the data pipe associated with this analysis.
        @type pipe_name:        str
        @keyword pipe_bundle:   The name of the data pipe bundle associated with this analysis.
        @type pipe_bundle:      str
        @keyword uf_exec:       The list of user function on_execute methods returned from the new analysis wizard.
        @type uf_exec:          list of methods
        @keyword data_index:    The index of the analysis in the relax data store (set to None if no data currently exists).
        @type data_index:       None or int
        """

        # Store the GUI main class.
        self.gui = gui

        # Init.
        self.init_flag = True

        # New data container.
        if data_index == None:
            # First create the data pipe if not already in existence.
            if not has_pipe(pipe_name):
                self.gui.interpreter.apply('pipe.create',
                                           pipe_name=pipe_name,
                                           pipe_type='relax_fit',
                                           bundle=pipe_bundle)

            # Create the data pipe bundle if needed.
            if not has_bundle(pipe_bundle):
                self.gui.interpreter.apply('pipe.bundle',
                                           bundle=pipe_bundle,
                                           pipe=pipe_name)

            # Generate a storage container in the relax data store, and alias it for easy access.
            data_index = ds.relax_gui.analyses.add(self.label)

            # Store the analysis and pipe names.
            ds.relax_gui.analyses[data_index].analysis_name = analysis_name
            ds.relax_gui.analyses[data_index].pipe_name = pipe_name
            ds.relax_gui.analyses[data_index].pipe_bundle = pipe_bundle

            # Initialise the variables.
            ds.relax_gui.analyses[data_index].frq = ''
            ds.relax_gui.analyses[data_index].grid_inc = None
            ds.relax_gui.analyses[data_index].mc_sim_num = None
            ds.relax_gui.analyses[
                data_index].save_dir = self.gui.system_cwd_path

        # Alias the data.
        self.data = ds.relax_gui.analyses[data_index]
        self.data_index = data_index

        # Register the method for updating the spin count for the completion of user functions.
        self.observer_register()

        # Execute the base class method to build the panel.
        super(Auto_rx, self).__init__(parent,
                                      id=id,
                                      pos=pos,
                                      size=size,
                                      style=style,
                                      name=name)

    def activate(self):
        """Activate or deactivate certain elements of the analysis in response to the execution lock."""

        # Flag for enabling or disabling the elements.
        enable = False
        if not status.exec_lock.locked():
            enable = True

        # Activate or deactivate the elements.
        wx.CallAfter(self.field_nmr_frq.Enable, enable)
        wx.CallAfter(self.field_results_dir.Enable, enable)
        wx.CallAfter(self.spin_systems.Enable, enable)
        wx.CallAfter(self.peak_intensity.Enable, enable)
        wx.CallAfter(self.grid_inc.Enable, enable)
        wx.CallAfter(self.mc_sim_num.Enable, enable)
        wx.CallAfter(self.button_exec_relax.Enable, enable)

    def add_buttons(self, box):
        """Add all of the buttons.

        @param box:     The box element to pack the GUI element into.
        @type box:      wx.BoxSizer instance
        """

        # Sizer.
        sizer = wx.BoxSizer(wx.HORIZONTAL)

        # Isotope type button.
        self.button_select_model = wx.lib.buttons.ThemedGenBitmapTextButton(
            self, -1, None, " Exponential curve model")
        self.button_select_model.SetBitmapLabel(
            wx.Bitmap(fetch_icon("oxygen.actions.list-add", "22x22"),
                      wx.BITMAP_TYPE_ANY))
        self.button_select_model.SetFont(font.normal)
        self.button_select_model.SetSize((-1, 25))
        self.button_select_model.SetToolTip(
            wx.ToolTip(
                "Select the model for the exponential curve via the relax_fit.select_model user function."
            ))
        self.gui.Bind(wx.EVT_BUTTON, self.select_model,
                      self.button_select_model)
        sizer.Add(self.button_select_model, 1, wx.ALL | wx.EXPAND, 0)

        # 3 invisible 'buttons' for better button layout.
        sizer.AddStretchSpacer()
        sizer.AddStretchSpacer()
        sizer.AddStretchSpacer()

        # Add the element to the box.
        box.Add(sizer, 0, wx.ALL | wx.EXPAND, 0)

    def assemble_data(self):
        """Assemble the data required for the auto-analysis.

        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.

        @return:    A container with all the data required for the auto-analysis.
        @rtype:     class instance, list of str
        """

        # The data container.
        data = Container()
        missing = []

        # The pipe name and bundle.
        data.pipe_name = self.data.pipe_name
        data.pipe_bundle = self.data.pipe_bundle

        # The frequency.
        frq = gui_to_str(self.field_nmr_frq.GetValue())
        if frq == None:
            missing.append('NMR frequency')

        # File root.
        data.file_root = '%s.%s' % (self.analysis_type, frq)

        # Check if sequence data is loaded
        if not exists_mol_res_spin_data():
            missing.append("Sequence data")

        # Spectral data.
        if not hasattr(cdp, 'spectrum_ids') or len(cdp.spectrum_ids) < 3:
            missing.append("Spectral data")

        # Increment size.
        data.inc = gui_to_int(self.grid_inc.GetValue())

        # The number of Monte Carlo simulations to be used for error analysis at the end of the analysis.
        data.mc_sim_num = gui_to_int(self.mc_sim_num.GetValue())

        # Results directory.
        data.save_dir = self.data.save_dir

        # Return the container and list of missing data.
        return data, missing

    def build_right_box(self):
        """Construct the right hand box to pack into the main Rx box.

        @return:    The right hand box element containing all Rx GUI elements (excluding the bitmap) to pack into the main Rx box.
        @rtype:     wx.BoxSizer instance
        """

        # Use a vertical packing of elements.
        box = wx.BoxSizer(wx.VERTICAL)

        # Add the frame title.
        self.add_title(box, "%s relaxation analysis" % self.gui_label)

        # Display the data pipe.
        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)

        # Add the frequency selection GUI element.
        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)

        # Add the results directory GUI element.
        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)

        # Add the spin GUI element.
        self.add_spin_systems(box, self)

        # Add the peak list wizard and selection GUI element, with spacing.
        box.AddSpacer(20)
        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)
        box.AddSpacer(10)

        # Add the buttons, with spacing.
        box.AddSpacer(10)
        self.add_buttons(box=box)
        box.AddSpacer(10)

        # The optimisation settings.
        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)
        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)

        # Stretchable spacing (with a minimal space).
        box.AddSpacer(30)
        box.AddStretchSpacer()

        # Add the execution GUI element.
        self.button_exec_relax = self.add_execute_analysis(box, self.execute)

        # Return the box.
        return box

    def delete(self):
        """Unregister the spin count from the user functions."""

        # Unregister the observer methods.
        self.observer_register(remove=True)

        # Clean up the peak intensity object.
        self.peak_intensity.delete()

        # Destroy the peak intensity wizard, if it exists.
        if hasattr(self, 'peak_wizard'):
            self.peak_wizard.Destroy()
            del self.peak_wizard

        # Destroy the missing data dialog, if present.
        if hasattr(self, 'missing_data'):
            self.missing_data.Destroy()
            del self.missing_data

    def execute(self, event):
        """Set up, execute, and process the automatic Rx analysis.

        @param event:   The wx event.
        @type event:    wx event
        """

        # Flush the GUI interpreter internal queue to make sure all user functions are complete.
        self.gui.interpreter.flush()

        # relax execution lock.
        if status.exec_lock.locked():
            error_message("relax is currently executing.",
                          "relax execution lock")
            event.Skip()
            return

        # User warning to close windows.
        self.gui.close_windows()

        # Synchronise the frame data to the relax data store.
        self.sync_ds(upload=True)

        # Assemble all the data needed for the auto-analysis.
        data, missing = self.assemble_data()

        # Missing data.
        if len(missing):
            self.missing_data = Missing_data(missing)
            return

        # Display the relax controller, and go to the end of the log window.
        self.gui.show_controller(None)
        self.gui.controller.log_panel.on_goto_end(None)

        # Start the thread.
        self.thread = Execute_rx(self.gui, data, self.data_index)
        self.thread.start()

        # Terminate the event.
        event.Skip()

    def observer_register(self, remove=False):
        """Register and unregister methods with the observer objects.

        @keyword remove:    If set to True, then the methods will be unregistered.
        @type remove:       False
        """

        # Register.
        if not remove:
            status.observers.gui_uf.register(self.data.pipe_bundle,
                                             self.update_spin_count,
                                             method_name='update_spin_count')
            status.observers.exec_lock.register(self.data.pipe_bundle,
                                                self.activate,
                                                method_name='activate')

        # Unregister.
        else:
            # The model-free methods.
            status.observers.gui_uf.unregister(self.data.pipe_bundle)
            status.observers.exec_lock.unregister(self.data.pipe_bundle)

            # The embedded objects methods.
            self.peak_intensity.observer_register(remove=True)

    def peak_wizard_launch(self, event):
        """Launch the peak loading wizard.

        @param event:   The wx event.
        @type event:    wx event
        """

        # Destroy the peak intensity wizard, if it exists.
        if hasattr(self, 'peak_wizard'):
            self.peak_wizard.Destroy()

        # A new wizard instance.
        self.peak_wizard = Peak_intensity_wizard(relax_fit=True)

    def results_directory(self, event):
        """The results directory selection.

        @param event:   The wx event.
        @type event:    wx event
        """

        # The dialog.
        dialog = RelaxDirDialog(parent=self,
                                message='Select the results directory',
                                defaultPath=self.field_results_dir.GetValue())

        # Show the dialog and catch if no file has been selected.
        if status.show_gui and dialog.ShowModal() != wx.ID_OK:
            # Don't do anything.
            return

        # The path (don't do anything if not set).
        path = gui_to_str(dialog.get_path())
        if not path:
            return

        # Store the path.
        self.data.save_dir = path

        # Place the path in the text box.
        self.field_results_dir.SetValue(str_to_gui(path))

    def select_model(self, event=None):
        """Launch the relax_fit.select_model user function.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Call the user function.
        uf_store['relax_fit.select_model'](wx_wizard_modal=True)

    def sync_ds(self, upload=False):
        """Synchronise the analysis frame and the relax data store, both ways.

        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.

        @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.
        @type upload:       bool
        """

        # The frequency.
        if upload:
            self.data.frq = gui_to_str(self.field_nmr_frq.GetValue())
        else:
            self.field_nmr_frq.SetValue(str_to_gui(self.data.frq))

        # The grid incs.
        if upload:
            self.data.grid_inc = gui_to_int(self.grid_inc.GetValue())
        elif hasattr(self.data, 'grid_inc'):
            self.grid_inc.SetValue(int(self.data.grid_inc))

        # The MC sim number.
        if upload:
            self.data.mc_sim_num = gui_to_int(self.mc_sim_num.GetValue())
        elif hasattr(self.data, 'mc_sim_num'):
            self.mc_sim_num.SetValue(int(self.data.mc_sim_num))

        # The results directory.
        if upload:
            self.data.save_dir = gui_to_str(self.field_results_dir.GetValue())
        else:
            self.field_results_dir.SetValue(str_to_gui(self.data.save_dir))
Ejemplo n.º 4
0
    def build_right_box(self):
        """Construct the right hand box to pack into the main Rx box.

        @return:    The right hand box element containing all Rx GUI elements (excluding the bitmap) to pack into the main Rx box.
        @rtype:     wx.BoxSizer instance
        """

        # Use a vertical packing of elements.
        box = wx.BoxSizer(wx.VERTICAL)

        # Add the frame title.
        self.add_title(box, "%s relaxation analysis" % self.gui_label)

        # Display the data pipe.
        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)

        # Add the frequency selection GUI element.
        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)

        # Add the results directory GUI element.
        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)

        # Add the spin GUI element.
        self.add_spin_systems(box, self)

        # Add the peak list wizard and selection GUI element, with spacing.
        box.AddSpacer(20)
        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)
        box.AddSpacer(10)

        # Add the buttons, with spacing.
        box.AddSpacer(10)
        self.add_buttons(box=box)
        box.AddSpacer(10)

        # The optimisation settings.
        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)
        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)

        # Stretchable spacing (with a minimal space).
        box.AddSpacer(30)
        box.AddStretchSpacer()

        # Add the execution GUI element.
        self.button_exec_relax = self.add_execute_analysis(box, self.execute)

        # Return the box.
        return box
Ejemplo n.º 5
0
class Auto_noe(Base_analysis):
    """The base class for the noe frames."""

    # Hardcoded variables.
    analysis_type = None
    bitmap = [ANALYSIS_IMAGE_PATH + "noe_200x200.png", IMAGE_PATH + 'noe.png']
    label = None

    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):
        """Build the automatic NOE analysis GUI frame elements.

        @param parent:          The parent wx element.
        @type parent:           wx object
        @keyword id:            The unique ID number.
        @type id:               int
        @keyword pos:           The position.
        @type pos:              wx.Size object
        @keyword size:          The size.
        @type size:             wx.Size object
        @keyword style:         The style.
        @type style:            int
        @keyword name:          The name for the panel.
        @type name:             unicode
        @keyword gui:           The main GUI class.
        @type gui:              gui.relax_gui.Main instance
        @keyword analysis_name: The name of the analysis (the name in the tab part of the notebook).
        @type analysis_name:    str
        @keyword pipe_name:     The name of the data pipe associated with this analysis.
        @type pipe_name:        str
        @keyword pipe_bundle:   The name of the data pipe bundle associated with this analysis.
        @type pipe_bundle:      str
        @keyword uf_exec:       The list of user function on_execute methods returned from the new analysis wizard.
        @type uf_exec:          list of methods
        @keyword data_index:    The index of the analysis in the relax data store (set to None if no data currently exists).
        @type data_index:       None or int
        """

        # Store the GUI main class.
        self.gui = gui

        # Init.
        self.init_flag = True

        # New data container.
        if data_index == None:
            # First create the data pipe if not already in existence.
            if not has_pipe(pipe_name):
                self.gui.interpreter.apply('pipe.create',
                                           pipe_name=pipe_name,
                                           pipe_type='noe',
                                           bundle=pipe_bundle)

            # Create the data pipe bundle if needed.
            if not has_bundle(pipe_bundle):
                self.gui.interpreter.apply('pipe.bundle',
                                           bundle=pipe_bundle,
                                           pipe=pipe_name)

            # Generate a storage container in the relax data store, and alias it for easy access.
            data_index = ds.relax_gui.analyses.add('NOE')

            # Store the analysis and pipe names.
            ds.relax_gui.analyses[data_index].analysis_name = analysis_name
            ds.relax_gui.analyses[data_index].pipe_name = pipe_name
            ds.relax_gui.analyses[data_index].pipe_bundle = pipe_bundle

            # Initialise the variables.
            ds.relax_gui.analyses[data_index].frq = ''
            ds.relax_gui.analyses[
                data_index].save_dir = self.gui.system_cwd_path

        # Alias the data.
        self.data = ds.relax_gui.analyses[data_index]
        self.data_index = data_index

        # Register the method for updating the spin count for the completion of user functions.
        self.observer_register()

        # Execute the base class method to build the panel.
        super(Auto_noe, self).__init__(parent,
                                       id=id,
                                       pos=pos,
                                       size=size,
                                       style=style,
                                       name=name)

    def activate(self):
        """Activate or deactivate certain elements of the analysis in response to the execution lock."""

        # Flag for enabling or disabling the elements.
        enable = False
        if not status.exec_lock.locked():
            enable = True

        # Activate or deactivate the elements.
        wx.CallAfter(self.field_nmr_frq.Enable, enable)
        wx.CallAfter(self.field_results_dir.Enable, enable)
        wx.CallAfter(self.spin_systems.Enable, enable)
        wx.CallAfter(self.peak_intensity.Enable, enable)
        wx.CallAfter(self.button_exec_relax.Enable, enable)

    def assemble_data(self):
        """Assemble the data required for the Auto_noe class.

        @return:    A container with all the data required for the auto-analysis.
        @rtype:     class instance, list of str
        """

        # The data container.
        data = Container()
        missing = []

        # The pipe name and bundle.
        data.pipe_name = self.data.pipe_name
        data.pipe_bundle = self.data.pipe_bundle

        # The frequency.
        frq = gui_to_str(self.field_nmr_frq.GetValue())
        if frq == None:
            missing.append('NMR frequency')

        # Filename.
        data.file_root = 'noe.%s' % frq

        # Results directory.
        data.save_dir = self.data.save_dir

        # Check if sequence data is loaded
        if not exists_mol_res_spin_data():
            missing.append("Sequence data")

        # Spectral data.
        if not hasattr(cdp, 'spectrum_ids') or len(cdp.spectrum_ids) < 2:
            missing.append("Spectral data")

        # Return the container and list of missing data.
        return data, missing

    def build_right_box(self):
        """Construct the right hand box to pack into the main NOE box.

        @return:    The right hand box element containing all NOE GUI elements (excluding the bitmap) to pack into the main Rx box.
        @rtype:     wx.BoxSizer instance
        """

        # Use a vertical packing of elements.
        box = wx.BoxSizer(wx.VERTICAL)

        # Add the frame title.
        self.add_title(box, "Steady-state NOE analysis")

        # Display the data pipe.
        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)

        # Add the frequency selection GUI element.
        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 NOE values will be located in the file 'noe.600.out'.",
            width_text=self.width_text,
            width_button=self.width_button,
            spacer=self.spacer_horizontal)

        # Add the results directory GUI element.
        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)

        # Add the spin GUI element.
        self.add_spin_systems(box, self)

        # Add the peak list selection GUI element, with spacing.
        box.AddSpacer(40)
        self.peak_intensity = Spectra_list(gui=self.gui,
                                           parent=self,
                                           box=box,
                                           id=str(self.data_index),
                                           fn_add=self.peak_wizard_launch,
                                           noe_flag=True)

        # Stretchable spacing (with a minimal space).
        box.AddSpacer(30)
        box.AddStretchSpacer()

        # Add the execution GUI element.
        self.button_exec_relax = self.add_execute_analysis(box, self.execute)

        # Return the box.
        return box

    def delete(self):
        """Unregister the spin count from the user functions."""

        # Unregister the observer methods.
        self.observer_register(remove=True)

        # Clean up the peak intensity object.
        self.peak_intensity.delete()

        # Destroy the peak intensity wizard, if it exists.
        if hasattr(self, 'peak_wizard'):
            self.peak_wizard.Destroy()
            del self.peak_wizard

        # Destroy the missing data dialog, if present.
        if hasattr(self, 'missing_data'):
            self.missing_data.Destroy()
            del self.missing_data

    def execute(self, event):
        """Set up, execute, and process the automatic Rx analysis.

        @param event:   The wx event.
        @type event:    wx event
        """

        # Flush the GUI interpreter internal queue to make sure all user functions are complete.
        self.gui.interpreter.flush()

        # relax execution lock.
        if status.exec_lock.locked():
            error_message("relax is currently executing.",
                          "relax execution lock")
            event.Skip()
            return

        # User warning to close windows.
        self.gui.close_windows()

        # Synchronise the frame data to the relax data store.
        self.sync_ds(upload=True)

        # Assemble all the data needed for the auto-analysis.
        data, missing = self.assemble_data()

        # Missing data.
        if len(missing):
            self.missing_data = Missing_data(missing)
            return

        # Display the relax controller, and go to the end of the log window.
        self.gui.show_controller(None)
        self.gui.controller.log_panel.on_goto_end(None)

        # Start the thread.
        self.thread = Execute_noe(self.gui, data, self.data_index)
        self.thread.start()

        # Terminate the event.
        event.Skip()

    def observer_register(self, remove=False):
        """Register and unregister methods with the observer objects.

        @keyword remove:    If set to True, then the methods will be unregistered.
        @type remove:       False
        """

        # Register.
        if not remove:
            status.observers.gui_uf.register(self.data.pipe_bundle,
                                             self.update_spin_count,
                                             method_name='update_spin_count')
            status.observers.exec_lock.register(self.data.pipe_bundle,
                                                self.activate,
                                                method_name='activate')

        # Unregister.
        else:
            # The model-free methods.
            status.observers.gui_uf.unregister(self.data.pipe_bundle)
            status.observers.exec_lock.unregister(self.data.pipe_bundle)

            # The embedded objects methods.
            self.peak_intensity.observer_register(remove=True)

    def peak_wizard_launch(self, event):
        """Launch the peak loading wizard.

        @param event:   The wx event.
        @type event:    wx event
        """

        # Destroy the peak intensity wizard, if it exists.
        if hasattr(self, 'peak_wizard'):
            self.peak_wizard.Destroy()

        # A new wizard instance.
        self.peak_wizard = Peak_intensity_wizard(noe=True)

    def results_directory(self, event):
        """The results directory selection.

        @param event:   The wx event.
        @type event:    wx event
        """

        # The dialog.
        dialog = RelaxDirDialog(parent=self,
                                message='Select the results directory',
                                defaultPath=self.field_results_dir.GetValue())

        # Show the dialog and catch if no file has been selected.
        if status.show_gui and dialog.ShowModal() != wx.ID_OK:
            # Don't do anything.
            return

        # The path (don't do anything if not set).
        path = gui_to_str(dialog.get_path())
        if not path:
            return

        # Store the path.
        self.data.save_dir = path

        # Place the path in the text box.
        self.field_results_dir.SetValue(str_to_gui(path))

    def sync_ds(self, upload=False):
        """Synchronise the noe analysis frame and the relax data store, both ways.

        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.

        @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.
        @type upload:       bool
        """

        # The frequency.
        if upload:
            self.data.frq = gui_to_str(self.field_nmr_frq.GetValue())
        else:
            self.field_nmr_frq.SetValue(str_to_gui(self.data.frq))

        # The results directory.
        if upload:
            self.data.save_dir = gui_to_str(self.field_results_dir.GetValue())
        else:
            self.field_results_dir.SetValue(str_to_gui(self.data.save_dir))
Ejemplo n.º 6
0
    def build_right_box(self):
        """Construct the right hand box to pack into the main NOE box.

        @return:    The right hand box element containing all NOE GUI elements (excluding the bitmap) to pack into the main Rx box.
        @rtype:     wx.BoxSizer instance
        """

        # Use a vertical packing of elements.
        box = wx.BoxSizer(wx.VERTICAL)

        # Add the frame title.
        self.add_title(box, "Steady-state NOE analysis")

        # Display the data pipe.
        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)

        # Add the frequency selection GUI element.
        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 NOE values will be located in the file 'noe.600.out'.",
            width_text=self.width_text,
            width_button=self.width_button,
            spacer=self.spacer_horizontal)

        # Add the results directory GUI element.
        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)

        # Add the spin GUI element.
        self.add_spin_systems(box, self)

        # Add the peak list selection GUI element, with spacing.
        box.AddSpacer(40)
        self.peak_intensity = Spectra_list(gui=self.gui,
                                           parent=self,
                                           box=box,
                                           id=str(self.data_index),
                                           fn_add=self.peak_wizard_launch,
                                           noe_flag=True)

        # Stretchable spacing (with a minimal space).
        box.AddSpacer(30)
        box.AddStretchSpacer()

        # Add the execution GUI element.
        self.button_exec_relax = self.add_execute_analysis(box, self.execute)

        # Return the box.
        return box
Ejemplo n.º 7
0
class Auto_model_free(Base_analysis):
    """The model-free auto-analysis GUI element."""
    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):
        """Build the automatic model-free protocol GUI element.

        @param parent:          The parent wx element.
        @type parent:           wx object
        @keyword id:            The unique ID number.
        @type id:               int
        @keyword pos:           The position.
        @type pos:              wx.Size object
        @keyword size:          The size.
        @type size:             wx.Size object
        @keyword style:         The style.
        @type style:            int
        @keyword name:          The name for the panel.
        @type name:             unicode
        @keyword gui:           The main GUI class.
        @type gui:              gui.relax_gui.Main instance
        @keyword analysis_name: The name of the analysis (the name in the tab part of the notebook).
        @type analysis_name:    str
        @keyword pipe_name:     The name of the original data pipe for this analysis.
        @type pipe_name:        str
        @keyword pipe_bundle:   The name of the data pipe bundle associated with this analysis.
        @type pipe_bundle:      str
        @keyword uf_exec:       The list of user function on_execute methods returned from the new analysis wizard.
        @type uf_exec:          list of methods
        @keyword data_index:    The index of the analysis in the relax data store (set to None if no data currently exists).
        @type data_index:       None or int
        """

        # Store the GUI main class.
        self.gui = gui

        # Init.
        self.init_flag = True

        # New data container.
        if data_index == None:
            # First create the data pipe if not already in existence.
            if not has_pipe(pipe_name):
                self.gui.interpreter.apply('pipe.create',
                                           pipe_name=pipe_name,
                                           pipe_type='mf',
                                           bundle=pipe_bundle)

            # Create the data pipe bundle if needed.
            if not has_bundle(pipe_bundle):
                self.gui.interpreter.apply('pipe.bundle',
                                           bundle=pipe_bundle,
                                           pipe=pipe_name)

            # Generate a storage container in the relax data store, and alias it for easy access.
            data_index = ds.relax_gui.analyses.add('model-free')

            # Store the analysis and pipe names.
            ds.relax_gui.analyses[data_index].analysis_name = analysis_name
            ds.relax_gui.analyses[data_index].pipe_name = pipe_name
            ds.relax_gui.analyses[data_index].pipe_bundle = pipe_bundle

            # Initialise the variables.
            ds.relax_gui.analyses[data_index].grid_inc = None
            ds.relax_gui.analyses[data_index].diff_tensor_grid_inc = {
                'sphere': 11,
                'prolate': 11,
                'oblate': 11,
                'ellipsoid': 6
            }
            ds.relax_gui.analyses[data_index].mc_sim_num = None
            ds.relax_gui.analyses[
                data_index].save_dir = self.gui.system_cwd_path
            ds.relax_gui.analyses[data_index].local_tm_models = [
                'tm0', 'tm1', 'tm2', 'tm3', 'tm4', 'tm5', 'tm6', 'tm7', 'tm8',
                'tm9'
            ]
            ds.relax_gui.analyses[data_index].mf_models = [
                'm0', 'm1', 'm2', 'm3', 'm4', 'm5', 'm6', 'm7', 'm8', 'm9'
            ]
            ds.relax_gui.analyses[data_index].max_iter = 30

        # Error checking.
        if ds.relax_gui.analyses[data_index].pipe_bundle == None:
            raise RelaxError("The pipe bundle must be supplied.")

        # Alias the data.
        self.data = ds.relax_gui.analyses[data_index]
        self.data_index = data_index

        # Backward compatibility.
        if not hasattr(self.data, 'local_tm_models'):
            self.data.local_tm_models = [
                'tm0', 'tm1', 'tm2', 'tm3', 'tm4', 'tm5', 'tm6', 'tm7', 'tm8',
                'tm9'
            ]
        if not hasattr(self.data, 'mf_models'):
            self.data.mf_models = [
                'm0', 'm1', 'm2', 'm3', 'm4', 'm5', 'm6', 'm7', 'm8', 'm9'
            ]

        # Initialise the mode selection window.
        self.mode_win = Protocol_mode_sel_window()

        # Register the method for updating the spin count for the completion of user functions.
        self.observer_register()

        # Execute the base class method to build the panel.
        super(Auto_model_free, self).__init__(parent,
                                              id=id,
                                              pos=pos,
                                              size=size,
                                              style=style,
                                              name=name)

    def _about(self, event=None):
        """The about window.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Initialise the dialog.
        self.about_dialog = About_window(self)

        # Show the dialog.
        if status.show_gui:
            self.about_dialog.Show()

    def activate(self):
        """Activate or deactivate certain elements of the analysis in response to the execution lock."""

        # Flag for enabling or disabling the elements.
        enable = False
        if not status.exec_lock.locked():
            enable = True

        # Activate or deactivate the elements.
        wx.CallAfter(self.field_results_dir.Enable, enable)
        wx.CallAfter(self.spin_systems.Enable, enable)
        wx.CallAfter(self.relax_data.Enable, enable)
        wx.CallAfter(self.button_dipole_pair.Enable, enable)
        wx.CallAfter(self.button_csa.Enable, enable)
        wx.CallAfter(self.button_isotope_heteronuc.Enable, enable)
        wx.CallAfter(self.button_isotope_proton.Enable, enable)
        wx.CallAfter(self.local_tm_model_field.Enable, enable)
        wx.CallAfter(self.mf_model_field.Enable, enable)
        wx.CallAfter(self.grid_inc.Enable, enable)
        wx.CallAfter(self.mc_sim_num.Enable, enable)
        wx.CallAfter(self.max_iter.Enable, enable)
        wx.CallAfter(self.mode.Enable, enable)
        wx.CallAfter(self.button_exec_relax.Enable, enable)

    def add_values(self, box):
        """Create and add the value.set buttons for the model-free analysis.

        @param box:     The box element to pack the GUI element into.
        @type box:      wx.BoxSizer instance
        """

        # Sizer.
        sizer = wx.BoxSizer(wx.HORIZONTAL)

        # Dipole-dipole relaxation setup button.
        self.button_dipole_pair = wx.lib.buttons.ThemedGenBitmapTextButton(
            self, -1, None, " Dipolar relaxation")
        self.button_dipole_pair.SetBitmapLabel(
            wx.Bitmap(fetch_icon("relax.dipole_pair", "22x22"),
                      wx.BITMAP_TYPE_ANY))
        self.button_dipole_pair.SetFont(font.normal)
        self.button_dipole_pair.SetSize((-1, 25))
        self.button_dipole_pair.SetToolTip(
            wx.ToolTip(
                "Define the magnetic dipole-dipole relaxation mechanism."))
        self.gui.Bind(wx.EVT_BUTTON, self.setup_dipole_pair,
                      self.button_dipole_pair)
        sizer.Add(self.button_dipole_pair, 1, wx.ALL | wx.EXPAND, 0)

        # CSA button.
        self.button_csa = wx.lib.buttons.ThemedGenBitmapTextButton(
            self, -1, None, " CSA relaxation")
        self.button_csa.SetBitmapLabel(
            wx.Bitmap(fetch_icon("relax.align_tensor", "22x22"),
                      wx.BITMAP_TYPE_ANY))
        self.button_csa.SetFont(font.normal)
        self.button_csa.SetSize((-1, 25))
        self.button_csa.SetToolTip(
            wx.ToolTip(
                "Define the Chemical Shift Anisotropy (CSA) relaxation mechanism via the value.set user function."
            ))
        self.gui.Bind(wx.EVT_BUTTON, self.value_set_csa, self.button_csa)
        sizer.Add(self.button_csa, 1, wx.ALL | wx.EXPAND, 0)

        # Isotope type button (heteronucleus).
        self.button_isotope_heteronuc = wx.lib.buttons.ThemedGenBitmapTextButton(
            self, -1, None, " X isotope")
        self.button_isotope_heteronuc.SetBitmapLabel(
            wx.Bitmap(fetch_icon("relax.nuclear_symbol", "22x22"),
                      wx.BITMAP_TYPE_ANY))
        self.button_isotope_heteronuc.SetFont(font.normal)
        self.button_isotope_heteronuc.SetSize((-1, 25))
        self.button_isotope_heteronuc.SetToolTip(
            wx.ToolTip(
                "Set the nuclear isotope types of the heteronuclear spins via the spin.isotope user function."
            ))
        self.gui.Bind(wx.EVT_BUTTON, self.spin_isotope_heteronuc,
                      self.button_isotope_heteronuc)
        sizer.Add(self.button_isotope_heteronuc, 1, wx.ALL | wx.EXPAND, 0)

        # Isotope type button (proton).
        self.button_isotope_proton = wx.lib.buttons.ThemedGenBitmapTextButton(
            self, -1, None, " H isotope")
        self.button_isotope_proton.SetBitmapLabel(
            wx.Bitmap(fetch_icon("relax.nuclear_symbol", "22x22"),
                      wx.BITMAP_TYPE_ANY))
        self.button_isotope_proton.SetFont(font.normal)
        self.button_isotope_proton.SetSize((-1, 25))
        self.button_isotope_proton.SetToolTip(
            wx.ToolTip(
                "Set the nuclear isotope types of the proton spins via the spin.isotope user function."
            ))
        self.gui.Bind(wx.EVT_BUTTON, self.spin_isotope_proton,
                      self.button_isotope_proton)
        sizer.Add(self.button_isotope_proton, 1, wx.ALL | wx.EXPAND, 0)

        # Add the element to the box.
        box.Add(sizer, 0, wx.ALL | wx.EXPAND, 0)

    def assemble_data(self):
        """Assemble the data required for the auto-analysis.

        See the docstring for auto_analyses.dauvernge_protocol for details.  All data is taken from the relax data store, so data upload from the GUI to there must have been previously performed.

        @return:    A container with all the data required for the auto-analysis.
        @rtype:     class instance, list of str
        """

        # The data container.
        data = Container()
        missing = []

        # The pipe name and bundle.
        data.pipe_name = self.data.pipe_name
        data.pipe_bundle = self.data.pipe_bundle

        # The model-free models (do not change these unless absolutely necessary).
        data.local_tm_models = self.local_tm_model_field.GetValue()
        data.mf_models = self.mf_model_field.GetValue()

        # Automatic looping over all rounds until convergence (must be a boolean value of True or False).
        data.conv_loop = True

        # Increment size.
        data.inc = gui_to_int(self.grid_inc.GetValue())
        if hasattr(self.data, 'diff_tensor_grid_inc'):
            data.diff_tensor_grid_inc = self.data.diff_tensor_grid_inc
        else:
            data.diff_tensor_grid_inc = {
                'sphere': 11,
                'prolate': 11,
                'oblate': 11,
                'ellipsoid': 6
            }

        # The number of Monte Carlo simulations to be used for error analysis at the end of the analysis.
        data.mc_sim_num = gui_to_int(self.mc_sim_num.GetValue())

        # Number of maximum iterations.
        data.max_iter = self.data.max_iter

        # Results directory.
        data.save_dir = self.data.save_dir

        # Check if sequence data is loaded
        if not exists_mol_res_spin_data():
            missing.append("Sequence data")

        # Relaxation data.
        if not hasattr(cdp, 'ri_ids') or len(cdp.ri_ids) == 0:
            missing.append("Relaxation data")

        # Insufficient data.
        if hasattr(cdp, 'ri_ids') and len(cdp.ri_ids) <= 3:
            missing.append(
                "Insufficient relaxation data, 4 or more data sets are essential for the execution of the dauvergne_protocol auto-analysis. Check that you have entered data for a least two spectrometer fields."
            )

        # Interatomic data containers.
        if not hasattr(cdp, 'interatomic') or len(cdp.interatomic) == 0:
            missing.append(
                "Interatomic data (for the dipole-dipole interaction)")

        # Get the mode.
        mode = gui_to_str(self.mode.GetValue())

        # Solve for all global models.
        if mode == 'Fully automated':
            # The global model list.
            data.global_models = [
                'local_tm', 'sphere', 'prolate', 'oblate', 'ellipsoid', 'final'
            ]

        # Any global model selected.
        else:
            data.global_models = [mode]

        # Check for vectors.
        vector_check = False
        if 'prolate' in data.global_models or 'oblate' in data.global_models or 'ellipsoid' in data.global_models:
            vector_check = True

        # Spin variables.
        for spin, spin_id in spin_loop(return_id=True):
            # Skip deselected spins.
            if not spin.select:
                continue

            # The message skeleton.
            msg = "Spin '%s' - %s (try the %s user function)." % (spin_id,
                                                                  "%s", "%s")

            # Test if the nuclear isotope type has been set.
            if not hasattr(spin, 'isotope') or spin.isotope == None:
                missing.append(msg % ("nuclear isotope data", "spin.isotope"))

            # Test if the CSA value has been set for the heteronuclei.
            if (hasattr(spin, 'isotope') and spin.isotope
                    in ['15N', '13C']) and (not hasattr(spin, 'csa')
                                            or spin.csa == None):
                missing.append(msg % ("CSA data", "value.set"))

        # Interatomic data container variables.
        for interatom in interatomic_loop():
            # Get the spin containers.
            spin1 = return_spin(spin_hash=interatom._spin_hash1)
            spin2 = return_spin(spin_hash=interatom._spin_hash2)

            # Skip deselected spins.
            if not spin1.select:
                continue
            if not spin2.select:
                continue

            # The message skeleton.
            msg = "Spin pair '%s' and '%s' - %s (try the %s user function)." % (
                interatom.spin_id1, interatom.spin_id2, "%s", "%s")

            # Test if the interatomic distance has been set.
            if not hasattr(interatom, 'r') or interatom.r == None:
                missing.append(msg % ("bond length data", "value.set"))

            # Test if the unit vectors have been loaded.
            if vector_check and (not hasattr(interatom, 'vector')
                                 or interatom.vector is None):
                missing.append(msg %
                               ("unit vectors", "interatom.unit_vectors"))

        # Return the container and list of missing data.
        return data, missing

    def build_left_box(self):
        """Construct the left hand box to pack into the main model-free box.

        @return:    The left hand box element containing the bitmap and about button to pack into the main model-free box.
        @rtype:     wx.BoxSizer instance
        """

        # Build the left hand box.
        left_box = wx.BoxSizer(wx.VERTICAL)

        # The images.
        bitmaps = [
            ANALYSIS_IMAGE_PATH + "model_free" + sep +
            "model_free_200x200.png", IMAGE_PATH + 'modelfree.png'
        ]

        # Add the model-free bitmap picture.
        for i in range(len(bitmaps)):
            # The bitmap.
            bitmap = wx.StaticBitmap(self, -1, bitmap_setup(bitmaps[i]))

            # Add it.
            left_box.Add(bitmap, 0, wx.ALL, 0)

        # A spacer.
        left_box.AddStretchSpacer()

        # A button sizer, with some initial spacing.
        button_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # An about button.
        button = wx.lib.buttons.ThemedGenBitmapTextButton(
            self, -1, None, "About")
        button.SetBitmapLabel(
            wx.Bitmap(fetch_icon('oxygen.actions.help-about', "22x22"),
                      wx.BITMAP_TYPE_ANY))
        button.SetFont(font.normal)
        button.SetToolTip(
            wx.ToolTip("Information about this automatic analysis"))

        # Bind the click.
        self.Bind(wx.EVT_BUTTON, self._about, button)

        # A cursor for the button.
        if dep_check.wx_classic:
            cursor = wx.StockCursor(wx.CURSOR_QUESTION_ARROW)
        else:
            cursor = wx.Cursor(wx.CURSOR_QUESTION_ARROW)
        button.SetCursor(cursor)

        # Pack the button.
        button_sizer.Add(button, 0, 0, 0)
        left_box.Add(button_sizer, 0, wx.ALL, 0)

        # Return the packed box.
        return left_box

    def build_right_box(self):
        """Construct the right hand box to pack into the main model-free box.

        @return:    The right hand box element containing all model-free GUI elements (excluding the bitmap) to pack into the main model-free box.
        @rtype:     wx.BoxSizer instance
        """

        # Use a vertical packing of elements.
        box = wx.BoxSizer(wx.VERTICAL)

        # Add the frame title.
        self.add_title(box, "Model-free analysis")

        # Display the data pipe.
        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)

        # Add the results directory GUI element.
        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)

        # Add the spin GUI element.
        self.add_spin_systems(box, self)

        # Add the relaxation data list GUI element, with spacing.
        box.AddSpacer(10)
        self.relax_data = Relax_data_list(gui=self.gui,
                                          parent=self,
                                          box=box,
                                          id=str(self.data_index))
        box.AddSpacer(10)

        # Add the value.set buttons.
        self.add_values(box)
        box.AddSpacer(10)

        # Add the local tau_m models GUI element, with spacing.
        self.local_tm_model_field = Local_tm_list(self, box)
        self.local_tm_model_field.set_value(self.data.local_tm_models)

        # Add the model-free models GUI element, with spacing.
        self.mf_model_field = Mf_list(self, box)
        self.mf_model_field.set_value(self.data.mf_models)

        # The optimisation settings.
        self.grid_inc = Spin_ctrl(
            box,
            self,
            text="Grid search increments:",
            default=11,
            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)
        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.",
            width_text=self.width_text,
            width_button=self.width_button,
            spacer=self.spacer_horizontal)

        # Add maximum iteration selector.
        self.max_iter = Spin_ctrl(
            box,
            self,
            text="Maximum iterations:",
            default=self.data.max_iter,
            tooltip=
            "The maximum number of iterations for the protocol.  This is the limit for the global looping over the optimisation of the model-free models, model elimination, model selection and then optimisation of the diffusion tensor.",
            min=25,
            max=100,
            width_text=self.width_text,
            width_button=self.width_button,
            spacer=self.spacer_horizontal)

        # The calculation mode.
        self.mode = Text_ctrl(
            box,
            self,
            text="Protocol mode:",
            default='Fully automated',
            tooltip=
            "Select if the dauvergne_protocol analysis will be fully automated or whether the individual global models will be optimised separately.",
            tooltip_button="Open the protocol mode selection window.",
            icon=fetch_icon('oxygen.actions.system-run', "16x16"),
            fn=self.mode_dialog,
            editable=False,
            button=True,
            width_text=self.width_text,
            width_button=self.width_button,
            spacer=self.spacer_horizontal)

        # Stretchable spacing (with a minimal space).
        box.AddSpacer(30)
        box.AddStretchSpacer()

        # Add the execution GUI element.
        self.button_exec_relax = self.add_execute_analysis(box, self.execute)

        # Return the box.
        return box

    def delete(self):
        """Unregister the spin count from the user functions."""

        # Unregister the observer methods.
        self.observer_register(remove=True)

        # Clean up the relaxation data list object.
        self.relax_data.delete()

        # Destroy the dipole-dipole interaction wizard.
        if hasattr(self, 'dipole_wizard'):
            self.dipole_wizard.Destroy()
            del self.dipole_wizard

        # Destroy the mode selection window.
        self.mode_win.Destroy()
        del self.mode_win

        # Destroy the model list windows.
        self.local_tm_model_field.model_win.Destroy()
        del self.local_tm_model_field
        self.mf_model_field.model_win.Destroy()
        del self.mf_model_field

        # Destroy the missing data dialog, if present.
        if hasattr(self, 'missing_data'):
            self.missing_data.Destroy()
            del self.missing_data

    def execute(self, event=None):
        """Set up, execute, and process the automatic model-free protocol.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Flush the GUI interpreter internal queue to make sure all user functions are complete.
        self.gui.interpreter.flush()

        # relax execution lock.
        if status.exec_lock.locked():
            error_message("relax is currently executing.",
                          "relax execution lock")
            event.Skip()
            return

        # User warning to close windows.
        self.gui.close_windows()

        # Synchronise the frame data to the relax data store.
        self.sync_ds(upload=True)

        # Assemble all the data needed for the auto-analysis.
        data, missing = self.assemble_data()

        # Missing data.
        if len(missing):
            self.missing_data = Missing_data(missing)
            return

        # Display the relax controller, and go to the end of the log window.
        self.gui.show_controller(None)
        self.gui.controller.log_panel.on_goto_end(None)

        # Start the thread.
        self.thread = Execute_mf(self.gui, data, self.data_index)
        self.thread.start()

        # Terminate the event.
        event.Skip()

    def mode_dialog(self, event=None):
        """The calculation mode selection.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Show the model selector window.
        if status.show_gui:
            self.mode_win.ShowModal()

        # Set the model.
        self.mode.SetValue(str_to_gui(self.mode_win.select))

    def observer_register(self, remove=False):
        """Register and unregister methods with the observer objects.

        @keyword remove:    If set to True, then the methods will be unregistered.
        @type remove:       False
        """

        # Register.
        if not remove:
            status.observers.gui_uf.register(self.data.pipe_bundle,
                                             self.update_spin_count,
                                             method_name='update_spin_count')
            status.observers.exec_lock.register(self.data.pipe_bundle,
                                                self.activate,
                                                method_name='activate')

        # Unregister.
        else:
            # The model-free methods.
            status.observers.gui_uf.unregister(self.data.pipe_bundle)
            status.observers.exec_lock.unregister(self.data.pipe_bundle)

            # The embedded objects methods.
            self.relax_data.observer_register(remove=True)

    def results_directory(self, event=None):
        """The results directory selection.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # The dialog.
        dialog = RelaxDirDialog(parent=self,
                                message='Select the results directory',
                                defaultPath=self.field_results_dir.GetValue())

        # Show the dialog and catch if no file has been selected.
        if status.show_gui and dialog.ShowModal() != wx.ID_OK:
            # Don't do anything.
            return

        # The path (don't do anything if not set).
        path = gui_to_str(dialog.get_path())
        if not path:
            return

        # Store the path.
        self.data.save_dir = path

        # Place the path in the text box.
        self.field_results_dir.SetValue(str_to_gui(path))

    def setup_dipole_pair(self, event=None):
        """Create the wizard for the dipole-dipole interaction.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Change the cursor to busy.
        wx.BeginBusyCursor()

        # Destroy the dipole-dipole interaction wizard, if it exists.
        if hasattr(self, 'dipole_wizard'):
            self.dipole_wizard.Destroy()

        # Create the wizard.
        self.dipole_wizard = Wiz_window(
            parent=self.gui,
            size_x=1000,
            size_y=750,
            title="Dipole-dipole interaction setup")

        # Structural data.
        if not hasattr(cdp, 'structure'):
            # Create the PDB reading page.
            page = uf_store['structure.read_pdb'].create_page(
                self.dipole_wizard, sync=True)
            self.dipole_wizard.add_page(page, skip_button=True)

            # Create the position reading page.
            page = uf_store['structure.get_pos'].create_page(
                self.dipole_wizard, sync=True)
            self.dipole_wizard.add_page(page, skip_button=True)

        # Create the interatom.define page.
        page = uf_store['interatom.define'].create_page(self.dipole_wizard,
                                                        sync=True)
        page.SetValue('spin_id1', '@N')
        page.SetValue('spin_id2', '@H')
        self.dipole_wizard.add_page(page)

        # Create the interatom.set_dist page.
        page = uf_store['interatom.set_dist'].create_page(self.dipole_wizard,
                                                          sync=True)
        page.SetValue('spin_id1', '@N*')
        page.SetValue('spin_id2', '@H*')
        page.SetValue('ave_dist', NH_BOND_LENGTH)
        self.dipole_wizard.add_page(page)

        # Create the interatom.unit_vectors page.
        page = uf_store['interatom.unit_vectors'].create_page(
            self.dipole_wizard, sync=True)
        self.dipole_wizard.add_page(page)

        # Reset the cursor.
        if wx.IsBusy():
            wx.EndBusyCursor()

        # Execute the wizard.
        self.dipole_wizard.run()

    def spin_isotope_heteronuc(self, event=None):
        """Set the nuclear isotope types of the heteronuclear spins via the spin.isotope user function.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Call the user function.
        uf_store['spin.isotope'](isotope='15N', spin_id='@N*')

    def spin_isotope_proton(self, event=None):
        """Set the nuclear isotope types of the proton spins via the spin.isotope user function.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Call the user function.
        uf_store['spin.isotope'](isotope='1H', spin_id='@H*')

    def sync_ds(self, upload=False):
        """Synchronise the analysis frame and the relax data store, both ways.

        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.

        @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.
        @type upload:       bool
        """

        # The local tau_m models to use.
        if upload:
            self.data.local_tm_models = self.local_tm_model_field.GetValue()
        else:
            self.local_tm_model_field.set_value(self.data.local_tm_models)

        # The model-free models to use.
        if upload:
            self.data.mf_models = self.mf_model_field.GetValue()
        else:
            self.mf_model_field.set_value(self.data.mf_models)

        # The grid incs.
        if upload:
            self.data.grid_inc = gui_to_int(self.grid_inc.GetValue())
        elif hasattr(self.data, 'grid_inc'):
            self.grid_inc.SetValue(int(self.data.grid_inc))

        # The MC sim number.
        if upload:
            self.data.mc_sim_num = gui_to_int(self.mc_sim_num.GetValue())
        elif hasattr(self.data, 'mc_sim_num'):
            self.mc_sim_num.SetValue(int(self.data.mc_sim_num))

        # The results directory.
        if upload:
            self.data.save_dir = str(self.field_results_dir.GetValue())
        else:
            self.field_results_dir.SetValue(str_to_gui(self.data.save_dir))

        # Maximum iterations.
        if upload:
            self.data.max_iter = gui_to_int(self.max_iter.GetValue())
        else:
            self.max_iter.SetValue(int(self.data.max_iter))

    def value_set_csa(self, event=None):
        """Set the CSA via the value.set uf.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Get the default value.
        api = return_api()
        val = api.default_value('csa')

        # Call the user function.
        uf_store['value.set'](val=val, param='csa', spin_id='@N*')
Ejemplo n.º 8
0
class Auto_relax_disp(Base_analysis):
    """The relaxation dispersion auto-analysis GUI element."""

    # Hardcoded variables.
    analysis_type = None
    bitmap = ANALYSIS_IMAGE_PATH+"relax_disp_200x200.png"
    label = 'Relax-disp'

    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):
        """Build the automatic R1 and R2 analysis GUI frame elements.

        @param parent:          The parent wx element.
        @type parent:           wx object
        @keyword id:            The unique ID number.
        @type id:               int
        @keyword pos:           The position.
        @type pos:              wx.Size object
        @keyword size:          The size.
        @type size:             wx.Size object
        @keyword style:         The style.
        @type style:            int
        @keyword name:          The name for the panel.
        @type name:             unicode
        @keyword gui:           The main GUI class.
        @type gui:              gui.relax_gui.Main instance
        @keyword analysis_name: The name of the analysis (the name in the tab part of the notebook).
        @type analysis_name:    str
        @keyword pipe_name:     The name of the data pipe associated with this analysis.
        @type pipe_name:        str
        @keyword pipe_bundle:   The name of the data pipe bundle associated with this analysis.
        @type pipe_bundle:      str
        @keyword uf_exec:       The list of user function on_execute methods returned from the new analysis wizard.
        @type uf_exec:          list of methods
        @keyword data_index:    The index of the analysis in the relax data store (set to None if no data currently exists).
        @type data_index:       None or int
        """

        # Store the GUI main class.
        self.gui = gui

        # Init.
        self.init_flag = True

        # New data container.
        if data_index == None:
            # First create the data pipe if not already in existence.
            if not has_pipe(pipe_name):
                self.gui.interpreter.apply('pipe.create', pipe_name=pipe_name, pipe_type='relax_disp', bundle=pipe_bundle)

            # Create the data pipe bundle if needed.
            if not has_bundle(pipe_bundle):
                self.gui.interpreter.apply('pipe.bundle', bundle=pipe_bundle, pipe=pipe_name)

            # Generate a storage container in the relax data store, and alias it for easy access.
            data_index = ds.relax_gui.analyses.add(self.label)

            # Store the analysis and pipe names.
            ds.relax_gui.analyses[data_index].analysis_name = analysis_name
            ds.relax_gui.analyses[data_index].pipe_name = pipe_name
            ds.relax_gui.analyses[data_index].pipe_bundle = pipe_bundle

            # Initialise the variables.
            ds.relax_gui.analyses[data_index].r1_fit = False
            ds.relax_gui.analyses[data_index].numeric_only = False
            ds.relax_gui.analyses[data_index].grid_inc = None
            ds.relax_gui.analyses[data_index].mc_sim_num = None
            ds.relax_gui.analyses[data_index].exp_mc_sim_num = None
            ds.relax_gui.analyses[data_index].pre_run_dir = None
            ds.relax_gui.analyses[data_index].mc_sim_all_models = False
            ds.relax_gui.analyses[data_index].insignificance = 1.0
            ds.relax_gui.analyses[data_index].save_dir = self.gui.system_cwd_path

            # Set the default dispersion models based on the experiment type.
            ds.relax_gui.analyses[data_index].disp_models = [
                MODEL_R2EFF,
                MODEL_NOREX,
                MODEL_CR72,
                MODEL_NS_CPMG_2SITE_EXPANDED,
                MODEL_MP05,
                MODEL_NS_R1RHO_2SITE
            ]

        # Error checking.
        if ds.relax_gui.analyses[data_index].pipe_bundle == None:
            raise RelaxError("The pipe bundle must be supplied.")

        # Alias the data.
        self.data = ds.relax_gui.analyses[data_index]
        self.data_index = data_index

        # Register the method for updating the spin count for the completion of user functions.
        self.observer_register()

        # Execute the base class method to build the panel.
        super(Auto_relax_disp, self).__init__(parent, id=id, pos=pos, size=size, style=style, name=name)

        # Optimisation variables for speeding up the test suite.
        self.opt_func_tol = 1e-25
        self.opt_max_iterations = int(1e7)

        # Update the isotope and cluster information.
        self.update_clusters()


    def activate(self):
        """Activate or deactivate certain elements of the analysis in response to the execution lock."""

        # Flag for enabling or disabling the elements.
        enable = False
        if not status.exec_lock.locked():
            enable = True

        # Activate or deactivate the elements.
        wx.CallAfter(self.field_results_dir.Enable, enable)
        wx.CallAfter(self.field_pre_run_dir.Enable, enable)
        wx.CallAfter(self.spin_systems.Enable, enable)
        wx.CallAfter(self.field_cluster.Enable, enable)
        wx.CallAfter(self.button_isotope.Enable, enable)
        wx.CallAfter(self.button_r1.Enable, enable)
        wx.CallAfter(self.button_chemical_shift.Enable, enable)
        wx.CallAfter(self.button_interatom_define.Enable, enable)
        wx.CallAfter(self.peak_intensity.Enable, enable)
        wx.CallAfter(self.model_field.Enable, enable)
        wx.CallAfter(self.button_exec_relax.Enable, enable)


    def add_buttons(self, box):
        """Add all of the buttons.

        @param box:     The box element to pack the GUI element into.
        @type box:      wx.BoxSizer instance
        """

        # Sizer.
        sizer = wx.BoxSizer(wx.HORIZONTAL)

        # Isotope type button.
        self.button_isotope = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, " Spin isotope")
        self.button_isotope.SetBitmapLabel(wx.Bitmap(fetch_icon("relax.nuclear_symbol", "22x22"), wx.BITMAP_TYPE_ANY))
        self.button_isotope.SetFont(font.normal)
        self.button_isotope.SetSize((-1, 25))
        self.button_isotope.SetToolTip(wx.ToolTip("Set the nuclear isotope types via the spin.isotope user function."))
        self.gui.Bind(wx.EVT_BUTTON, self.spin_isotope, self.button_isotope)
        sizer.Add(self.button_isotope, 1, wx.ALL|wx.EXPAND, 0)

        # R1 button.
        self.button_r1 = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, " %s relaxation data"%r1)
        self.button_r1.SetBitmapLabel(wx.Bitmap(fetch_icon("relax.fid", "22x22"), wx.BITMAP_TYPE_ANY))
        self.button_r1.SetFont(font.normal)
        self.button_r1.SetSize((-1, 25))
        self.button_r1.SetToolTip(wx.ToolTip("Load the %s relaxation data for the off-resonance %s-type experiments.  For all other experiment types this is unused.  One %s data set per magnetic field strength must be loaded."%(r1, r1rho, r1)))
        self.gui.Bind(wx.EVT_BUTTON, self.load_r1_data, self.button_r1)
        sizer.Add(self.button_r1, 1, wx.ALL|wx.EXPAND, 0)

        # Chemical shift button.
        self.button_chemical_shift = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, " Chemical shift")
        self.button_chemical_shift.SetBitmapLabel(wx.Bitmap(fetch_icon("relax.chemical_shift", "22x22"), wx.BITMAP_TYPE_ANY))
        self.button_chemical_shift.SetFont(font.normal)
        self.button_chemical_shift.SetSize((-1, 25))
        self.button_chemical_shift.SetToolTip(wx.ToolTip("Read chemical shifts from a peak list for the off-resonance %s-type experiments.  For all other experiment types this is unused."%r1rho))
        self.gui.Bind(wx.EVT_BUTTON, self.load_cs_data, self.button_chemical_shift)
        sizer.Add(self.button_chemical_shift, 1, wx.ALL|wx.EXPAND, 0)

        # Interatomic interaction button.
        self.button_interatom_define = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, " Interatomic interaction")
        self.button_interatom_define.SetBitmapLabel(wx.Bitmap(fetch_icon("relax.dipole_pair", "22x22"), wx.BITMAP_TYPE_ANY))
        self.button_interatom_define.SetFont(font.normal)
        self.button_interatom_define.SetSize((-1, 25))
        self.button_interatom_define.SetToolTip(wx.ToolTip("Define the interatomic interations via the interatom.define user function for the MQ dispersion models."))
        self.gui.Bind(wx.EVT_BUTTON, self.interatom_define, self.button_interatom_define)
        sizer.Add(self.button_interatom_define, 1, wx.ALL|wx.EXPAND, 0)

        # value.set button.
        self.button_value_set = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, " Value setting")
        self.button_value_set.SetBitmapLabel(wx.Bitmap(fetch_icon("relax.value", "22x22"), wx.BITMAP_TYPE_ANY))
        self.button_value_set.SetFont(font.normal)
        self.button_value_set.SetSize((-1, 25))
        tooltip = "Set certain parameters to experimentally determined values.\n\nThis is simply used to speed up optimisation by skipping this parameter in the initial grid search.  The result is that the number of dimensions in the grid search is decreased, resulting in roughly one order of magnitude decrease in time for each parameter in this part of the analysis.  Important to note is that the parameter will be optimised after the initial grid search."
        self.button_value_set.SetToolTip(wx.ToolTip(tooltip))
        self.gui.Bind(wx.EVT_BUTTON, self.value_set, self.button_value_set)
        sizer.Add(self.button_value_set, 1, wx.ALL|wx.EXPAND, 0)

        # Add the element to the box.
        box.Add(sizer, 0, wx.ALL|wx.EXPAND, 0)


    def assemble_data(self):
        """Assemble the data required for the Auto_noe class.

        @return:    A container with all the data required for the auto-analysis, the missing list, and a list of models that don't match the experiment types.
        @rtype:     class instance, list of str, list of str
        """

        # The data container.
        data = Container()
        missing = []
        model_mismatch = []

        # The pipe name and bundle.
        data.pipe_name = self.data.pipe_name
        data.pipe_bundle = self.data.pipe_bundle

        # Results directories.
        data.save_dir = self.data.save_dir
        data.pre_run_dir = gui_to_str(self.field_pre_run_dir.GetValue())

        # Check if sequence data is loaded
        if not exists_mol_res_spin_data():
            missing.append("Sequence data")

        # Spin variables.
        for spin, spin_id in spin_loop(return_id=True, skip_desel=True):
            # The message skeleton.
            msg = "Spin '%s' - %s (try the %s user function)." % (spin_id, "%s", "%s")

            # Test if the nuclear isotope type has been set.
            if not hasattr(spin, 'isotope') or spin.isotope == None:
                missing.append(msg % ("nuclear isotope data", "spin.isotope"))

        # Spectral data.
        if not hasattr(cdp, 'spectrum_ids') or len(cdp.spectrum_ids) < 2:
            missing.append("Spectral data")

        # The dispersion models.
        data.models = self.model_field.GetValue()

        # Invalid models.
        for model in data.models:
            # Invalid CPMG models.
            if model != MODEL_NOREX and model in MODEL_LIST_CPMG and not has_cpmg_exp_type():
                model_mismatch.append([model, 'CPMG'])

            # Invalid R1rho models.
            if model != MODEL_NOREX and model in MODEL_LIST_R1RHO and not has_r1rho_exp_type():
                model_mismatch.append([model, 'R1rho'])

        # The R1 parameter fitting flag.
        data.r1_fit = self.r1_fit.GetValue()

        # The numeric only solution.
        data.numeric_only = self.numeric_only.GetValue()

        # Increment size.
        data.inc = gui_to_int(self.grid_inc.GetValue())

        # The number of Monte Carlo simulations to be used for error analysis at the end of the analysis.
        data.mc_sim_num = gui_to_int(self.mc_sim_num.GetValue())
        data.exp_mc_sim_num = gui_to_int(self.exp_mc_sim_num.GetValue())
        data.mc_sim_all_models = self.mc_sim_all_models.GetValue()

        # The insignificance level.
        data.insignificance = self.insignificance.GetValue()
        try:
            data.insignificance = gui_to_float(data.insignificance)
        except:
            missing.append("The insignificance level must be a number.")

        # Optimisation precision.
        data.opt_func_tol = self.opt_func_tol
        data.opt_max_iterations = self.opt_max_iterations

        # Return the container, the list of missing data, and any models that don't match the experiment types.
        return data, missing, model_mismatch


    def build_right_box(self):
        """Construct the right hand box to pack into the main relax_disp box.

        @return:    The right hand box element containing all relaxation dispersion GUI elements (excluding the bitmap) to pack into the main box.
        @rtype:     wx.BoxSizer instance
        """

        # Use a vertical packing of elements.
        box = wx.BoxSizer(wx.VERTICAL)

        # Add the frame title.
        self.add_title(box, "Relaxation dispersion analysis")

        # Display the data pipe.
        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)

        # Add the results directory GUI element.
        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)

        # Add the results directory GUI element.
        tooltip = "The optional directory containing the dispersion auto-analysis results from a previous run.  The optimised parameters from these previous results will be used as the starting point for optimisation rather than performing a grid search.  This is essential for when large spin clusters are specified, as a grid search becomes prohibitively expensive with clusters of three or more spins.  At some point a RelaxError will occur because the grid search is impossibly large.  For the cluster specific parameters, i.e. the populations of the states and the exchange parameters, an average value will be used as the starting point.  For all other parameters, the R20 values for each spin and magnetic field, as well as the parameters related to the chemical shift difference dw, the optimised values of the previous run will be directly copied."
        self.field_pre_run_dir = Text_ctrl(box, self, text="Previous run directory:", icon=fetch_icon('oxygen.actions.document-open-folder', "16x16"), tooltip=tooltip, tooltip_button="Select the results directory of the previous run.", fn=self.pre_run_directory, button=True, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)

        # Add the spin GUI element.
        self.add_spin_systems(box, self)

        # Spin cluster setup.
        self.field_cluster = Text_ctrl(box, self, text="Spin cluster IDs:", button_text=" Cluster", icon=fetch_icon("relax.cluster", "16x16"), tooltip="The list of currently defined spin clusters.  A spin cluster will share the same the dispersion parameters during the optimisation of the dispersion model.  The special 'free spins' cluster ID refers to all non-clustered spins.", tooltip_button="Define clusters of spins using the relax_disp.cluster user function.", fn=self.relax_disp_cluster, button=True, editable=False, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)

        # Add the buttons.
        box.AddSpacer(20)
        self.add_buttons(box=box)

        # Add the peak list selection GUI element, with spacing.
        box.AddSpacer(20)
        self.peak_intensity = Spectra_list(gui=self.gui, parent=self, box=box, id=str(self.data_index), fn_add=self.peak_wizard_launch, relax_disp_flag=True)
        box.AddSpacer(10)

        # Add the dispersion models GUI element, with spacing.
        self.model_field = Disp_model_list(self, box)
        self.model_field.set_value(self.data.disp_models)

        # R1 parameter optimisation.
        tooltip = "Toggle the optimisation of the off-resonance R1 parameter.\n\nThis allows the optimisation of R1 values to be turned on an off for the relaxation dispersion dispersion models.  If turned off, the current values of R1 will be fixed.  Otherwise the R1 values will be added to the model parameter set.  For models which do not support the R1 parameter for off-resonance effects, this setting will have no effect."
        self.r1_fit = Boolean_ctrl(box, self, text="R1 parameter optimisation:", default=False, tooltip=tooltip, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)

        # The numeric only solution.
        tooltip = "The class of models to use in the final model selection.\n\nThe default of False allows all dispersion models to be compared for statistical significance in the analysis (no exchange, the analytic models and the numeric models).  The value of True will activate a pure numeric solution - the analytic models will be optimised, as they are very useful for replacing the grid search for the numeric models, but the final model selection will not include them."
        self.numeric_only = Boolean_ctrl(box, self, text="Pure numeric solutions:", default=False, tooltip=tooltip, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)

        # The grid search optimisation settings.
        self.grid_inc = Spin_ctrl(box, self, text="Grid search increments:", default=default_grid_inc, 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)

        # The MC simulation settings.
        self.exp_mc_sim_num = Spin_ctrl(box, self, text="Exponential curve error analysis:", default=default_exp_mc_sim_num, min=-1, max=100000, tooltip="This is the number of Monte Carlo simulations performed for error propagation and analysis when estimating R2eff errors from exponential curve fitting.  Setting to '-1' estimates error from the Covariance matrix.", width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)
        self.mc_sim_num = Spin_ctrl(box, self, text="Monte Carlo simulation number:", default=default_mc_sim_num, 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)
        self.mc_sim_all_models = Boolean_ctrl(box, self, text="Per model error analysis:", default=False, tooltip="A flag which if True will cause Monte Carlo simulations to be performed for each individual model.  Otherwise Monte Carlo simulations will be reserved for the final model.", width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)

        # The speed up of grid search.
        tooltip = "Experimental option, be careful.  True will set the grid %s values from the minimum %s values.  This will speed up the grid search with a factor GRID_INC^(Nr_spec_freq).  For a CPMG experiment with two fields and standard GRID_INC=21, the speed-up is a factor 441." % (r2, r2eff)
        self.set_grid_r20 = Boolean_ctrl(box, self, text="Set R20 to the minimum R2eff:", default=False, tooltip=tooltip, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)

        # The insignificance cutoff.
        tooltip = "The %s/%s value in rad/s by which to judge insignificance.  If the maximum difference between two points on all dispersion curves for a spin is less than this value, that spin will be deselected.  This does not affect the '%s' model.  Set this value to 0.0 to use all data." % (r2eff, r1rho, MODEL_NOREX)
        self.insignificance = Float_ctrl(box, self, text="Insignificance level:", default=1.0, tooltip=tooltip, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)

        # Stretchable spacing (with a minimal space).
        box.AddSpacer(30)
        box.AddStretchSpacer()

        # Add the execution GUI element.
        self.button_exec_relax = self.add_execute_analysis(box, self.execute)

        # Return the box.
        return box


    def delete(self):
        """Unregister the spin count from the user functions."""

        # Unregister the observer methods.
        self.observer_register(remove=True)

        # Clean up the peak intensity object.
        self.peak_intensity.delete()

        # Destroy the peak intensity wizard, if it exists.
        if hasattr(self, 'peak_wizard'):
            self.peak_wizard.Destroy()
            del self.peak_wizard

        # Destroy the model list windows.
        self.model_field.model_win.Destroy()
        del self.model_field

        # Destroy the missing data dialog, if present.
        if hasattr(self, 'missing_data'):
            self.missing_data.Destroy()
            del self.missing_data


    def execute(self, event):
        """Set up, execute, and process the automatic Rx analysis.

        @param event:   The wx event.
        @type event:    wx event
        """

        # Flush the GUI interpreter internal queue to make sure all user functions are complete.
        self.gui.interpreter.flush()

        # relax execution lock.
        if status.exec_lock.locked():
            error_message("relax is currently executing.", "relax execution lock")
            event.Skip()
            return

        # User warning to close windows.
        self.gui.close_windows()

        # Synchronise the frame data to the relax data store.
        self.sync_ds(upload=True)

        # Assemble all the data needed for the auto-analysis.
        data, missing, model_mismatch = self.assemble_data()

        # Missing data.
        if len(missing):
            self.missing_data = Missing_data(missing)
            return

        # Model mismatch.
        if len(model_mismatch):
            # Generate the text.
            text = ''
            for model, exp in model_mismatch:
                text += "The '%s' %s model cannot be used as no %s experiment types have been set up.\n" % (model, exp, exp)

            # The error message.
            error_message(text, caption='Model mismatch')
            return

        # Display the relax controller, and go to the end of the log window.
        self.gui.show_controller(None)
        self.gui.controller.log_panel.on_goto_end(None)

        # Start the thread.
        self.thread = Execute_relax_disp(self.gui, data, self.data_index)
        self.thread.start()

        # Terminate the event.
        event.Skip()


    def interatom_define(self, event=None):
        """Define the interatomic interactions of the spins via the interatom.define user function.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Call the user function.
        uf_store['interatom.define'](wx_wizard_modal=True, spin_id1='@N', spin_id2='@H')


    def load_cs_data(self, event=None):
        """Read chemical shift data from a peak list via the chemical_shift.read user function.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Call the user function.
        uf_store['chemical_shift.read'](wx_wizard_modal=True)


    def load_r1_data(self, event=None):
        """Load R1 relaxation data via the relax_data.read user function.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Call the user function.
        uf_store['relax_data.read'](wx_wizard_modal=True, ri_type='R1')


    def observer_register(self, remove=False):
        """Register and unregister methods with the observer objects.

        @keyword remove:    If set to True, then the methods will be unregistered.
        @type remove:       False
        """

        # Register.
        if not remove:
            status.observers.gui_uf.register('spin count - %s' % self.data.pipe_bundle, self.update_spin_count, method_name='update_spin_count')
            status.observers.exec_lock.register(self.data.pipe_bundle, self.activate, method_name='activate')
            status.observers.gui_uf.register('clusters - %s' % self.data.pipe_bundle, self.update_clusters, method_name='update_clusters')

        # Unregister.
        else:
            # The methods.
            status.observers.gui_uf.unregister('spin count - %s' % self.data.pipe_bundle)
            status.observers.exec_lock.unregister(self.data.pipe_bundle)
            status.observers.gui_uf.unregister('isotopes - %s' % self.data.pipe_bundle)
            status.observers.gui_uf.unregister('clusters - %s' % self.data.pipe_bundle)

            # The embedded objects methods.
            self.peak_intensity.observer_register(remove=True)


    def peak_wizard_launch(self, event):
        """Launch the peak loading wizard.

        @param event:   The wx event.
        @type event:    wx event
        """

        # Destroy the peak intensity wizard, if it exists.
        if hasattr(self, 'peak_wizard'):
            self.peak_wizard.Destroy()

        # A new wizard instance.
        self.peak_wizard = Peak_intensity_wizard(relax_disp=True)


    def pre_run_directory(self, event):
        """The pre-run directory selection.

        @param event:   The wx event.
        @type event:    wx event
        """

        # The dialog.
        dialog = RelaxDirDialog(parent=self, message='Select the directory of the previous run', defaultPath=self.field_pre_run_dir.GetValue())

        # Show the dialog and catch if no file has been selected.
        if status.show_gui and dialog.ShowModal() != wx.ID_OK:
            # Don't do anything.
            return

        # The path (don't do anything if not set).
        path = gui_to_str(dialog.get_path())
        if not path:
            return

        # Place the path in the text box.
        self.field_pre_run_dir.SetValue(str_to_gui(path))


    def relax_disp_cluster(self, event=None):
        """Set up spin clustering via the relax_disp.cluster user function.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Call the user function.
        uf_store['relax_disp.cluster'](wx_wizard_modal=True)


    def results_directory(self, event):
        """The results directory selection.

        @param event:   The wx event.
        @type event:    wx event
        """

        # The dialog.
        dialog = RelaxDirDialog(parent=self, message='Select the results directory', defaultPath=self.field_results_dir.GetValue())

        # Show the dialog and catch if no file has been selected.
        if status.show_gui and dialog.ShowModal() != wx.ID_OK:
            # Don't do anything.
            return

        # The path (don't do anything if not set).
        path = gui_to_str(dialog.get_path())
        if not path:
            return

        # Store the path.
        self.data.save_dir = path

        # Place the path in the text box.
        self.field_results_dir.SetValue(str_to_gui(path))


    def spin_isotope(self, event=None):
        """Set the nuclear isotope types of the spins via the spin.isotope user function.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Call the user function.
        uf_store['spin.isotope'](wx_wizard_modal=True, isotope='15N', spin_id='@N*')


    def sync_ds(self, upload=False):
        """Synchronise the analysis frame and the relax data store, both ways.

        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.

        @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.
        @type upload:       bool
        """

        # The R1 parameter fitting flag.
        if upload:
            self.data.r1_fit = self.r1_fit.GetValue()
        elif hasattr(self.data, 'r1_fit'):
            self.r1_fit.SetValue(bool(self.data.r1_fit))

        # The numeric solution only flag.
        if upload:
            self.data.numeric_only = self.numeric_only.GetValue()
        elif hasattr(self.data, 'numeric_only'):
            self.numeric_only.SetValue(bool(self.data.numeric_only))

        # The grid incs.
        if upload:
            self.data.grid_inc = gui_to_int(self.grid_inc.GetValue())
        elif hasattr(self.data, 'grid_inc'):
            if self.data.grid_inc == None:
                self.data.grid_inc = default_grid_inc
            self.grid_inc.SetValue(int(self.data.grid_inc))

        # The MC sim number.
        if upload:
            self.data.mc_sim_num = gui_to_int(self.mc_sim_num.GetValue())
        elif hasattr(self.data, 'mc_sim_num'):
            if self.data.mc_sim_num == None:
                self.data.mc_sim_num = default_mc_sim_num
            self.mc_sim_num.SetValue(int(self.data.mc_sim_num))

        # The EXP MC sim number.
        if upload:
            self.data.exp_mc_sim_num = gui_to_int(self.exp_mc_sim_num.GetValue())
        elif hasattr(self.data, 'exp_mc_sim_num'):
            if self.data.exp_mc_sim_num == None:
                self.data.exp_mc_sim_num = default_exp_mc_sim_num
            self.exp_mc_sim_num.SetValue(int(self.data.exp_mc_sim_num))

        # The All model MC sim flag.
        if upload:
            self.data.mc_sim_all_models = self.mc_sim_all_models.GetValue()
        elif hasattr(self.data, 'mc_sim_all_models'):
            self.mc_sim_all_models.SetValue(bool(self.data.mc_sim_all_models))

        # The insignificance level.
        if upload:
            self.data.insignificance = self.insignificance.GetValue()
            try:
                self.data.insignificance = gui_to_float(self.data.insignificance)
            except:
                pass
        elif hasattr(self.data, 'insignificance'):
            self.insignificance.SetValue(float_to_gui(self.data.insignificance))

        # The results directory.
        if upload:
            self.data.save_dir = gui_to_str(self.field_results_dir.GetValue())
        else:
            self.field_results_dir.SetValue(str_to_gui(self.data.save_dir))

        # The previous run results directory.
        if upload:
            self.data.pre_run_dir = gui_to_str(self.field_pre_run_dir.GetValue())
        elif hasattr(self.data, 'pre_run_dir'):
            self.field_pre_run_dir.SetValue(str_to_gui(self.data.pre_run_dir))

        # The models to use.
        if upload:
            self.data.disp_models = self.model_field.GetValue()
        else:
            self.model_field.set_value(self.data.disp_models)


    def update_clusters(self):
        """Update the cluster field."""

        # Assemble a list of all unique isotope types.
        cluster_keys = []
        if hasattr(cdp, 'clustering'):
            cluster_keys = sorted(cdp.clustering.keys())

        # Nothing yet.
        if not len(cluster_keys):
            wx.CallAfter(self.field_cluster.SetValue, "free spins")

        # List the clusters.
        else:
            # Build the text to show.
            text = ""
            if "free spins" in cluster_keys:
                text += "free spins"
            for i in range(len(cluster_keys)):
                if cluster_keys[i] != "free spins":
                    text += ", %s" % cluster_keys[i]

            # Update the text.
            wx.CallAfter(self.field_cluster.SetValue, text)


    def value_set(self, event=None):
        """Launch the value.set user function.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Call the user function.
        uf_store['value.set'](wx_wizard_modal=True)
Ejemplo n.º 9
0
    def build_right_box(self):
        """Construct the right hand box to pack into the main relax_disp box.

        @return:    The right hand box element containing all relaxation dispersion GUI elements (excluding the bitmap) to pack into the main box.
        @rtype:     wx.BoxSizer instance
        """

        # Use a vertical packing of elements.
        box = wx.BoxSizer(wx.VERTICAL)

        # Add the frame title.
        self.add_title(box, "Relaxation dispersion analysis")

        # Display the data pipe.
        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)

        # Add the results directory GUI element.
        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)

        # Add the results directory GUI element.
        tooltip = "The optional directory containing the dispersion auto-analysis results from a previous run.  The optimised parameters from these previous results will be used as the starting point for optimisation rather than performing a grid search.  This is essential for when large spin clusters are specified, as a grid search becomes prohibitively expensive with clusters of three or more spins.  At some point a RelaxError will occur because the grid search is impossibly large.  For the cluster specific parameters, i.e. the populations of the states and the exchange parameters, an average value will be used as the starting point.  For all other parameters, the R20 values for each spin and magnetic field, as well as the parameters related to the chemical shift difference dw, the optimised values of the previous run will be directly copied."
        self.field_pre_run_dir = Text_ctrl(box, self, text="Previous run directory:", icon=fetch_icon('oxygen.actions.document-open-folder', "16x16"), tooltip=tooltip, tooltip_button="Select the results directory of the previous run.", fn=self.pre_run_directory, button=True, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)

        # Add the spin GUI element.
        self.add_spin_systems(box, self)

        # Spin cluster setup.
        self.field_cluster = Text_ctrl(box, self, text="Spin cluster IDs:", button_text=" Cluster", icon=fetch_icon("relax.cluster", "16x16"), tooltip="The list of currently defined spin clusters.  A spin cluster will share the same the dispersion parameters during the optimisation of the dispersion model.  The special 'free spins' cluster ID refers to all non-clustered spins.", tooltip_button="Define clusters of spins using the relax_disp.cluster user function.", fn=self.relax_disp_cluster, button=True, editable=False, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)

        # Add the buttons.
        box.AddSpacer(20)
        self.add_buttons(box=box)

        # Add the peak list selection GUI element, with spacing.
        box.AddSpacer(20)
        self.peak_intensity = Spectra_list(gui=self.gui, parent=self, box=box, id=str(self.data_index), fn_add=self.peak_wizard_launch, relax_disp_flag=True)
        box.AddSpacer(10)

        # Add the dispersion models GUI element, with spacing.
        self.model_field = Disp_model_list(self, box)
        self.model_field.set_value(self.data.disp_models)

        # R1 parameter optimisation.
        tooltip = "Toggle the optimisation of the off-resonance R1 parameter.\n\nThis allows the optimisation of R1 values to be turned on an off for the relaxation dispersion dispersion models.  If turned off, the current values of R1 will be fixed.  Otherwise the R1 values will be added to the model parameter set.  For models which do not support the R1 parameter for off-resonance effects, this setting will have no effect."
        self.r1_fit = Boolean_ctrl(box, self, text="R1 parameter optimisation:", default=False, tooltip=tooltip, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)

        # The numeric only solution.
        tooltip = "The class of models to use in the final model selection.\n\nThe default of False allows all dispersion models to be compared for statistical significance in the analysis (no exchange, the analytic models and the numeric models).  The value of True will activate a pure numeric solution - the analytic models will be optimised, as they are very useful for replacing the grid search for the numeric models, but the final model selection will not include them."
        self.numeric_only = Boolean_ctrl(box, self, text="Pure numeric solutions:", default=False, tooltip=tooltip, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)

        # The grid search optimisation settings.
        self.grid_inc = Spin_ctrl(box, self, text="Grid search increments:", default=default_grid_inc, 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)

        # The MC simulation settings.
        self.exp_mc_sim_num = Spin_ctrl(box, self, text="Exponential curve error analysis:", default=default_exp_mc_sim_num, min=-1, max=100000, tooltip="This is the number of Monte Carlo simulations performed for error propagation and analysis when estimating R2eff errors from exponential curve fitting.  Setting to '-1' estimates error from the Covariance matrix.", width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)
        self.mc_sim_num = Spin_ctrl(box, self, text="Monte Carlo simulation number:", default=default_mc_sim_num, 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)
        self.mc_sim_all_models = Boolean_ctrl(box, self, text="Per model error analysis:", default=False, tooltip="A flag which if True will cause Monte Carlo simulations to be performed for each individual model.  Otherwise Monte Carlo simulations will be reserved for the final model.", width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)

        # The speed up of grid search.
        tooltip = "Experimental option, be careful.  True will set the grid %s values from the minimum %s values.  This will speed up the grid search with a factor GRID_INC^(Nr_spec_freq).  For a CPMG experiment with two fields and standard GRID_INC=21, the speed-up is a factor 441." % (r2, r2eff)
        self.set_grid_r20 = Boolean_ctrl(box, self, text="Set R20 to the minimum R2eff:", default=False, tooltip=tooltip, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)

        # The insignificance cutoff.
        tooltip = "The %s/%s value in rad/s by which to judge insignificance.  If the maximum difference between two points on all dispersion curves for a spin is less than this value, that spin will be deselected.  This does not affect the '%s' model.  Set this value to 0.0 to use all data." % (r2eff, r1rho, MODEL_NOREX)
        self.insignificance = Float_ctrl(box, self, text="Insignificance level:", default=1.0, tooltip=tooltip, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)

        # Stretchable spacing (with a minimal space).
        box.AddSpacer(30)
        box.AddStretchSpacer()

        # Add the execution GUI element.
        self.button_exec_relax = self.add_execute_analysis(box, self.execute)

        # Return the box.
        return box