"Fossies" - the Fresh Open Source Software Archive

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


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

    1 ###############################################################################
    2 #                                                                             #
    3 # Copyright (C) 2009-2011 Michael Bieri                                       #
    4 # Copyright (C) 2009-2015 Edward d'Auvergne                                   #
    5 # Copyright (C) 2014-2015 Troels E. Linnet                                    #
    6 #                                                                             #
    7 # This file is part of the program relax (http://www.nmr-relax.com).          #
    8 #                                                                             #
    9 # This program is free software: you can redistribute it and/or modify        #
   10 # it under the terms of the GNU General Public License as published by        #
   11 # the Free Software Foundation, either version 3 of the License, or           #
   12 # (at your option) any later version.                                         #
   13 #                                                                             #
   14 # This program is distributed in the hope that it will be useful,             #
   15 # but WITHOUT ANY WARRANTY; without even the implied warranty of              #
   16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
   17 # GNU General Public License for more details.                                #
   18 #                                                                             #
   19 # You should have received a copy of the GNU General Public License           #
   20 # along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
   21 #                                                                             #
   22 ###############################################################################
   23 
   24 # Module docstring.
   25 """Module for the automatic relaxation dispersion analysis."""
   26 
   27 # Python module imports.
   28 import wx
   29 
   30 # relax module imports.
   31 from auto_analyses.relax_disp import Relax_disp
   32 from data_store import Relax_data_store; ds = Relax_data_store()
   33 from graphics import ANALYSIS_IMAGE_PATH, fetch_icon
   34 from gui.analyses.base import Base_analysis
   35 from gui.analyses.elements.bool_element import Boolean_ctrl
   36 from gui.analyses.elements.float_element import Float_ctrl
   37 from gui.analyses.elements.spin_element import Spin_ctrl
   38 from gui.analyses.elements.text_element import Text_ctrl
   39 from gui.analyses.elements.model_list import Model_list
   40 from gui.analyses.execute import Execute
   41 from gui.base_classes import Container
   42 from gui.components.spectrum import Spectra_list
   43 from gui.filedialog import RelaxDirDialog
   44 from gui.fonts import font
   45 from gui.message import error_message, Missing_data
   46 from gui.string_conv import float_to_gui, gui_to_float, gui_to_int, gui_to_str, str_to_gui
   47 from gui.uf_objects import Uf_storage; uf_store = Uf_storage()
   48 from gui.wizards.peak_intensity import Peak_intensity_wizard
   49 from lib.dispersion.variables import MODEL_B14, MODEL_B14_FULL, MODEL_CR72, MODEL_CR72_FULL, MODEL_DPL94, MODEL_IT99, MODEL_LIST_CPMG, MODEL_LIST_R1RHO, MODEL_LM63, MODEL_LM63_3SITE, MODEL_M61, MODEL_M61B, MODEL_MMQ_CR72, MODEL_MP05, MODEL_NOREX, MODEL_NS_CPMG_2SITE_3D, MODEL_NS_CPMG_2SITE_3D_FULL, MODEL_NS_CPMG_2SITE_EXPANDED, MODEL_NS_CPMG_2SITE_STAR, MODEL_NS_CPMG_2SITE_STAR_FULL, MODEL_NS_MMQ_2SITE, MODEL_NS_MMQ_3SITE, MODEL_NS_MMQ_3SITE_LINEAR, MODEL_NS_R1RHO_2SITE, MODEL_NS_R1RHO_3SITE, MODEL_NS_R1RHO_3SITE_LINEAR, MODEL_R2EFF, MODEL_TAP03, MODEL_TP02, MODEL_TSMFK01
   50 from lib.text.gui import dw, dw_AB, dw_BC, dwH, dwH_AB, dwH_BC, i0, kex, kAB, kBC, kAC, padw2, phi_ex, phi_exB, phi_exC, r1, r1rho, r1rho_prime, r1, r2, r2a, r2b, r2eff
   51 from pipe_control.mol_res_spin import exists_mol_res_spin_data, spin_loop
   52 from pipe_control.pipes import has_bundle, has_pipe
   53 from specific_analyses.relax_disp.data import has_cpmg_exp_type, has_r1rho_exp_type
   54 from status import Status; status = Status()
   55 
   56 # set defaults
   57 default_grid_inc = 21
   58 default_exp_mc_sim_num = 500
   59 default_mc_sim_num = 500
   60 
   61 
   62 class Auto_relax_disp(Base_analysis):
   63     """The relaxation dispersion auto-analysis GUI element."""
   64 
   65     # Hardcoded variables.
   66     analysis_type = None
   67     bitmap = ANALYSIS_IMAGE_PATH+"relax_disp_200x200.png"
   68     label = 'Relax-disp'
   69 
   70     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):
   71         """Build the automatic R1 and R2 analysis GUI frame elements.
   72 
   73         @param parent:          The parent wx element.
   74         @type parent:           wx object
   75         @keyword id:            The unique ID number.
   76         @type id:               int
   77         @keyword pos:           The position.
   78         @type pos:              wx.Size object
   79         @keyword size:          The size.
   80         @type size:             wx.Size object
   81         @keyword style:         The style.
   82         @type style:            int
   83         @keyword name:          The name for the panel.
   84         @type name:             unicode
   85         @keyword gui:           The main GUI class.
   86         @type gui:              gui.relax_gui.Main instance
   87         @keyword analysis_name: The name of the analysis (the name in the tab part of the notebook).
   88         @type analysis_name:    str
   89         @keyword pipe_name:     The name of the data pipe associated with this analysis.
   90         @type pipe_name:        str
   91         @keyword pipe_bundle:   The name of the data pipe bundle associated with this analysis.
   92         @type pipe_bundle:      str
   93         @keyword uf_exec:       The list of user function on_execute methods returned from the new analysis wizard.
   94         @type uf_exec:          list of methods
   95         @keyword data_index:    The index of the analysis in the relax data store (set to None if no data currently exists).
   96         @type data_index:       None or int
   97         """
   98 
   99         # Store the GUI main class.
  100         self.gui = gui
  101 
  102         # Init.
  103         self.init_flag = True
  104 
  105         # New data container.
  106         if data_index == None:
  107             # First create the data pipe if not already in existence.
  108             if not has_pipe(pipe_name):
  109                 self.gui.interpreter.apply('pipe.create', pipe_name=pipe_name, pipe_type='relax_disp', bundle=pipe_bundle)
  110 
  111             # Create the data pipe bundle if needed.
  112             if not has_bundle(pipe_bundle):
  113                 self.gui.interpreter.apply('pipe.bundle', bundle=pipe_bundle, pipe=pipe_name)
  114 
  115             # Generate a storage container in the relax data store, and alias it for easy access.
  116             data_index = ds.relax_gui.analyses.add(self.label)
  117 
  118             # Store the analysis and pipe names.
  119             ds.relax_gui.analyses[data_index].analysis_name = analysis_name
  120             ds.relax_gui.analyses[data_index].pipe_name = pipe_name
  121             ds.relax_gui.analyses[data_index].pipe_bundle = pipe_bundle
  122 
  123             # Initialise the variables.
  124             ds.relax_gui.analyses[data_index].r1_fit = False
  125             ds.relax_gui.analyses[data_index].numeric_only = False
  126             ds.relax_gui.analyses[data_index].grid_inc = None
  127             ds.relax_gui.analyses[data_index].mc_sim_num = None
  128             ds.relax_gui.analyses[data_index].exp_mc_sim_num = None
  129             ds.relax_gui.analyses[data_index].pre_run_dir = None
  130             ds.relax_gui.analyses[data_index].mc_sim_all_models = False
  131             ds.relax_gui.analyses[data_index].insignificance = 1.0
  132             ds.relax_gui.analyses[data_index].save_dir = self.gui.system_cwd_path
  133 
  134             # Set the default dispersion models based on the experiment type.
  135             ds.relax_gui.analyses[data_index].disp_models = [
  136                 MODEL_R2EFF,
  137                 MODEL_NOREX,
  138                 MODEL_CR72,
  139                 MODEL_NS_CPMG_2SITE_EXPANDED,
  140                 MODEL_MP05,
  141                 MODEL_NS_R1RHO_2SITE
  142             ]
  143 
  144         # Error checking.
  145         if ds.relax_gui.analyses[data_index].pipe_bundle == None:
  146             raise RelaxError("The pipe bundle must be supplied.")
  147 
  148         # Alias the data.
  149         self.data = ds.relax_gui.analyses[data_index]
  150         self.data_index = data_index
  151 
  152         # Register the method for updating the spin count for the completion of user functions.
  153         self.observer_register()
  154 
  155         # Execute the base class method to build the panel.
  156         super(Auto_relax_disp, self).__init__(parent, id=id, pos=pos, size=size, style=style, name=name)
  157 
  158         # Optimisation variables for speeding up the test suite.
  159         self.opt_func_tol = 1e-25
  160         self.opt_max_iterations = int(1e7)
  161 
  162         # Update the isotope and cluster information.
  163         self.update_clusters()
  164 
  165 
  166     def activate(self):
  167         """Activate or deactivate certain elements of the analysis in response to the execution lock."""
  168 
  169         # Flag for enabling or disabling the elements.
  170         enable = False
  171         if not status.exec_lock.locked():
  172             enable = True
  173 
  174         # Activate or deactivate the elements.
  175         wx.CallAfter(self.field_results_dir.Enable, enable)
  176         wx.CallAfter(self.field_pre_run_dir.Enable, enable)
  177         wx.CallAfter(self.spin_systems.Enable, enable)
  178         wx.CallAfter(self.field_cluster.Enable, enable)
  179         wx.CallAfter(self.button_isotope.Enable, enable)
  180         wx.CallAfter(self.button_r1.Enable, enable)
  181         wx.CallAfter(self.button_chemical_shift.Enable, enable)
  182         wx.CallAfter(self.button_interatom_define.Enable, enable)
  183         wx.CallAfter(self.peak_intensity.Enable, enable)
  184         wx.CallAfter(self.model_field.Enable, enable)
  185         wx.CallAfter(self.button_exec_relax.Enable, enable)
  186 
  187 
  188     def add_buttons(self, box):
  189         """Add all of the buttons.
  190 
  191         @param box:     The box element to pack the GUI element into.
  192         @type box:      wx.BoxSizer instance
  193         """
  194 
  195         # Sizer.
  196         sizer = wx.BoxSizer(wx.HORIZONTAL)
  197 
  198         # Isotope type button.
  199         self.button_isotope = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, " Spin isotope")
  200         self.button_isotope.SetBitmapLabel(wx.Bitmap(fetch_icon("relax.nuclear_symbol", "22x22"), wx.BITMAP_TYPE_ANY))
  201         self.button_isotope.SetFont(font.normal)
  202         self.button_isotope.SetSize((-1, 25))
  203         self.button_isotope.SetToolTip(wx.ToolTip("Set the nuclear isotope types via the spin.isotope user function."))
  204         self.gui.Bind(wx.EVT_BUTTON, self.spin_isotope, self.button_isotope)
  205         sizer.Add(self.button_isotope, 1, wx.ALL|wx.EXPAND, 0)
  206 
  207         # R1 button.
  208         self.button_r1 = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, " %s relaxation data"%r1)
  209         self.button_r1.SetBitmapLabel(wx.Bitmap(fetch_icon("relax.fid", "22x22"), wx.BITMAP_TYPE_ANY))
  210         self.button_r1.SetFont(font.normal)
  211         self.button_r1.SetSize((-1, 25))
  212         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)))
  213         self.gui.Bind(wx.EVT_BUTTON, self.load_r1_data, self.button_r1)
  214         sizer.Add(self.button_r1, 1, wx.ALL|wx.EXPAND, 0)
  215 
  216         # Chemical shift button.
  217         self.button_chemical_shift = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, " Chemical shift")
  218         self.button_chemical_shift.SetBitmapLabel(wx.Bitmap(fetch_icon("relax.chemical_shift", "22x22"), wx.BITMAP_TYPE_ANY))
  219         self.button_chemical_shift.SetFont(font.normal)
  220         self.button_chemical_shift.SetSize((-1, 25))
  221         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))
  222         self.gui.Bind(wx.EVT_BUTTON, self.load_cs_data, self.button_chemical_shift)
  223         sizer.Add(self.button_chemical_shift, 1, wx.ALL|wx.EXPAND, 0)
  224 
  225         # Interatomic interaction button.
  226         self.button_interatom_define = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, " Interatomic interaction")
  227         self.button_interatom_define.SetBitmapLabel(wx.Bitmap(fetch_icon("relax.dipole_pair", "22x22"), wx.BITMAP_TYPE_ANY))
  228         self.button_interatom_define.SetFont(font.normal)
  229         self.button_interatom_define.SetSize((-1, 25))
  230         self.button_interatom_define.SetToolTip(wx.ToolTip("Define the interatomic interations via the interatom.define user function for the MQ dispersion models."))
  231         self.gui.Bind(wx.EVT_BUTTON, self.interatom_define, self.button_interatom_define)
  232         sizer.Add(self.button_interatom_define, 1, wx.ALL|wx.EXPAND, 0)
  233 
  234         # value.set button.
  235         self.button_value_set = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, " Value setting")
  236         self.button_value_set.SetBitmapLabel(wx.Bitmap(fetch_icon("relax.value", "22x22"), wx.BITMAP_TYPE_ANY))
  237         self.button_value_set.SetFont(font.normal)
  238         self.button_value_set.SetSize((-1, 25))
  239         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."
  240         self.button_value_set.SetToolTip(wx.ToolTip(tooltip))
  241         self.gui.Bind(wx.EVT_BUTTON, self.value_set, self.button_value_set)
  242         sizer.Add(self.button_value_set, 1, wx.ALL|wx.EXPAND, 0)
  243 
  244         # Add the element to the box.
  245         box.Add(sizer, 0, wx.ALL|wx.EXPAND, 0)
  246 
  247 
  248     def assemble_data(self):
  249         """Assemble the data required for the Auto_noe class.
  250 
  251         @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.
  252         @rtype:     class instance, list of str, list of str
  253         """
  254 
  255         # The data container.
  256         data = Container()
  257         missing = []
  258         model_mismatch = []
  259 
  260         # The pipe name and bundle.
  261         data.pipe_name = self.data.pipe_name
  262         data.pipe_bundle = self.data.pipe_bundle
  263 
  264         # Results directories.
  265         data.save_dir = self.data.save_dir
  266         data.pre_run_dir = gui_to_str(self.field_pre_run_dir.GetValue())
  267 
  268         # Check if sequence data is loaded
  269         if not exists_mol_res_spin_data():
  270             missing.append("Sequence data")
  271 
  272         # Spin variables.
  273         for spin, spin_id in spin_loop(return_id=True, skip_desel=True):
  274             # The message skeleton.
  275             msg = "Spin '%s' - %s (try the %s user function)." % (spin_id, "%s", "%s")
  276 
  277             # Test if the nuclear isotope type has been set.
  278             if not hasattr(spin, 'isotope') or spin.isotope == None:
  279                 missing.append(msg % ("nuclear isotope data", "spin.isotope"))
  280 
  281         # Spectral data.
  282         if not hasattr(cdp, 'spectrum_ids') or len(cdp.spectrum_ids) < 2:
  283             missing.append("Spectral data")
  284 
  285         # The dispersion models.
  286         data.models = self.model_field.GetValue()
  287 
  288         # Invalid models.
  289         for model in data.models:
  290             # Invalid CPMG models.
  291             if model != MODEL_NOREX and model in MODEL_LIST_CPMG and not has_cpmg_exp_type():
  292                 model_mismatch.append([model, 'CPMG'])
  293 
  294             # Invalid R1rho models.
  295             if model != MODEL_NOREX and model in MODEL_LIST_R1RHO and not has_r1rho_exp_type():
  296                 model_mismatch.append([model, 'R1rho'])
  297 
  298         # The R1 parameter fitting flag.
  299         data.r1_fit = self.r1_fit.GetValue()
  300 
  301         # The numeric only solution.
  302         data.numeric_only = self.numeric_only.GetValue()
  303 
  304         # Increment size.
  305         data.inc = gui_to_int(self.grid_inc.GetValue())
  306 
  307         # The number of Monte Carlo simulations to be used for error analysis at the end of the analysis.
  308         data.mc_sim_num = gui_to_int(self.mc_sim_num.GetValue())
  309         data.exp_mc_sim_num = gui_to_int(self.exp_mc_sim_num.GetValue())
  310         data.mc_sim_all_models = self.mc_sim_all_models.GetValue()
  311 
  312         # The insignificance level.
  313         data.insignificance = self.insignificance.GetValue()
  314         try:
  315             data.insignificance = gui_to_float(data.insignificance)
  316         except:
  317             missing.append("The insignificance level must be a number.")
  318 
  319         # Optimisation precision.
  320         data.opt_func_tol = self.opt_func_tol
  321         data.opt_max_iterations = self.opt_max_iterations
  322 
  323         # Return the container, the list of missing data, and any models that don't match the experiment types.
  324         return data, missing, model_mismatch
  325 
  326 
  327     def build_right_box(self):
  328         """Construct the right hand box to pack into the main relax_disp box.
  329 
  330         @return:    The right hand box element containing all relaxation dispersion GUI elements (excluding the bitmap) to pack into the main box.
  331         @rtype:     wx.BoxSizer instance
  332         """
  333 
  334         # Use a vertical packing of elements.
  335         box = wx.BoxSizer(wx.VERTICAL)
  336 
  337         # Add the frame title.
  338         self.add_title(box, "Relaxation dispersion analysis")
  339 
  340         # Display the data pipe.
  341         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)
  342 
  343         # Add the results directory GUI element.
  344         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)
  345 
  346         # Add the results directory GUI element.
  347         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."
  348         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)
  349 
  350         # Add the spin GUI element.
  351         self.add_spin_systems(box, self)
  352 
  353         # Spin cluster setup.
  354         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)
  355 
  356         # Add the buttons.
  357         box.AddSpacer(20)
  358         self.add_buttons(box=box)
  359 
  360         # Add the peak list selection GUI element, with spacing.
  361         box.AddSpacer(20)
  362         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)
  363         box.AddSpacer(10)
  364 
  365         # Add the dispersion models GUI element, with spacing.
  366         self.model_field = Disp_model_list(self, box)
  367         self.model_field.set_value(self.data.disp_models)
  368 
  369         # R1 parameter optimisation.
  370         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."
  371         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)
  372 
  373         # The numeric only solution.
  374         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."
  375         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)
  376 
  377         # The grid search optimisation settings.
  378         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)
  379 
  380         # The MC simulation settings.
  381         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)
  382         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)
  383         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)
  384 
  385         # The speed up of grid search.
  386         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)
  387         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)
  388 
  389         # The insignificance cutoff.
  390         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)
  391         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)
  392 
  393         # Stretchable spacing (with a minimal space).
  394         box.AddSpacer(30)
  395         box.AddStretchSpacer()
  396 
  397         # Add the execution GUI element.
  398         self.button_exec_relax = self.add_execute_analysis(box, self.execute)
  399 
  400         # Return the box.
  401         return box
  402 
  403 
  404     def delete(self):
  405         """Unregister the spin count from the user functions."""
  406 
  407         # Unregister the observer methods.
  408         self.observer_register(remove=True)
  409 
  410         # Clean up the peak intensity object.
  411         self.peak_intensity.delete()
  412 
  413         # Destroy the peak intensity wizard, if it exists.
  414         if hasattr(self, 'peak_wizard'):
  415             self.peak_wizard.Destroy()
  416             del self.peak_wizard
  417 
  418         # Destroy the model list windows.
  419         self.model_field.model_win.Destroy()
  420         del self.model_field
  421 
  422         # Destroy the missing data dialog, if present.
  423         if hasattr(self, 'missing_data'):
  424             self.missing_data.Destroy()
  425             del self.missing_data
  426 
  427 
  428     def execute(self, event):
  429         """Set up, execute, and process the automatic Rx analysis.
  430 
  431         @param event:   The wx event.
  432         @type event:    wx event
  433         """
  434 
  435         # Flush the GUI interpreter internal queue to make sure all user functions are complete.
  436         self.gui.interpreter.flush()
  437 
  438         # relax execution lock.
  439         if status.exec_lock.locked():
  440             error_message("relax is currently executing.", "relax execution lock")
  441             event.Skip()
  442             return
  443 
  444         # User warning to close windows.
  445         self.gui.close_windows()
  446 
  447         # Synchronise the frame data to the relax data store.
  448         self.sync_ds(upload=True)
  449 
  450         # Assemble all the data needed for the auto-analysis.
  451         data, missing, model_mismatch = self.assemble_data()
  452 
  453         # Missing data.
  454         if len(missing):
  455             self.missing_data = Missing_data(missing)
  456             return
  457 
  458         # Model mismatch.
  459         if len(model_mismatch):
  460             # Generate the text.
  461             text = ''
  462             for model, exp in model_mismatch:
  463                 text += "The '%s' %s model cannot be used as no %s experiment types have been set up.\n" % (model, exp, exp)
  464 
  465             # The error message.
  466             error_message(text, caption='Model mismatch')
  467             return
  468 
  469         # Display the relax controller, and go to the end of the log window.
  470         self.gui.show_controller(None)
  471         self.gui.controller.log_panel.on_goto_end(None)
  472 
  473         # Start the thread.
  474         self.thread = Execute_relax_disp(self.gui, data, self.data_index)
  475         self.thread.start()
  476 
  477         # Terminate the event.
  478         event.Skip()
  479 
  480 
  481     def interatom_define(self, event=None):
  482         """Define the interatomic interactions of the spins via the interatom.define user function.
  483 
  484         @keyword event: The wx event.
  485         @type event:    wx event
  486         """
  487 
  488         # Call the user function.
  489         uf_store['interatom.define'](wx_wizard_modal=True, spin_id1='@N', spin_id2='@H')
  490 
  491 
  492     def load_cs_data(self, event=None):
  493         """Read chemical shift data from a peak list via the chemical_shift.read user function.
  494 
  495         @keyword event: The wx event.
  496         @type event:    wx event
  497         """
  498 
  499         # Call the user function.
  500         uf_store['chemical_shift.read'](wx_wizard_modal=True)
  501 
  502 
  503     def load_r1_data(self, event=None):
  504         """Load R1 relaxation data via the relax_data.read user function.
  505 
  506         @keyword event: The wx event.
  507         @type event:    wx event
  508         """
  509 
  510         # Call the user function.
  511         uf_store['relax_data.read'](wx_wizard_modal=True, ri_type='R1')
  512 
  513 
  514     def observer_register(self, remove=False):
  515         """Register and unregister methods with the observer objects.
  516 
  517         @keyword remove:    If set to True, then the methods will be unregistered.
  518         @type remove:       False
  519         """
  520 
  521         # Register.
  522         if not remove:
  523             status.observers.gui_uf.register('spin count - %s' % self.data.pipe_bundle, self.update_spin_count, method_name='update_spin_count')
  524             status.observers.exec_lock.register(self.data.pipe_bundle, self.activate, method_name='activate')
  525             status.observers.gui_uf.register('clusters - %s' % self.data.pipe_bundle, self.update_clusters, method_name='update_clusters')
  526 
  527         # Unregister.
  528         else:
  529             # The methods.
  530             status.observers.gui_uf.unregister('spin count - %s' % self.data.pipe_bundle)
  531             status.observers.exec_lock.unregister(self.data.pipe_bundle)
  532             status.observers.gui_uf.unregister('isotopes - %s' % self.data.pipe_bundle)
  533             status.observers.gui_uf.unregister('clusters - %s' % self.data.pipe_bundle)
  534 
  535             # The embedded objects methods.
  536             self.peak_intensity.observer_register(remove=True)
  537 
  538 
  539     def peak_wizard_launch(self, event):
  540         """Launch the peak loading wizard.
  541 
  542         @param event:   The wx event.
  543         @type event:    wx event
  544         """
  545 
  546         # Destroy the peak intensity wizard, if it exists.
  547         if hasattr(self, 'peak_wizard'):
  548             self.peak_wizard.Destroy()
  549 
  550         # A new wizard instance.
  551         self.peak_wizard = Peak_intensity_wizard(relax_disp=True)
  552 
  553 
  554     def pre_run_directory(self, event):
  555         """The pre-run directory selection.
  556 
  557         @param event:   The wx event.
  558         @type event:    wx event
  559         """
  560 
  561         # The dialog.
  562         dialog = RelaxDirDialog(parent=self, message='Select the directory of the previous run', defaultPath=self.field_pre_run_dir.GetValue())
  563 
  564         # Show the dialog and catch if no file has been selected.
  565         if status.show_gui and dialog.ShowModal() != wx.ID_OK:
  566             # Don't do anything.
  567             return
  568 
  569         # The path (don't do anything if not set).
  570         path = gui_to_str(dialog.get_path())
  571         if not path:
  572             return
  573 
  574         # Place the path in the text box.
  575         self.field_pre_run_dir.SetValue(str_to_gui(path))
  576 
  577 
  578     def relax_disp_cluster(self, event=None):
  579         """Set up spin clustering via the relax_disp.cluster user function.
  580 
  581         @keyword event: The wx event.
  582         @type event:    wx event
  583         """
  584 
  585         # Call the user function.
  586         uf_store['relax_disp.cluster'](wx_wizard_modal=True)
  587 
  588 
  589     def results_directory(self, event):
  590         """The results directory selection.
  591 
  592         @param event:   The wx event.
  593         @type event:    wx event
  594         """
  595 
  596         # The dialog.
  597         dialog = RelaxDirDialog(parent=self, message='Select the results directory', defaultPath=self.field_results_dir.GetValue())
  598 
  599         # Show the dialog and catch if no file has been selected.
  600         if status.show_gui and dialog.ShowModal() != wx.ID_OK:
  601             # Don't do anything.
  602             return
  603 
  604         # The path (don't do anything if not set).
  605         path = gui_to_str(dialog.get_path())
  606         if not path:
  607             return
  608 
  609         # Store the path.
  610         self.data.save_dir = path
  611 
  612         # Place the path in the text box.
  613         self.field_results_dir.SetValue(str_to_gui(path))
  614 
  615 
  616     def spin_isotope(self, event=None):
  617         """Set the nuclear isotope types of the spins via the spin.isotope user function.
  618 
  619         @keyword event: The wx event.
  620         @type event:    wx event
  621         """
  622 
  623         # Call the user function.
  624         uf_store['spin.isotope'](wx_wizard_modal=True, isotope='15N', spin_id='@N*')
  625 
  626 
  627     def sync_ds(self, upload=False):
  628         """Synchronise the analysis frame and the relax data store, both ways.
  629 
  630         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.
  631 
  632         @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.
  633         @type upload:       bool
  634         """
  635 
  636         # The R1 parameter fitting flag.
  637         if upload:
  638             self.data.r1_fit = self.r1_fit.GetValue()
  639         elif hasattr(self.data, 'r1_fit'):
  640             self.r1_fit.SetValue(bool(self.data.r1_fit))
  641 
  642         # The numeric solution only flag.
  643         if upload:
  644             self.data.numeric_only = self.numeric_only.GetValue()
  645         elif hasattr(self.data, 'numeric_only'):
  646             self.numeric_only.SetValue(bool(self.data.numeric_only))
  647 
  648         # The grid incs.
  649         if upload:
  650             self.data.grid_inc = gui_to_int(self.grid_inc.GetValue())
  651         elif hasattr(self.data, 'grid_inc'):
  652             if self.data.grid_inc == None:
  653                 self.data.grid_inc = default_grid_inc
  654             self.grid_inc.SetValue(int(self.data.grid_inc))
  655 
  656         # The MC sim number.
  657         if upload:
  658             self.data.mc_sim_num = gui_to_int(self.mc_sim_num.GetValue())
  659         elif hasattr(self.data, 'mc_sim_num'):
  660             if self.data.mc_sim_num == None:
  661                 self.data.mc_sim_num = default_mc_sim_num
  662             self.mc_sim_num.SetValue(int(self.data.mc_sim_num))
  663 
  664         # The EXP MC sim number.
  665         if upload:
  666             self.data.exp_mc_sim_num = gui_to_int(self.exp_mc_sim_num.GetValue())
  667         elif hasattr(self.data, 'exp_mc_sim_num'):
  668             if self.data.exp_mc_sim_num == None:
  669                 self.data.exp_mc_sim_num = default_exp_mc_sim_num
  670             self.exp_mc_sim_num.SetValue(int(self.data.exp_mc_sim_num))
  671 
  672         # The All model MC sim flag.
  673         if upload:
  674             self.data.mc_sim_all_models = self.mc_sim_all_models.GetValue()
  675         elif hasattr(self.data, 'mc_sim_all_models'):
  676             self.mc_sim_all_models.SetValue(bool(self.data.mc_sim_all_models))
  677 
  678         # The insignificance level.
  679         if upload:
  680             self.data.insignificance = self.insignificance.GetValue()
  681             try:
  682                 self.data.insignificance = gui_to_float(self.data.insignificance)
  683             except:
  684                 pass
  685         elif hasattr(self.data, 'insignificance'):
  686             self.insignificance.SetValue(float_to_gui(self.data.insignificance))
  687 
  688         # The results directory.
  689         if upload:
  690             self.data.save_dir = gui_to_str(self.field_results_dir.GetValue())
  691         else:
  692             self.field_results_dir.SetValue(str_to_gui(self.data.save_dir))
  693 
  694         # The previous run results directory.
  695         if upload:
  696             self.data.pre_run_dir = gui_to_str(self.field_pre_run_dir.GetValue())
  697         elif hasattr(self.data, 'pre_run_dir'):
  698             self.field_pre_run_dir.SetValue(str_to_gui(self.data.pre_run_dir))
  699 
  700         # The models to use.
  701         if upload:
  702             self.data.disp_models = self.model_field.GetValue()
  703         else:
  704             self.model_field.set_value(self.data.disp_models)
  705 
  706 
  707     def update_clusters(self):
  708         """Update the cluster field."""
  709 
  710         # Assemble a list of all unique isotope types.
  711         cluster_keys = []
  712         if hasattr(cdp, 'clustering'):
  713             cluster_keys = sorted(cdp.clustering.keys())
  714 
  715         # Nothing yet.
  716         if not len(cluster_keys):
  717             wx.CallAfter(self.field_cluster.SetValue, "free spins")
  718 
  719         # List the clusters.
  720         else:
  721             # Build the text to show.
  722             text = ""
  723             if "free spins" in cluster_keys:
  724                 text += "free spins"
  725             for i in range(len(cluster_keys)):
  726                 if cluster_keys[i] != "free spins":
  727                     text += ", %s" % cluster_keys[i]
  728 
  729             # Update the text.
  730             wx.CallAfter(self.field_cluster.SetValue, text)
  731 
  732 
  733     def value_set(self, event=None):
  734         """Launch the value.set user function.
  735 
  736         @keyword event: The wx event.
  737         @type event:    wx event
  738         """
  739 
  740         # Call the user function.
  741         uf_store['value.set'](wx_wizard_modal=True)
  742 
  743 
  744 
  745 class Execute_relax_disp(Execute):
  746     """The relaxation dispersion analysis execution object."""
  747 
  748     def run_analysis(self):
  749         """Execute the calculation."""
  750 
  751         # Optimisation precision.
  752         Relax_disp.opt_func_tol = self.data.opt_func_tol
  753         Relax_disp.opt_max_iterations = self.data.opt_max_iterations
  754 
  755         # Execute.
  756         Relax_disp(pipe_name=self.data.pipe_name, pipe_bundle=self.data.pipe_bundle, results_dir=self.data.save_dir, models=self.data.models, grid_inc=self.data.inc, mc_sim_num=self.data.mc_sim_num, exp_mc_sim_num=self.data.exp_mc_sim_num, pre_run_dir=self.data.pre_run_dir, mc_sim_all_models=self.data.mc_sim_all_models, insignificance=self.data.insignificance, numeric_only=self.data.numeric_only, r1_fit=self.data.r1_fit)
  757 
  758         # Alias the relax data store data.
  759         data = ds.relax_gui.analyses[self.data_index]
  760 
  761 
  762 
  763 class Disp_model_list(Model_list):
  764     """The diffusion model list GUI element."""
  765 
  766     # Class variables.
  767     desc = "Relaxation dispersion models:"
  768     models = [
  769         MODEL_R2EFF,
  770         None,
  771         MODEL_NOREX,
  772         None,
  773         MODEL_LM63,
  774         MODEL_LM63_3SITE,
  775         MODEL_CR72,
  776         MODEL_CR72_FULL,
  777         MODEL_IT99,
  778         MODEL_TSMFK01,
  779         MODEL_B14,
  780         MODEL_B14_FULL,
  781         MODEL_NS_CPMG_2SITE_EXPANDED,
  782         MODEL_NS_CPMG_2SITE_3D,
  783         MODEL_NS_CPMG_2SITE_3D_FULL,
  784         MODEL_NS_CPMG_2SITE_STAR,
  785         MODEL_NS_CPMG_2SITE_STAR_FULL,
  786         None,
  787         MODEL_M61,
  788         MODEL_M61B,
  789         MODEL_DPL94,
  790         MODEL_TP02,
  791         MODEL_TAP03,
  792         MODEL_MP05,
  793         MODEL_NS_R1RHO_2SITE,
  794         MODEL_NS_R1RHO_3SITE_LINEAR,
  795         MODEL_NS_R1RHO_3SITE,
  796         None,
  797         MODEL_MMQ_CR72,
  798         MODEL_NS_MMQ_2SITE,
  799         MODEL_NS_MMQ_3SITE_LINEAR,
  800         MODEL_NS_MMQ_3SITE
  801     ]
  802     params = [
  803         "{%s/%s, %s}" % (r2eff, r1rho, i0),
  804         None,
  805         "{%s, ...}" % (r2),
  806         None,
  807         "{%s, ..., %s, %s}" % (r2, phi_ex, kex),
  808         "{%s, ..., %s, kB, %s, kC}" % (r2, phi_exB, phi_exC),
  809         "{%s, ..., pA, %s, %s}" % (r2, dw, kex),
  810         "{%s, %s, ..., pA, %s, %s}" % (r2a, r2b, dw, kex),
  811         "{%s, ..., %s, %s, %s}" % (r2, phi_ex, padw2, kex),
  812         "{%s, ..., %s, k_AB}" % (r2a, dw),
  813         "{%s, ..., pA, %s, %s}" % (r2, dw, kex),
  814         "{%s, %s, ..., pA, %s, %s}" % (r2a, r2b, dw, kex),
  815         "{%s, ..., pA, %s, %s}" % (r2, dw, kex),
  816         "{%s, ..., pA, %s, %s}" % (r2, dw, kex),
  817         "{%s, %s, ..., pA, %s, %s}" % (r2a, r2b, dw, kex),
  818         "{%s, ..., pA, %s, %s}" % (r2, dw, kex),
  819         "{%s, %s, ..., pA, %s, %s}" % (r2a, r2b, dw, kex),
  820         None,
  821         "{%s, ..., %s, %s}" % (r1rho_prime, phi_ex, kex),
  822         "{%s, ..., pA, %s, %s}" % (r1rho_prime, dw, kex),
  823         "{%s, ..., %s, %s}" % (r1rho_prime, phi_ex, kex),
  824         "{%s, ..., pA, %s, %s}" % (r1rho_prime, dw, kex),
  825         "{%s, ..., pA, %s, %s}" % (r1rho_prime, dw, kex),
  826         "{%s, ..., pA, %s, %s}" % (r1rho_prime, dw, kex),
  827         "{%s, ..., pA, %s, %s}" % (r1rho_prime, dw, kex),
  828         "{%s, ..., pA, %s, %s, pB, %s, %s}" % (r1rho_prime, dw_AB, kAB, dw_BC, kBC),
  829         "{%s, ..., pA, %s, %s, pB, %s, %s, %s}" % (r1rho_prime, dw_AB, kAB, dw_BC, kBC, kAC),
  830         None,
  831         "{%s, ..., pA, %s, %s, %s}" % (r2, dw, dwH, kex),
  832         "{%s, ..., pA, %s, %s, %s}" % (r2, dw, dwH, kex),
  833         "{%s, ..., pA, %s, %s, %s, pB, %s, %s, %s}" % (r2, dw_AB, dwH_AB, kAB, dw_BC, dwH_BC, kBC),
  834         "{%s, ..., pA, %s, %s, %s, pB, %s, %s, %s, %s}" % (r2, dw_AB, dwH_AB, kAB, dw_BC, dwH_BC, kBC, kAC)
  835     ]
  836     model_desc = [
  837         "The base model for determining the %s/%s values and errors for all other models." % (r2eff, r1rho),
  838         None,
  839         "The model for no chemical exchange relaxation.",
  840         None,
  841         "The original Luz and Meiboom (1963) 2-site fast exchange equation.",
  842         "The original Luz and Meiboom (1963) 3-site fast exchange equation.",
  843         "The Carver and Richards (1972) 2-site equation for all time scales (with %s = %s)." % (r2a, r2b),
  844         "The Carver and Richards (1972) 2-site equation for all time scales.",
  845         "The Ishima and Torchia (1999) 2-site model for all time scales with pA >> pB.",
  846         "The Tollinger et al. (2001) 2-site very-slow exchange model.",
  847         "The Baldwin (2014) 2-site exact solution model for all time scales (with %s = %s)." % (r2a, r2b),
  848         "The Baldwin (2014) 2-site exact solution model for all time scales.",
  849         "The 2-site numerical solution expanded using Maple by Nikolai Skrynnikov.",
  850         "The 2-site numerical solution using 3D magnetisation vectors (with %s = %s)." % (r2a, r2b),
  851         "The 2-site numerical solution using 3D magnetisation vectors.",
  852         "The 2-site numerical solution using complex conjugate matrices (with %s = %s)." % (r2a, r2b),
  853         "The 2-site numerical solution using complex conjugate matrices.",
  854         None,
  855         "The Meiboom (1961) 2-site fast exchange equation.",
  856         "The Meiboom (1961) 2-site equation for all time scales with pA >> pB.",
  857         "The Davis, Perlman and London (1994) 2-site fast exchange equation.",
  858         "The Trott and Palmer (2002) 2-site equation for all time scales.",
  859         "The Trott, Abergel and Palmer (2003) off-resonance 2-site equation for all time scales.",
  860         "The Miloushev and Palmer (2005) off-resonance 2-site equation for all time scales.",
  861         "The 2-site numerical solution using 3D magnetisation vectors.",
  862         "The 3-site linearised numerical solution using 3D magnetisation vectors.",
  863         "The 3-site numerical solution using 3D magnetisation vectors.",
  864         None,
  865         "The CR72 2-site model extended to MMQ CPMG data by Korzhnev et al., 2004.",
  866         "The 2-site numerical solution of Korzhnev et al. (2004) from multi-quantum CPMG data.",
  867         "The 3-site linearised numerical solution of Korzhnev et al. (2005) for MMQ CPMG data.",
  868         "The 3-site numerical solution of Korzhnev et al. (2005) for MMQ CPMG data."
  869     ]
  870     size = wx.Size(1024, 750)
  871     tooltip = "The list of all relaxation dispersion models to be optimised as part of the protocol."
  872     tooltip_button = "Open the model list selector window."