source: sasview/src/sas/perspectives/fitting/fitting.py @ 78f75d02

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 78f75d02 was f21d496, checked in by Doucet, Mathieu <doucetm@…>, 9 years ago

Cleaning up old "standalone" code

  • Property mode set to 100644
File size: 83.1 KB
RevLine 
[f76bf17]1"""
2    Fitting perspective
3"""
4################################################################################
5#This software was developed by the University of Tennessee as part of the
6#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
7#project funded by the US National Science Foundation.
8#
9#See the license text in license.txt
10#
11#copyright 2009, University of Tennessee
12################################################################################
13import re
14import sys
15import os
16import wx
17import logging
18import numpy
19import time
20from copy import deepcopy
21import models
22
23from sas.dataloader.loader import Loader
24from sas.guiframe.dataFitting import Data2D
25from sas.guiframe.dataFitting import Data1D
26from sas.guiframe.dataFitting import check_data_validity
27from sas.guiframe.events import NewPlotEvent
28from sas.guiframe.events import StatusEvent
29from sas.guiframe.events import EVT_SLICER_PANEL
30from sas.guiframe.events import EVT_SLICER_PARS_UPDATE
31from sas.guiframe.gui_style import GUIFRAME_ID
32from sas.guiframe.plugin_base import PluginBase
33from sas.guiframe.data_processor import BatchCell
[acf8e4a5]34from sas.fit.BumpsFitting import BumpsFit as Fit
[f76bf17]35from sas.perspectives.fitting.console import ConsoleUpdate
36from sas.perspectives.fitting.fitproblem import FitProblemDictionary
37from sas.perspectives.fitting.fitpanel import FitPanel
38from sas.perspectives.fitting.resultpanel import ResultPanel, PlotResultEvent
39
40from sas.perspectives.fitting.fit_thread import FitThread
41from sas.perspectives.fitting.pagestate import Reader
42from sas.perspectives.fitting.fitpage import Chi2UpdateEvent
43from sas.perspectives.calculator.model_editor import TextDialog
44from sas.perspectives.calculator.model_editor import EditorWindow
45from sas.guiframe.gui_manager import MDIFrame
46
47MAX_NBR_DATA = 4
48
49(PageInfoEvent, EVT_PAGE_INFO) = wx.lib.newevent.NewEvent()
50
51
52if sys.platform == "win32":
53    ON_MAC = False
54else:
55    ON_MAC = True
56
57
58
59class Plugin(PluginBase):
60    """
61    Fitting plugin is used to perform fit
62    """
[f21d496]63    def __init__(self):
64        PluginBase.__init__(self, name="Fitting")
[f76bf17]65
66        #list of panel to send to guiframe
67        self.mypanels = []
68        # reference to the current running thread
69        self.calc_2D = None
70        self.calc_1D = None
71
72        self.color_dict = {}
73
74        self.fit_thread_list = {}
75        self.residuals = None
76        self.weight = None
77        self.fit_panel = None
78        self.plot_panel = None
79        # Start with a good default
80        self.elapsed = 0.022
81        self.fit_panel = None
82        ## dictionary of page closed and id
83        self.closed_page_dict = {}
84        ## Relative error desired in the sum of squares (float)
85        self.batch_reset_flag = True
86        #List of selected data
87        self.selected_data_list = []
88        ## list of slicer panel created to display slicer parameters and results
89        self.slicer_panels = []
90        # model 2D view
91        self.model2D_id = None
92        #keep reference of the simultaneous fit page
93        self.sim_page = None
94        self.sim_menu = None
95        self.batch_page = None
96        self.batch_menu = None
97        self.index_model = 0
98        self.test_model_color = None
99        #Create a reader for fit page's state
100        self.state_reader = None
101        self._extensions = '.fitv'
102        self.menu1 = None
103        self.new_model_frame = None
104
105        self.temp_state = []
106        self.state_index = 0
107        self.sfile_ext = None
108        # take care of saving  data, model and page associated with each other
109        self.page_finder = {}
110        # Log startup
111        logging.info("Fitting plug-in started")
112        self.batch_capable = self.get_batch_capable()
113
114    def get_batch_capable(self):
115        """
116        Check if the plugin has a batch capability
117        """
118        return True
119
120    def create_fit_problem(self, page_id):
121        """
122        Given an ID create a fitproblem container
123        """
124        self.page_finder[page_id] = FitProblemDictionary()
125
126    def delete_fit_problem(self, page_id):
127        """
128        Given an ID create a fitproblem container
129        """
130        if page_id in self.page_finder.iterkeys():
131            del self.page_finder[page_id]
132
133    def add_color(self, color, id):
134        """
135        adds a color as a key with a plot id as its value to a dictionary
136        """
137        self.color_dict[id] = color
138
139    def on_batch_selection(self, flag):
140        """
141        switch the the notebook of batch mode or not
142        """
143        self.batch_on = flag
144        if self.fit_panel is not None:
145            self.fit_panel.batch_on = self.batch_on
146
147    def populate_menu(self, owner):
148        """
149        Create a menu for the Fitting plug-in
150
151        :param id: id to create a menu
152        :param owner: owner of menu
153
154        :return: list of information to populate the main menu
155
156        """
157        #Menu for fitting
158        self.menu1 = wx.Menu()
159        id1 = wx.NewId()
160        simul_help = "Add new fit panel"
161        self.menu1.Append(id1, '&New Fit Page', simul_help)
162        wx.EVT_MENU(owner, id1, self.on_add_new_page)
163        self.menu1.AppendSeparator()
164        self.id_simfit = wx.NewId()
165        simul_help = "Constrained or Simultaneous Fit"
166        self.menu1.Append(self.id_simfit, '&Constrained or Simultaneous Fit', simul_help)
167        wx.EVT_MENU(owner, self.id_simfit, self.on_add_sim_page)
168        self.sim_menu = self.menu1.FindItemById(self.id_simfit)
169        self.sim_menu.Enable(False)
170        #combined Batch
171        self.id_batchfit = wx.NewId()
172        batch_help = "Combined Batch"
173        self.menu1.Append(self.id_batchfit, '&Combine Batch Fit', batch_help)
174        wx.EVT_MENU(owner, self.id_batchfit, self.on_add_sim_page)
175        self.batch_menu = self.menu1.FindItemById(self.id_batchfit)
176        self.batch_menu.Enable(False)
177
178        self.menu1.AppendSeparator()
179        self.id_bumps_options = wx.NewId()
180        bopts_help = "Fitting options"
181        self.menu1.Append(self.id_bumps_options, 'Fit &Options', bopts_help)
182        wx.EVT_MENU(owner, self.id_bumps_options, self.on_bumps_options)
183        self.bumps_options_menu = self.menu1.FindItemById(self.id_bumps_options)
184        self.bumps_options_menu.Enable(True)
185
186        self.id_result_panel = wx.NewId()
187        self.menu1.Append(self.id_result_panel, "Fit Results", "Show fit results panel")
[9776c82]188        wx.EVT_MENU(owner, self.id_result_panel, self.on_fit_results)
[f76bf17]189        self.menu1.AppendSeparator()
190
191        self.id_reset_flag = wx.NewId()
192        resetf_help = "BatchFit: If checked, the initial param values will be "
193        resetf_help += "propagated from the previous results. "
194        resetf_help += "Otherwise, the same initial param values will be used "
195        resetf_help += "for all fittings."
196        self.menu1.AppendCheckItem(self.id_reset_flag,
197                                   "Chain Fitting [BatchFit Only]",
198                                   resetf_help)
199        wx.EVT_MENU(owner, self.id_reset_flag, self.on_reset_batch_flag)
200        chain_menu = self.menu1.FindItemById(self.id_reset_flag)
201        chain_menu.Check(not self.batch_reset_flag)
202        chain_menu.Enable(self.batch_on)
203
204        self.menu1.AppendSeparator()
205        self.edit_model_menu = wx.Menu()
206        # Find and put files name in menu
207        try:
208            self.set_edit_menu(owner=owner)
209        except:
210            raise
211
212        self.id_edit = wx.NewId()
213        editmodel_help = "Edit customized model sample file"
214        self.menu1.AppendMenu(self.id_edit, "Edit Custom Model",
215                              self.edit_model_menu, editmodel_help)
216        #create  menubar items
217        return [(self.menu1, self.sub_menu)]
218
219    def edit_custom_model(self, event):
220        """
221        Get the python editor panel
222        """
[f21d496]223        event_id = event.GetId()
224        label = self.edit_menu.GetLabel(event_id)
[f76bf17]225        from sas.perspectives.calculator.pyconsole import PyConsole
226        filename = os.path.join(models.find_plugins_dir(), label)
227        frame = PyConsole(parent=self.parent, manager=self,
228                          panel=self.fit_panel,
229                          title='Advanced Custom Model Editor',
230                          filename=filename)
231        self.put_icon(frame)
232        frame.Show(True)
233
234    def delete_custom_model(self, event):
235        """
236        Delete custom model file
237        """
[f21d496]238        event_id = event.GetId()
239        label = self.delete_menu.GetLabel(event_id)
[f76bf17]240        toks = os.path.splitext(label)
241        path = os.path.join(models.find_plugins_dir(), toks[0])
242        try:
243            for ext in ['.py', '.pyc']:
244                p_path = path + ext
245                os.remove(p_path)
246            self.update_custom_combo()
247            if os.path.isfile(p_path):
248                msg = "Sorry! Could not be able to delete the default "
249                msg += "custom model... \n"
250                msg += "Please manually remove the files (.py, .pyc) "
251                msg += "in the 'plugin_models' folder \n"
252                msg += "inside of the SasView application, "
253                msg += "and try it again."
254                wx.MessageBox(msg, 'Info')
255                #wx.PostEvent(self.parent, StatusEvent(status=msg, type='stop',
256                #                                      info='warning'))
257            else:
[f21d496]258                self.delete_menu.Delete(event_id)
[f76bf17]259                for item in self.edit_menu.GetMenuItems():
260                    if item.GetLabel() == label:
261                        self.edit_menu.DeleteItem(item)
262                        msg = "The custom model, %s, has been deleted." % label
263                        wx.PostEvent(self.parent, StatusEvent(status=msg,
264                                                type='stop', info='info'))
265                        break
266        except:
267            msg = 'Delete Error: \nCould not delete the file; Check if in use.'
268            wx.MessageBox(msg, 'Error')
269
270    def make_sum_model(self, event):
271        """
272        Edit summodel template and make one
273        """
[f21d496]274        event_id = event.GetId()
[f76bf17]275        model_manager = models.ModelManager()
276        model_list = model_manager.get_model_name_list()
277        plug_dir = models.find_plugins_dir()
278        textdial = TextDialog(None, self, -1, 'Easy Sum/Multi(p1, p2) Editor',
279                              model_list, plug_dir)
280        self.put_icon(textdial)
281        textdial.ShowModal()
282        textdial.Destroy()
283
284    def make_new_model(self, event):
285        """
286        Make new model
287        """
288        if self.new_model_frame != None:
289            self.new_model_frame.Show(False)
290            self.new_model_frame.Show(True)
291        else:
[f21d496]292            event_id = event.GetId()
[f76bf17]293            dir_path = models.find_plugins_dir()
294            title = "New Custom Model Function"
295            self.new_model_frame = EditorWindow(parent=self, base=self,
296                                                path=dir_path, title=title)
297            self.put_icon(self.new_model_frame)
298        self.new_model_frame.Show(True)
299
300    def update_custom_combo(self):
301        """
302        Update custom model list in the fitpage combo box
303        """
304        custom_model = 'Customized Models'
305        try:
306            # Update edit menus
307            self.set_edit_menu_helper(self.parent, self.edit_custom_model)
308            self.set_edit_menu_helper(self.parent, self.delete_custom_model)
309            temp = self.fit_panel.reset_pmodel_list()
310            if temp:
311                # Set the new custom model list for all fit pages
312                for uid, page in self.fit_panel.opened_pages.iteritems():
313                    if hasattr(page, "formfactorbox"):
314                        page.model_list_box = temp
315                        current_val = page.formfactorbox.GetLabel()
316                        #if page.plugin_rbutton.GetValue():
317                        mod_cat = page.categorybox.GetStringSelection()
318                        if mod_cat == custom_model:
319                            #pos = page.formfactorbox.GetSelection()
320                            page._show_combox_helper()
321                            new_val = page.formfactorbox.GetLabel()
322                            if current_val != new_val and new_val != '':
323                                page.formfactorbox.SetLabel(new_val)
324                            else:
325                                page.formfactorbox.SetLabel(current_val)
326        except:
327            pass
328
329
330    def set_edit_menu(self, owner):
331        """
332        Set list of the edit model menu labels
333        """
[f21d496]334        wx_id = wx.NewId()
[f76bf17]335        #new_model_menu = wx.Menu()
[f21d496]336        self.edit_model_menu.Append(wx_id, 'New',
[f76bf17]337                                   'Add a new model function')
[f21d496]338        wx.EVT_MENU(owner, wx_id, self.make_new_model)
339        wx_id = wx.NewId()
340        self.edit_model_menu.Append(wx_id, 'Sum|Multi(p1, p2)',
[f76bf17]341                                    'Sum of two model functions')
[f21d496]342        wx.EVT_MENU(owner, wx_id, self.make_sum_model)
[f76bf17]343        e_id = wx.NewId()
344        self.edit_menu = wx.Menu()
345        self.edit_model_menu.AppendMenu(e_id,
346                                    'Advanced', self.edit_menu)
347        self.set_edit_menu_helper(owner, self.edit_custom_model)
348
349        d_id = wx.NewId()
350        self.delete_menu = wx.Menu()
351        self.edit_model_menu.AppendMenu(d_id,
352                                        'Delete', self.delete_menu)
353        self.set_edit_menu_helper(owner, self.delete_custom_model)
354
355    def set_edit_menu_helper(self, owner=None, menu=None):
356        """
357        help for setting list of the edit model menu labels
358        """
359        if menu == None:
360            menu = self.edit_custom_model
361        list_fnames = os.listdir(models.find_plugins_dir())
362        list_fnames.sort()
363        for f_item in list_fnames:
364            name = os.path.basename(f_item)
365            toks = os.path.splitext(name)
366            if toks[-1] == '.py' and not toks[0] == '__init__':
367                if menu == self.edit_custom_model:
368                    if toks[0] == 'easy_sum_of_p1_p2':
369                        continue
370                    submenu = self.edit_menu
371                else:
372                    submenu = self.delete_menu
373                has_file = False
374                for item in submenu.GetMenuItems():
375                    if name == submenu.GetLabel(item.GetId()):
376                        has_file = True
377                if not has_file:
[f21d496]378                    wx_id = wx.NewId()
379                    submenu.Append(wx_id, name)
380                    wx.EVT_MENU(owner, wx_id, menu)
[f76bf17]381                    has_file = False
382
383    def put_icon(self, frame):
384        """
385        Put icon in the frame title bar
386        """
387        if hasattr(frame, "IsIconized"):
388            if not frame.IsIconized():
389                try:
390                    icon = self.parent.GetIcon()
391                    frame.SetIcon(icon)
392                except:
393                    pass
394
395    def on_add_sim_page(self, event):
396        """
397        Create a page to access simultaneous fit option
398        """
[f21d496]399        event_id = event.GetId()
[f76bf17]400        caption = "Const & Simul Fit"
401        page = self.sim_page
[f21d496]402        if event_id == self.id_batchfit:
[f76bf17]403            caption = "Combined Batch"
404            page = self.batch_page
405
406        def set_focus_page(page):
407            page.Show(True)
408            page.Refresh()
409            page.SetFocus()
410            #self.parent._mgr.Update()
411            msg = "%s already opened\n" % str(page.window_caption)
412            wx.PostEvent(self.parent, StatusEvent(status=msg))
413
414        if page != None:
415            return set_focus_page(page)
416        if caption == "Const & Simul Fit":
417            self.sim_page = self.fit_panel.add_sim_page(caption=caption)
418        else:
419            self.batch_page = self.fit_panel.add_sim_page(caption=caption)
420
421    def get_context_menu(self, plotpanel=None):
422        """
423        Get the context menu items available for P(r).them allow fitting option
424        for Data2D and Data1D only.
425
426        :param graph: the Graph object to which we attach the context menu
427
428        :return: a list of menu items with call-back function
429
430        :note: if Data1D was generated from Theory1D
431                the fitting option is not allowed
432
433        """
434        self.plot_panel = plotpanel
435        graph = plotpanel.graph
436        fit_option = "Select data for fitting"
437        fit_hint = "Dialog with fitting parameters "
438
439        if graph.selected_plottable not in plotpanel.plots:
440            return []
441        item = plotpanel.plots[graph.selected_plottable]
442        if item.__class__.__name__ is "Data2D":
443            if hasattr(item, "is_data"):
444                if item.is_data:
445                    return [[fit_option, fit_hint, self._onSelect]]
446                else:
447                    return []
448            return [[fit_option, fit_hint, self._onSelect]]
449        else:
450
451            # if is_data is true , this in an actual data loaded
452            #else it is a data created from a theory model
453            if hasattr(item, "is_data"):
454                if item.is_data:
455                    return [[fit_option, fit_hint, self._onSelect]]
456                else:
457                    return []
458            return [[fit_option, fit_hint, self._onSelect]]
459        return []
460
461    def get_panels(self, parent):
462        """
463        Create and return a list of panel objects
464        """
465        self.parent = parent
466        #self.parent.Bind(EVT_FITSTATE_UPDATE, self.on_set_state_helper)
467        # Creation of the fit panel
468        self.frame = MDIFrame(self.parent, None, 'None', (100, 200))
469        self.fit_panel = FitPanel(parent=self.frame, manager=self)
470        self.frame.set_panel(self.fit_panel)
471        self._frame_set_helper()
472        self.on_add_new_page(event=None)
473        #Set the manager for the main panel
474        self.fit_panel.set_manager(self)
475        # List of windows used for the perspective
476        self.perspective = []
477        self.perspective.append(self.fit_panel.window_name)
478
479        self.result_frame = MDIFrame(self.parent, None, ResultPanel.window_caption, (220, 200))
480        self.result_panel = ResultPanel(parent=self.result_frame, manager=self)
481        self.perspective.append(self.result_panel.window_name)
482
483        #index number to create random model name
484        self.index_model = 0
485        self.index_theory = 0
486        self.parent.Bind(EVT_SLICER_PANEL, self._on_slicer_event)
487        self.parent.Bind(EVT_SLICER_PARS_UPDATE, self._onEVT_SLICER_PANEL)
488        #self.parent._mgr.Bind(wx.aui.EVT_AUI_PANE_CLOSE,self._onclearslicer)
489        #Create reader when fitting panel are created
490        self.state_reader = Reader(self.set_state)
491        #append that reader to list of available reader
492        loader = Loader()
493        loader.associate_file_reader(".fitv", self.state_reader)
494        #Send the fitting panel to guiframe
495        self.mypanels.append(self.fit_panel)
496        self.mypanels.append(self.result_panel)
497        return self.mypanels
498
499    def clear_panel(self):
500        """
501        """
502        self.fit_panel.clear_panel()
503
504    def delete_data(self, data):
505        """
506        delete  the given data from panel
507        """
508        self.fit_panel.delete_data(data)
509
510    def set_data(self, data_list=None):
511        """
512        receive a list of data to fit
513        """
514        if data_list is None:
515            data_list = []
516        selected_data_list = []
517        if self.batch_on:
[f21d496]518            self.add_fit_page(data=data_list)
[f76bf17]519        else:
520            if len(data_list) > MAX_NBR_DATA:
521                from fitting_widgets import DataDialog
522                dlg = DataDialog(data_list=data_list, nb_data=MAX_NBR_DATA)
523                if dlg.ShowModal() == wx.ID_OK:
524                    selected_data_list = dlg.get_data()
525                dlg.Destroy()
526
527            else:
528                selected_data_list = data_list
529            try:
530                group_id = wx.NewId()
531                for data in selected_data_list:
532                    if data is not None:
533                        # 2D has no same group_id
534                        if data.__class__.__name__ == 'Data2D':
535                            group_id = wx.NewId()
536                        data.group_id = group_id
537                        if group_id not in data.list_group_id:
538                            data.list_group_id.append(group_id)
[f21d496]539                        self.add_fit_page(data=[data])
[f76bf17]540            except:
541                msg = "Fitting Set_data: " + str(sys.exc_value)
542                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
543
544    def set_theory(self, theory_list=None):
545        """
546        """
547        #set the model state for a given theory_state:
548        for item in theory_list:
549            try:
550                _, theory_state = item
551                self.fit_panel.set_model_state(theory_state)
552            except:
553                msg = "Fitting: cannot deal with the theory received"
554                logging.error("set_theory " + msg + "\n" + str(sys.exc_value))
555                wx.PostEvent(self.parent,
556                             StatusEvent(status=msg, info="error"))
557
558    def set_state(self, state=None, datainfo=None, format=None):
559        """
560        Call-back method for the fit page state reader.
561        This method is called when a .fitv/.svs file is loaded.
562
563        : param state: PageState object
564        : param datainfo: data
565        """
566        #state = self.state_reader.get_state()
567        if state != None:
568            state = state.clone()
569            # store fitting state in temp_state
570            self.temp_state.append(state)
571        else:
572            self.temp_state = []
573        # index to start with for a new set_state
574        self.state_index = 0
575        # state file format
576        self.sfile_ext = format
577
578        self.on_set_state_helper(event=None)
579
580    def  on_set_state_helper(self, event=None):
581        """
582        Set_state_helper. This actually sets state
583        after plotting data from state file.
584
585        : event: FitStateUpdateEvent called
586            by dataloader.plot_data from guiframe
587        """
588        if len(self.temp_state) == 0:
589            if self.state_index == 0 and len(self.mypanels) <= 0 \
590            and self.sfile_ext == '.svs':
591                self.temp_state = []
592                self.state_index = 0
593            return
594
595        try:
596            # Load fitting state
597            state = self.temp_state[self.state_index]
598            #panel state should have model selection to set_state
599            if state.formfactorcombobox != None:
600                #set state
601                data = self.parent.create_gui_data(state.data)
602                data.group_id = state.data.group_id
603                self.parent.add_data(data_list={data.id: data})
604                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
605                                        title=data.title))
606                #need to be fix later make sure we are sendind guiframe.data
607                #to panel
608                state.data = data
609                page = self.fit_panel.set_state(state)
610            else:
611                #just set data because set_state won't work
612                data = self.parent.create_gui_data(state.data)
613                data.group_id = state.data.group_id
614                self.parent.add_data(data_list={data.id: data})
615                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
616                                        title=data.title))
617                page = self.add_fit_page([data])
618                caption = page.window_caption
619                self.store_data(uid=page.uid, data_list=page.get_data_list(),
620                        caption=caption)
621                self.mypanels.append(page)
622
623            # get ready for the next set_state
624            self.state_index += 1
625
626            #reset state variables to default when all set_state is finished.
627            if len(self.temp_state) == self.state_index:
628
629                self.temp_state = []
630                #self.state_index = 0
631                # Make sure the user sees the fitting panel after loading
632                #self.parent.set_perspective(self.perspective)
633                self.on_perspective(event=None)
634        except:
635            self.state_index = 0
636            self.temp_state = []
637            raise
638
639    def set_param2fit(self, uid, param2fit):
640        """
641        Set the list of param names to fit for fitprobelm
642        """
643        self.page_finder[uid].set_param2fit(param2fit)
644
645    def set_graph_id(self, uid, graph_id):
646        """
647        Set graph_id for fitprobelm
648        """
649        self.page_finder[uid].set_graph_id(graph_id)
650
651    def get_graph_id(self, uid):
652        """
653        Set graph_id for fitprobelm
654        """
655        return self.page_finder[uid].get_graph_id()
656
657    def save_fit_state(self, filepath, fitstate):
658        """
659        save fit page state into file
660        """
661        self.state_reader.write(filename=filepath, fitstate=fitstate)
662
663    def set_fit_weight(self, uid, flag, is2d=False, fid=None):
664        """
665        Set the fit weights of a given page for all
666        its data by default. If fid is provide then set the range
667        only for the data with fid as id
668        :param uid: id corresponding to a fit page
669        :param fid: id corresponding to a fit problem (data, model)
670        :param weight: current dy data
671        """
672        if uid in self.page_finder.keys():
673            self.page_finder[uid].set_weight(flag=flag, is2d=is2d)
674
675    def set_fit_range(self, uid, qmin, qmax, fid=None):
676        """
677        Set the fitting range of a given page for all
678        its data by default. If fid is provide then set the range
679        only for the data with fid as id
680        :param uid: id corresponding to a fit page
681        :param fid: id corresponding to a fit problem (data, model)
682        :param qmin: minimum  value of the fit range
683        :param qmax: maximum  value of the fit range
684        """
685        if uid in self.page_finder.keys():
686            self.page_finder[uid].set_range(qmin=qmin, qmax=qmax, fid=fid)
687
688    def schedule_for_fit(self, value=0, uid=None):
689        """
690        Set the fit problem field to 0 or 1 to schedule that problem to fit.
691        Schedule the specified fitproblem or get the fit problem related to
692        the current page and set value.
693        :param value: integer 0 or 1
694        :param uid: the id related to a page contaning fitting information
695        """
696        if uid in self.page_finder.keys():
697            self.page_finder[uid].schedule_tofit(value)
698
699    def get_page_finder(self):
700        """
701        return self.page_finder used also by simfitpage.py
702        """
703        return self.page_finder
704
705    def set_page_finder(self, modelname, names, values):
706        """
707        Used by simfitpage.py to reset a parameter given the string constrainst.
708
709        :param modelname: the name ot the model for with the parameter
710                            has to reset
711        :param value: can be a string in this case.
712        :param names: the paramter name
713        """
714        sim_page_id = self.sim_page.uid
715        for uid, value in self.page_finder.iteritems():
716            if uid != sim_page_id and uid != self.batch_page.uid:
[f21d496]717                model_list = value.get_model()
718                model = model_list[0]
[f76bf17]719                if model.name == modelname:
720                    value.set_model_param(names, values)
721                    break
722
723    def split_string(self, item):
724        """
725        receive a word containing dot and split it. used to split parameterset
726        name into model name and parameter name example: ::
727
728            paramaterset (item) = M1.A
729            Will return model_name = M1 , parameter name = A
730
731        """
732        if item.find(".") >= 0:
733            param_names = re.split("\.", item)
734            model_name = param_names[0]
735            ##Assume max len is 3; eg., M0.radius.width
736            if len(param_names) == 3:
737                param_name = param_names[1] + "." + param_names[2]
738            else:
739                param_name = param_names[1]
740            return model_name, param_name
741
742    def on_bumps_options(self, event=None):
[9776c82]743        """
744        Open the bumps options panel.
745        """
[f76bf17]746        from bumps.gui.fit_dialog import OpenFitOptions
747        OpenFitOptions()
748
[9776c82]749    def on_fit_results(self, event=None):
750        """
751        Make the Fit Results panel visible.
752        """
753        self.result_frame.Show()
754        self.result_frame.Raise()
755
[f76bf17]756    def stop_fit(self, uid):
757        """
[acf8e4a5]758        Stop the fit
[f76bf17]759        """
760        if uid in self.fit_thread_list.keys():
761            calc_fit = self.fit_thread_list[uid]
762            if calc_fit is not  None and calc_fit.isrunning():
763                calc_fit.stop()
764                msg = "Fit stop!"
765                wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
766            del self.fit_thread_list[uid]
767        #set the fit button label of page when fit stop is trigger from
768        #simultaneous fit pane
769        sim_flag = self.sim_page is not None and uid == self.sim_page.uid
770        batch_flag = self.batch_page is not None and uid == self.batch_page.uid
771        if sim_flag or batch_flag:
772            for uid, value in self.page_finder.iteritems():
773                if value.get_scheduled() == 1:
774                    if uid in self.fit_panel.opened_pages.keys():
775                        panel = self.fit_panel.opened_pages[uid]
776                        panel._on_fit_complete()
777
778    def set_smearer(self, uid, smearer, fid, qmin=None, qmax=None, draw=True,
779                    enable_smearer=False):
780        """
781        Get a smear object and store it to a fit problem of fid as id. If proper
782        flag is enable , will plot the theory with smearing information.
783
784        :param smearer: smear object to allow smearing data of id fid
785        :param enable_smearer: Define whether or not all (data, model) contained
786            in the structure of id uid will be smeared before fitting.
787        :param qmin: the maximum value of the theory plotting range
788        :param qmax: the maximum value of the theory plotting range
789        :param draw: Determine if the theory needs to be plot
790        """
791        if uid not in self.page_finder.keys():
792            return
793        self.page_finder[uid].enable_smearing(flag=enable_smearer)
794        self.page_finder[uid].set_smearer(smearer, fid=fid)
795        if draw:
796            ## draw model 1D with smeared data
797            data = self.page_finder[uid].get_fit_data(fid=fid)
798            if data is None:
799                msg = "set_mearer requires at least data.\n"
800                msg += "Got data = %s .\n" % str(data)
801                return
802                #raise ValueError, msg
803            model = self.page_finder[uid].get_model(fid=fid)
804            if model is None:
805                return
806            enable1D = issubclass(data.__class__, Data1D)
807            enable2D = issubclass(data.__class__, Data2D)
808            ## if user has already selected a model to plot
809            ## redraw the model with data smeared
810            smear = self.page_finder[uid].get_smearer(fid=fid)
811
812            # compute weight for the current data
813            weight = self.page_finder[uid].get_weight(fid=fid)
814
815            self.draw_model(model=model, data=data, page_id=uid, smearer=smear,
816                enable1D=enable1D, enable2D=enable2D,
817                qmin=qmin, qmax=qmax, weight=weight)
818            self._mac_sleep(0.2)
819
820    def _mac_sleep(self, sec=0.2):
821        """
822        Give sleep to MAC
823        """
824        if ON_MAC:
825            time.sleep(sec)
826
827    def draw_model(self, model, page_id, data=None, smearer=None,
828                   enable1D=True, enable2D=False,
829                   state=None,
830                   fid=None,
831                   toggle_mode_on=False,
832                   qmin=None, qmax=None,
833                   update_chisqr=True, weight=None, source='model'):
834        """
835        Draw model.
836
837        :param model: the model to draw
838        :param name: the name of the model to draw
839        :param data: the data on which the model is based to be drawn
840        :param description: model's description
841        :param enable1D: if true enable drawing model 1D
842        :param enable2D: if true enable drawing model 2D
843        :param qmin:  Range's minimum value to draw model
844        :param qmax:  Range's maximum value to draw model
845        :param qstep: number of step to divide the x and y-axis
846        :param update_chisqr: update chisqr [bool]
847
848        """
849        #self.weight = weight
850        if issubclass(data.__class__, Data1D) or not enable2D:
851            ## draw model 1D with no loaded data
852            self._draw_model1D(model=model,
853                               data=data,
854                               page_id=page_id,
855                               enable1D=enable1D,
856                               smearer=smearer,
857                               qmin=qmin,
858                               qmax=qmax,
859                               fid=fid,
860                               weight=weight,
861                               toggle_mode_on=toggle_mode_on,
862                               state=state,
863                               update_chisqr=update_chisqr,
864                               source=source)
865        else:
866            ## draw model 2D with no initial data
867            self._draw_model2D(model=model,
868                                page_id=page_id,
869                                data=data,
870                                enable2D=enable2D,
871                                smearer=smearer,
872                                qmin=qmin,
873                                qmax=qmax,
874                                fid=fid,
875                                weight=weight,
876                                state=state,
877                                toggle_mode_on=toggle_mode_on,
878                                update_chisqr=update_chisqr,
879                                source=source)
880
881    def onFit(self, uid):
882        """
883        Get series of data, model, associates parameters and range and send then
[acf8e4a5]884        to  series of fitters. Fit data and model, display result to
[f76bf17]885        corresponding panels.
886        :param uid: id related to the panel currently calling this fit function.
887        """
888        if uid is None: raise RuntimeError("no page to fit") # Should never happen
889
890        sim_page_uid = getattr(self.sim_page, 'uid', None)
891        batch_page_uid = getattr(self.batch_page, 'uid', None)
892
893        if uid == sim_page_uid:
894            fit_type = 'simultaneous'
895        elif uid == batch_page_uid:
896            fit_type = 'combined_batch'
897        else:
898            fit_type = 'single'
899
900        fitter_list = []
901        sim_fitter = None
902        if fit_type == 'simultaneous':
[acf8e4a5]903            # for simultaneous fitting only one fitter is needed
904            sim_fitter = Fit()
[f76bf17]905            sim_fitter.fitter_id = self.sim_page.uid
906            fitter_list.append(sim_fitter)
907
908        self.current_pg = None
909        list_page_id = []
910        fit_id = 0
911        for page_id, page_info in self.page_finder.iteritems():
912            # For simulfit (uid give with None), do for-loop
913            # if uid is specified (singlefit), do it only on the page.
914            if page_id in (sim_page_uid, batch_page_uid): continue
915            if fit_type == "single" and page_id != uid: continue
916
917            try:
918                if page_info.get_scheduled() == 1:
919                    page_info.nbr_residuals_computed = 0
920                    page = self.fit_panel.get_page_by_id(page_id)
921                    self.set_fit_weight(uid=page.uid,
922                                     flag=page.get_weight_flag(),
923                                     is2d=page._is_2D())
924                    if not page.param_toFit:
925                        msg = "No fitting parameters for %s" % page.window_caption
926                        wx.PostEvent(page.parent.parent,
927                                     StatusEvent(status=msg, info="error",
928                                                 type="stop"))
929                        return False
930                    if not page._update_paramv_on_fit():
931                        msg = "Fitting range or parameter values are"
932                        msg += " invalid in %s" % \
933                                    page.window_caption
934                        wx.PostEvent(page.parent.parent,
935                                     StatusEvent(status=msg, info="error",
936                                     type="stop"))
937                        return False
938
939                    pars = [str(element[1]) for element in page.param_toFit]
940                    fitproblem_list = page_info.values()
941                    for fitproblem in  fitproblem_list:
942                        if sim_fitter is None:
[acf8e4a5]943                            fitter = Fit()
[f76bf17]944                            fitter.fitter_id = page_id
945                            fitter_list.append(fitter)
946                        else:
947                            fitter = sim_fitter
948                        self._add_problem_to_fit(fitproblem=fitproblem,
949                                             pars=pars,
950                                             fitter=fitter,
951                                             fit_id=fit_id)
952                        fit_id += 1
953                    list_page_id.append(page_id)
954                    page_info.clear_model_param()
955            except KeyboardInterrupt:
956                msg = "Fitting terminated"
957                wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
958                                                      type="stop"))
959                return True
960            except:
[a3f125f0]961                raise
[f76bf17]962                msg = "Fitting error: %s" % str(sys.exc_value)
963                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
964                                                      type="stop"))
965                return False
966        ## If a thread is already started, stop it
967        #if self.calc_fit!= None and self.calc_fit.isrunning():
968        #    self.calc_fit.stop()
969        msg = "Fitting is in progress..."
970        wx.PostEvent(self.parent, StatusEvent(status=msg, type="progress"))
971
[acf8e4a5]972        #Handler used to display fit message
[f76bf17]973        handler = ConsoleUpdate(parent=self.parent,
974                                manager=self,
975                                improvement_delta=0.1)
976        self._mac_sleep(0.2)
977
978        # batch fit
979        batch_inputs = {}
980        batch_outputs = {}
981        if fit_type == "simultaneous":
982            page = self.sim_page
983        elif fit_type == "combined_batch":
984            page = self.batch_page
985        else:
986            page = self.fit_panel.get_page_by_id(uid)
987        if page.batch_on:
988            calc_fit = FitThread(handler=handler,
989                                 fn=fitter_list,
990                                 pars=pars,
991                                 batch_inputs=batch_inputs,
992                                 batch_outputs=batch_outputs,
993                                 page_id=list_page_id,
994                                 completefn=self._batch_fit_complete,
995                                 reset_flag=self.batch_reset_flag)
996        else:
997            ## Perform more than 1 fit at the time
998            calc_fit = FitThread(handler=handler,
999                                    fn=fitter_list,
1000                                    batch_inputs=batch_inputs,
1001                                    batch_outputs=batch_outputs,
1002                                    page_id=list_page_id,
1003                                    updatefn=handler.update_fit,
1004                                    completefn=self._fit_completed)
1005        #self.fit_thread_list[current_page_id] = calc_fit
1006        self.fit_thread_list[uid] = calc_fit
1007        calc_fit.queue()
1008        calc_fit.ready(2.5)
1009        msg = "Fitting is in progress..."
1010        wx.PostEvent(self.parent, StatusEvent(status=msg, type="progress"))
1011
1012        return True
1013
1014    def remove_plot(self, uid, fid=None, theory=False):
1015        """
1016        remove model plot when a fit page is closed
1017        :param uid: the id related to the fitpage to close
1018        :param fid: the id of the fitproblem(data, model, range,etc)
1019        """
1020        if uid not in self.page_finder.keys():
1021            return
1022        fitproblemList = self.page_finder[uid].get_fit_problem(fid)
1023        for fitproblem in fitproblemList:
1024            data = fitproblem.get_fit_data()
1025            model = fitproblem.get_model()
1026            plot_id = None
1027            if model is not None:
1028                plot_id = data.id + model.name
1029            if theory:
1030                plot_id = data.id + model.name
1031            group_id = data.group_id
1032            wx.PostEvent(self.parent, NewPlotEvent(id=plot_id,
1033                                                   group_id=group_id,
1034                                                   action='remove'))
1035
1036    def store_data(self, uid, data_list=None, caption=None):
1037        """
1038        Recieve a list of data and store them ans well as a caption of
1039        the fit page where they come from.
1040        :param uid: if related to a fit page
1041        :param data_list: list of data to fit
1042        :param caption: caption of the window related to these data
1043        """
1044        if data_list is None:
1045            data_list = []
1046
1047        self.page_finder[uid].set_fit_data(data=data_list)
1048        if caption is not None:
1049            self.page_finder[uid].set_fit_tab_caption(caption=caption)
1050
1051    def on_add_new_page(self, event=None):
1052        """
1053        ask fit panel to create a new empty page
1054        """
1055        try:
1056            page = self.fit_panel.add_empty_page()
1057            # add data associated to the page created
1058            if page != None:
1059                wx.PostEvent(self.parent, StatusEvent(status="Page Created",
1060                                               info="info"))
1061            else:
1062                msg = "Page was already Created"
1063                wx.PostEvent(self.parent, StatusEvent(status=msg,
1064                                                       info="warning"))
1065        except:
1066            msg = "Creating Fit page: %s" % sys.exc_value
1067            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
1068
1069    def add_fit_page(self, data):
1070        """
1071        given a data, ask to the fitting panel to create a new fitting page,
1072        get this page and store it into the page_finder of this plug-in
1073        :param data: is a list of data
1074        """
1075        page = self.fit_panel.set_data(data)
1076        # page could be None when loading state files
1077        if page == None:
1078            return page
1079        #append Data1D to the panel containing its theory
1080        #if theory already plotted
1081        if page.uid in self.page_finder:
1082            data = page.get_data()
1083            theory_data = self.page_finder[page.uid].get_theory_data(data.id)
1084            if issubclass(data.__class__, Data2D):
1085                data.group_id = wx.NewId()
1086                if theory_data is not None:
1087                    group_id = str(page.uid) + " Model1D"
1088                    wx.PostEvent(self.parent,
1089                             NewPlotEvent(group_id=group_id,
1090                                               action="delete"))
1091                    self.parent.update_data(prev_data=theory_data,
1092                                             new_data=data)
1093            else:
1094                if theory_data is not None:
1095                    group_id = str(page.uid) + " Model2D"
1096                    data.group_id = theory_data.group_id
1097                    wx.PostEvent(self.parent,
1098                             NewPlotEvent(group_id=group_id,
1099                                               action="delete"))
1100                    self.parent.update_data(prev_data=theory_data,
1101                                             new_data=data)
1102        self.store_data(uid=page.uid, data_list=page.get_data_list(),
1103                        caption=page.window_caption)
1104        if self.sim_page is not None and not self.batch_on:
1105            self.sim_page.draw_page()
1106        if self.batch_page is not None and self.batch_on:
1107            self.batch_page.draw_page()
1108
1109        return page
1110
1111    def _onEVT_SLICER_PANEL(self, event):
1112        """
1113        receive and event telling to update a panel with a name starting with
1114        event.panel_name. this method update slicer panel
1115        for a given interactor.
1116
1117        :param event: contains type of slicer , paramaters for updating
1118            the panel and panel_name to find the slicer 's panel concerned.
1119        """
1120        event.panel_name
1121        for item in self.parent.panels:
1122            name = event.panel_name
1123            if self.parent.panels[item].window_caption.startswith(name):
1124                self.parent.panels[item].set_slicer(event.type, event.params)
1125
1126        #self.parent._mgr.Update()
1127
1128    def _closed_fitpage(self, event):
1129        """
1130        request fitpanel to close a given page when its unique data is removed
1131        from the plot. close fitpage only when the a loaded data is removed
1132        """
1133        if event is None or event.data is None:
1134            return
1135        if hasattr(event.data, "is_data"):
1136            if not event.data.is_data or \
1137                event.data.__class__.__name__ == "Data1D":
1138                self.fit_panel.close_page_with_data(event.data)
1139
1140    def _reset_schedule_problem(self, value=0, uid=None):
1141        """
1142        unschedule or schedule all fitproblem to be fit
1143        """
1144        # case that uid is not specified
1145        if uid == None:
1146            for page_id in self.page_finder.keys():
1147                self.page_finder[page_id].schedule_tofit(value)
1148        # when uid is given
1149        else:
1150            if uid in self.page_finder.keys():
1151                self.page_finder[uid].schedule_tofit(value)
1152
1153    def _add_problem_to_fit(self, fitproblem, pars, fitter, fit_id):
1154        """
[acf8e4a5]1155        Create and set fitter with series of data and model
[f76bf17]1156        """
1157        data = fitproblem.get_fit_data()
1158        model = fitproblem.get_model()
1159        smearer = fitproblem.get_smearer()
1160        qmin, qmax = fitproblem.get_range()
1161
1162        #Extra list of parameters and their constraints
1163        listOfConstraint = []
1164        param = fitproblem.get_model_param()
1165        if len(param) > 0:
1166            for item in param:
1167                ## check if constraint
1168                if item[0] != None and item[1] != None:
1169                    listOfConstraint.append((item[0], item[1]))
1170        new_model = model
1171        fitter.set_model(new_model, fit_id, pars, data=data,
1172                         constraints=listOfConstraint)
1173        fitter.set_data(data=data, id=fit_id, smearer=smearer, qmin=qmin,
1174                        qmax=qmax)
1175        fitter.select_problem_for_fit(id=fit_id, value=1)
1176
1177    def _onSelect(self, event):
1178        """
1179        when Select data to fit a new page is created .Its reference is
1180        added to self.page_finder
1181        """
1182        panel = self.plot_panel
1183        if panel == None:
1184            raise ValueError, "Fitting:_onSelect: NonType panel"
1185        Plugin.on_perspective(self, event=event)
1186        self.select_data(panel)
1187
1188    def select_data(self, panel):
1189        """
1190        """
1191        for plottable in panel.graph.plottables:
1192            if plottable.__class__.__name__ in ["Data1D", "Theory1D"]:
1193                data_id = panel.graph.selected_plottable
1194                if plottable == panel.plots[data_id]:
1195                    data = plottable
1196                    self.add_fit_page(data=[data])
1197                    return
1198            else:
1199                data = plottable
1200                self.add_fit_page(data=[data])
1201
1202    def update_fit(self, result=None, msg=""):
1203        """
1204        """
1205        print "update_fit result", result
1206
1207    def _batch_fit_complete(self, result, pars, page_id,
1208                            batch_outputs, batch_inputs, elapsed=None):
1209        """
1210        Display fit result in batch
[acf8e4a5]1211        :param result: list of objects received from fitters
[f76bf17]1212        :param pars: list of  fitted parameters names
1213        :param page_id: list of page ids which called fit function
1214        :param elapsed: time spent at the fitting level
1215        """
1216        self._mac_sleep(0.2)
1217        uid = page_id[0]
1218        if uid in self.fit_thread_list.keys():
1219            del self.fit_thread_list[uid]
1220
[373d4ee]1221        wx.CallAfter(self._update_fit_button, page_id)
[f76bf17]1222        t1 = time.time()
1223        str_time = time.strftime("%a, %d %b %Y %H:%M:%S ", time.localtime(t1))
1224        msg = "Fit completed on %s \n" % str_time
1225        msg += "Duration time: %s s.\n" % str(elapsed)
1226        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
1227                                              type="stop"))
1228
1229        if batch_outputs is None:
1230            batch_outputs = {}
1231
1232        # format batch_outputs
1233        batch_outputs["Chi2"] = []
1234        #Don't like these loops
1235        # Need to create dictionary of all fitted parameters
1236        # since the number of parameters can differ between each fit result
1237        for list_res in result:
1238            for res in list_res:
1239                model, data = res.inputs[0]
1240                if model is not None and hasattr(model, "model"):
1241                    model = model.model
1242                #get all fittable parameters of the current model
1243                for param in  model.getParamList():
1244                    if param  not in batch_outputs.keys():
1245                        batch_outputs[param] = []
1246                for param in model.getDispParamList():
1247                    if not model.is_fittable(param) and \
1248                        param in batch_outputs.keys():
1249                        del batch_outputs[param]
1250                # Add fitted parameters and their error
1251                for param in res.param_list:
1252                    if param not in batch_outputs.keys():
1253                        batch_outputs[param] = []
1254                    err_param = "error on %s" % str(param)
1255                    if err_param not in batch_inputs.keys():
1256                        batch_inputs[err_param] = []
1257        msg = ""
1258        for list_res in result:
1259            for res in list_res:
1260                pid = res.fitter_id
1261                model, data = res.inputs[0]
1262                correct_result = False
1263                if model is not None and hasattr(model, "model"):
1264                    model = model.model
1265                if data is not None and hasattr(data, "sas_data"):
1266                    data = data.sas_data
1267
1268                is_data2d = issubclass(data.__class__, Data2D)
1269                #check consistency of arrays
1270                if not is_data2d:
1271                    if len(res.theory) == len(res.index[res.index]) and \
1272                        len(res.index) == len(data.y):
1273                        correct_result = True
1274                else:
1275                    copy_data = deepcopy(data)
1276                    new_theory = copy_data.data
1277                    new_theory[res.index] = res.theory
1278                    new_theory[res.index == False] = numpy.nan
1279                    correct_result = True
1280                #get all fittable parameters of the current model
1281                param_list = model.getParamList()
1282                for param in model.getDispParamList():
1283                    if not model.is_fittable(param) and \
1284                        param in param_list:
1285                        param_list.remove(param)
1286                if not correct_result or res.fitness is None or \
1287                    not numpy.isfinite(res.fitness) or \
1288                    numpy.any(res.pvec == None) or not \
1289                    numpy.all(numpy.isfinite(res.pvec)):
1290                    data_name = str(None)
1291                    if data is not None:
1292                        data_name = str(data.name)
1293                    model_name = str(None)
1294                    if model is not None:
1295                        model_name = str(model.name)
1296                    msg += "Data %s and Model %s did not fit.\n" % (data_name,
1297                                                                    model_name)
1298                    ERROR = numpy.NAN
1299                    cell = BatchCell()
1300                    cell.label = res.fitness
1301                    cell.value = res.fitness
1302                    batch_outputs["Chi2"].append(ERROR)
1303                    for param in param_list:
1304                        # save value of  fixed parameters
1305                        if param not in res.param_list:
1306                            batch_outputs[str(param)].append(ERROR)
1307                        else:
1308                            #save only fitted values
1309                            batch_outputs[param].append(ERROR)
1310                            batch_inputs["error on %s" % str(param)].append(ERROR)
1311                else:
[acf8e4a5]1312                    # TODO: Why sometimes res.pvec comes with numpy.float64?
1313                    # probably from scipy lmfit
[f76bf17]1314                    if res.pvec.__class__ == numpy.float64:
1315                        res.pvec = [res.pvec]
1316
1317                    cell = BatchCell()
1318                    cell.label = res.fitness
1319                    cell.value = res.fitness
1320                    batch_outputs["Chi2"].append(cell)
1321                    # add parameters to batch_results
1322                    for param in param_list:
1323                        # save value of  fixed parameters
1324                        if param not in res.param_list:
1325                            batch_outputs[str(param)].append(model.getParam(param))
1326                        else:
1327                            index = res.param_list.index(param)
1328                            #save only fitted values
1329                            batch_outputs[param].append(res.pvec[index])
1330                            if res.stderr is not None and \
1331                                len(res.stderr) == len(res.param_list):
1332                                item = res.stderr[index]
1333                                batch_inputs["error on %s" % param].append(item)
1334                            else:
1335                                batch_inputs["error on %s" % param].append('-')
1336                            model.setParam(param, res.pvec[index])
1337                #fill the batch result with emtpy value if not in the current
1338                #model
1339                EMPTY = "-"
1340                for key in batch_outputs.keys():
1341                    if key not in param_list and key not in ["Chi2", "Data"]:
1342                        batch_outputs[key].append(EMPTY)
1343
1344                self.page_finder[pid].set_batch_result(batch_inputs=batch_inputs,
1345                                                       batch_outputs=batch_outputs)
1346
1347                cpage = self.fit_panel.get_page_by_id(pid)
1348                cpage._on_fit_complete()
1349                self.page_finder[pid][data.id].set_result(res)
1350                fitproblem = self.page_finder[pid][data.id]
1351                qmin, qmax = fitproblem.get_range()
1352                plot_result = False
1353                if correct_result:
1354                    if not is_data2d:
1355                        self._complete1D(x=data.x[res.index], y=res.theory, page_id=pid,
1356                                         elapsed=None,
1357                                         index=res.index, model=model,
1358                                         weight=None, fid=data.id,
1359                                         toggle_mode_on=False, state=None,
1360                                         data=data, update_chisqr=False,
1361                                         source='fit', plot_result=plot_result)
1362                    else:
1363                        self._complete2D(image=new_theory, data=data,
1364                                         model=model,
1365                                         page_id=pid, elapsed=None,
1366                                         index=res.index,
1367                                         qmin=qmin,
1368                                         qmax=qmax, fid=data.id, weight=None,
1369                                         toggle_mode_on=False, state=None,
1370                                         update_chisqr=False,
1371                                         source='fit', plot_result=plot_result)
1372                self.on_set_batch_result(page_id=pid,
1373                                         fid=data.id,
1374                                         batch_outputs=batch_outputs,
1375                                         batch_inputs=batch_inputs)
1376
1377        wx.PostEvent(self.parent, StatusEvent(status=msg, error="info",
1378                                              type="stop"))
1379        # Remove parameters that are not shown
1380        cpage = self.fit_panel.get_page_by_id(uid)
1381        tbatch_outputs = {}
1382        shownkeystr = cpage.get_copy_params()
1383        for key in batch_outputs.keys():
1384            if key in ["Chi2", "Data"] or shownkeystr.count(key) > 0:
1385                tbatch_outputs[key] = batch_outputs[key]
1386
1387        wx.CallAfter(self.parent.on_set_batch_result, tbatch_outputs,
1388                     batch_inputs, self.sub_menu)
1389
1390    def on_set_batch_result(self, page_id, fid, batch_outputs, batch_inputs):
1391        """
1392        """
1393        pid = page_id
1394        if fid not in self.page_finder[pid]:
1395            return
1396        fitproblem = self.page_finder[pid][fid]
1397        index = self.page_finder[pid].nbr_residuals_computed - 1
1398        residuals = fitproblem.get_residuals()
1399        theory_data = fitproblem.get_theory_data()
1400        data = fitproblem.get_fit_data()
1401        model = fitproblem.get_model()
1402        #fill batch result information
1403        if "Data" not in batch_outputs.keys():
1404            batch_outputs["Data"] = []
1405        from sas.guiframe.data_processor import BatchCell
1406        cell = BatchCell()
1407        cell.label = data.name
1408        cell.value = index
1409
1410        if theory_data != None:
1411            #Suucessful fit
1412            theory_data.id = wx.NewId()
1413            theory_data.name = model.name + "[%s]" % str(data.name)
1414            if issubclass(theory_data.__class__, Data2D):
1415                group_id = wx.NewId()
1416                theory_data.group_id = group_id
1417                if group_id not in theory_data.list_group_id:
1418                    theory_data.list_group_id.append(group_id)
1419
1420            try:
1421                # associate residuals plot
1422                if issubclass(residuals.__class__, Data2D):
1423                    group_id = wx.NewId()
1424                    residuals.group_id = group_id
1425                    if group_id not in residuals.list_group_id:
1426                        residuals.list_group_id.append(group_id)
1427                batch_outputs["Chi2"][index].object = [residuals]
1428            except:
1429                pass
1430
1431        cell.object = [data, theory_data]
1432        batch_outputs["Data"].append(cell)
1433        for key, value in data.meta_data.iteritems():
1434            if key not in batch_inputs.keys():
1435                batch_inputs[key] = []
1436            #if key.lower().strip() != "loader":
1437            batch_inputs[key].append(value)
1438        param = "temperature"
1439        if hasattr(data.sample, param):
1440            if param not in  batch_inputs.keys():
1441                batch_inputs[param] = []
1442            batch_inputs[param].append(data.sample.temperature)
1443
1444    def _fit_completed(self, result, page_id, batch_outputs,
1445                       batch_inputs=None, pars=None, elapsed=None):
1446        """
1447        Display result of the fit on related panel(s).
1448        :param result: list of object generated when fit ends
1449        :param pars: list of names of parameters fitted
1450        :param page_id: list of page ids which called fit function
1451        :param elapsed: time spent at the fitting level
1452        """
1453        t1 = time.time()
1454        str_time = time.strftime("%a, %d %b %Y %H:%M:%S ", time.localtime(t1))
1455        msg = "Fit completed on %s \n" % str_time
1456        msg += "Duration time: %s s.\n" % str(elapsed)
1457        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
1458                                                      type="stop"))
1459        wx.PostEvent(self.result_panel, PlotResultEvent(result=result))
[373d4ee]1460        wx.CallAfter(self._update_fit_button, page_id)
[f76bf17]1461        result = result[0]
1462        self.fit_thread_list = {}
1463        if page_id is None:
1464            page_id = []
1465        ## fit more than 1 model at the same time
1466        self._mac_sleep(0.2)
1467        try:
1468            index = 0
1469            for uid in page_id:
1470                res = result[index]
1471                if res.fitness is None or \
1472                    not numpy.isfinite(res.fitness) or \
1473                    numpy.any(res.pvec == None) or \
1474                    not numpy.all(numpy.isfinite(res.pvec)):
1475                    msg = "Fitting did not converge!!!"
1476                    wx.PostEvent(self.parent,
1477                             StatusEvent(status=msg,
1478                                         info="warning",
1479                                         type="stop"))
[373d4ee]1480                    wx.CallAfter(self._update_fit_button, page_id)
[f76bf17]1481                else:
1482                    #set the panel when fit result are float not list
1483                    if res.pvec.__class__ == numpy.float64:
1484                        pvec = [res.pvec]
1485                    else:
1486                        pvec = res.pvec
1487                    if res.stderr.__class__ == numpy.float64:
1488                        stderr = [res.stderr]
1489                    else:
1490                        stderr = res.stderr
1491                    cpage = self.fit_panel.get_page_by_id(uid)
1492                    # Make sure we got all results
1493                    #(CallAfter is important to MAC)
1494                    try:
1495                        #if res != None:
1496                        wx.CallAfter(cpage.onsetValues, res.fitness,
1497                                     res.param_list,
1498                                     pvec, stderr)
1499                        index += 1
1500                        wx.CallAfter(cpage._on_fit_complete)
1501                    except KeyboardInterrupt:
1502                        msg = "Singular point: Fitting Stoped."
1503                        wx.PostEvent(self.parent, StatusEvent(status=msg,
1504                                                              info="info",
1505                                                              type="stop"))
1506                    except:
1507                        msg = "Singular point: Fitting Error occurred."
1508                        wx.PostEvent(self.parent, StatusEvent(status=msg,
1509                                                              info="error",
1510                                                              type="stop"))
1511
1512        except:
[e1442d4]1513            msg = ("Fit completed but the following error occurred: %s"
1514                   % sys.exc_value)
1515            #import traceback; msg = "\n".join((traceback.format_exc(), msg))
[f76bf17]1516            wx.PostEvent(self.parent, StatusEvent(status=msg, info="warning",
1517                                                  type="stop"))
1518
1519    def _update_fit_button(self, page_id):
1520        """
1521        Update Fit button when fit stopped
1522
1523        : parameter page_id: fitpage where the button is
1524        """
1525        if page_id.__class__.__name__ != 'list':
1526            page_id = [page_id]
1527        for uid in page_id:
1528            page = self.fit_panel.get_page_by_id(uid)
1529            page._on_fit_complete()
1530
1531    def _on_show_panel(self, event):
1532        """
1533        """
1534        pass
1535
1536    def on_reset_batch_flag(self, event):
1537        """
1538        Set batch_reset_flag
1539        """
1540        event.Skip()
1541        if self.menu1 == None:
1542            return
1543        menu_item = self.menu1.FindItemById(self.id_reset_flag)
1544        flag = menu_item.IsChecked()
1545        if not flag:
1546            menu_item.Check(False)
1547            self.batch_reset_flag = True
1548        else:
1549            menu_item.Check(True)
1550            self.batch_reset_flag = False
1551
1552        ## post a message to status bar
1553        msg = "Set Chain Fitting: %s" % str(not self.batch_reset_flag)
1554        wx.PostEvent(self.parent,
1555                     StatusEvent(status=msg))
1556
1557
1558    def _on_slicer_event(self, event):
1559        """
1560        Receive a panel as event and send it to guiframe
1561
1562        :param event: event containing a panel
1563
1564        """
1565        if event.panel is not None:
1566            self.slicer_panels.append(event.panel)
1567            # Set group ID if available
1568            event_id = self.parent.popup_panel(event.panel)
1569            event.panel.uid = event_id
1570            self.mypanels.append(event.panel)
1571
1572    def _onclearslicer(self, event):
1573        """
1574        Clear the boxslicer when close the panel associate with this slicer
1575        """
1576        name = event.GetEventObject().frame.GetTitle()
1577        for panel in self.slicer_panels:
1578            if panel.window_caption == name:
1579
1580                for item in self.parent.panels:
1581                    if hasattr(self.parent.panels[item], "uid"):
1582                        if self.parent.panels[item].uid == panel.base.uid:
1583                            self.parent.panels[item].onClearSlicer(event)
1584                            #self.parent._mgr.Update()
1585                            break
1586                break
1587
1588    def _on_model_panel(self, evt):
1589        """
1590        react to model selection on any combo box or model menu.plot the model
1591
1592        :param evt: wx.combobox event
1593
1594        """
1595        model = evt.model
1596        uid = evt.uid
1597        qmin = evt.qmin
1598        qmax = evt.qmax
1599        caption = evt.caption
1600        enable_smearer = evt.enable_smearer
1601        if model == None:
1602            return
1603        if uid not in self.page_finder.keys():
1604            return
1605        # save the name containing the data name with the appropriate model
1606        self.page_finder[uid].set_model(model)
1607        self.page_finder[uid].enable_smearing(enable_smearer)
1608        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
1609        self.page_finder[uid].set_fit_tab_caption(caption=caption)
1610        if self.sim_page is not None and not self.batch_on:
1611            self.sim_page.draw_page()
1612        if self.batch_page is not None and self.batch_on:
1613            self.batch_page.draw_page()
1614
1615    def _update1D(self, x, output):
1616        """
1617        Update the output of plotting model 1D
1618        """
1619        msg = "Plot updating ... "
1620        wx.PostEvent(self.parent, StatusEvent(status=msg, type="update"))
1621
1622    def _complete1D(self, x, y, page_id, elapsed, index, model,
1623                    weight=None, fid=None,
1624                    toggle_mode_on=False, state=None,
1625                    data=None, update_chisqr=True,
1626                    source='model', plot_result=True):
1627        """
1628        Complete plotting 1D data
1629        """
1630        try:
1631            numpy.nan_to_num(y)
1632
1633            new_plot = Data1D(x=x, y=y)
1634            new_plot.is_data = False
1635            new_plot.dy = numpy.zeros(len(y))
1636            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
1637            _yaxis, _yunit = data.get_yaxis()
1638            _xaxis, _xunit = data.get_xaxis()
1639            new_plot.title = data.name
1640
1641            new_plot.group_id = data.group_id
1642            if new_plot.group_id == None:
1643                new_plot.group_id = data.group_id
1644            new_plot.id = str(page_id) + " " + data.name
1645            #if new_plot.id in self.color_dict:
1646            #    new_plot.custom_color = self.color_dict[new_plot.id]
1647            #find if this theory was already plotted and replace that plot given
1648            #the same id
[f21d496]1649            self.page_finder[page_id].get_theory_data(fid=data.id)
[f76bf17]1650
1651            if data.is_data:
1652                data_name = str(data.name)
1653            else:
1654                data_name = str(model.__class__.__name__)
1655
1656            new_plot.name = model.name + " [" + data_name + "]"
1657            new_plot.xaxis(_xaxis, _xunit)
1658            new_plot.yaxis(_yaxis, _yunit)
1659            self.page_finder[page_id].set_theory_data(data=new_plot,
1660                                                      fid=data.id)
1661            self.parent.update_theory(data_id=data.id, theory=new_plot,
1662                                       state=state)
1663            current_pg = self.fit_panel.get_page_by_id(page_id)
1664            title = new_plot.title
1665            batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
1666            if not batch_on:
1667                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1668                                            title=str(title)))
1669            elif plot_result:
1670                top_data_id = self.fit_panel.get_page_by_id(page_id).data.id
1671                if data.id == top_data_id:
1672                    wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1673                                            title=str(title)))
1674            caption = current_pg.window_caption
1675            self.page_finder[page_id].set_fit_tab_caption(caption=caption)
1676
1677            self.page_finder[page_id].set_theory_data(data=new_plot,
1678                                                      fid=data.id)
1679            if toggle_mode_on:
1680                wx.PostEvent(self.parent,
1681                             NewPlotEvent(group_id=str(page_id) + " Model2D",
1682                                          action="Hide"))
1683            else:
1684                if update_chisqr:
1685                    wx.PostEvent(current_pg,
1686                                 Chi2UpdateEvent(output=self._cal_chisqr(
1687                                                                data=data,
1688                                                                fid=fid,
1689                                                                weight=weight,
1690                                                            page_id=page_id,
1691                                                            index=index)))
1692                else:
1693                    self._plot_residuals(page_id=page_id, data=data, fid=fid,
1694                                         index=index, weight=weight)
1695
1696            msg = "Computation  completed!"
1697            wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1698        except:
1699            raise
1700
1701    def _update2D(self, output, time=None):
1702        """
1703        Update the output of plotting model
1704        """
1705        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1706        #updating ... ", type="update"))
1707        #self.ready_fit()
1708
1709    def _complete2D(self, image, data, model, page_id, elapsed, index, qmin,
1710                qmax, fid=None, weight=None, toggle_mode_on=False, state=None,
1711                     update_chisqr=True, source='model', plot_result=True):
1712        """
1713        Complete get the result of modelthread and create model 2D
1714        that can be plot.
1715        """
1716        numpy.nan_to_num(image)
1717        new_plot = Data2D(image=image, err_image=data.err_data)
1718        new_plot.name = model.name + '2d'
1719        new_plot.title = "Analytical model 2D "
1720        new_plot.id = str(page_id) + " " + data.name
1721        new_plot.group_id = str(page_id) + " Model2D"
1722        new_plot.detector = data.detector
1723        new_plot.source = data.source
1724        new_plot.is_data = False
1725        new_plot.qx_data = data.qx_data
1726        new_plot.qy_data = data.qy_data
1727        new_plot.q_data = data.q_data
1728        new_plot.mask = data.mask
1729        ## plot boundaries
1730        new_plot.ymin = data.ymin
1731        new_plot.ymax = data.ymax
1732        new_plot.xmin = data.xmin
1733        new_plot.xmax = data.xmax
1734        title = data.title
1735
1736        new_plot.is_data = False
1737        if data.is_data:
1738            data_name = str(data.name)
1739        else:
1740            data_name = str(model.__class__.__name__) + '2d'
1741
1742        if len(title) > 1:
1743            new_plot.title = "Model2D for %s " % model.name + data_name
1744        new_plot.name = model.name + " [" + \
1745                                    data_name + "]"
1746        theory_data = deepcopy(new_plot)
1747
1748        self.page_finder[page_id].set_theory_data(data=theory_data,
1749                                                  fid=data.id)
1750        self.parent.update_theory(data_id=data.id,
1751                                       theory=new_plot,
1752                                       state=state)
1753        current_pg = self.fit_panel.get_page_by_id(page_id)
1754        title = new_plot.title
1755        if not source == 'fit' and plot_result:
1756            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1757                                               title=title))
1758        if toggle_mode_on:
1759            wx.PostEvent(self.parent,
1760                             NewPlotEvent(group_id=str(page_id) + " Model1D",
1761                                               action="Hide"))
1762        else:
1763            # Chisqr in fitpage
1764            if update_chisqr:
1765                wx.PostEvent(current_pg,
1766                             Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1767                                                                    weight=weight,
1768                                                                    fid=fid,
1769                                                         page_id=page_id,
1770                                                         index=index)))
1771            else:
1772                self._plot_residuals(page_id=page_id, data=data, fid=fid,
1773                                      index=index, weight=weight)
1774        msg = "Computation  completed!"
1775        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1776
1777    def _draw_model2D(self, model, page_id, qmin,
1778                      qmax,
1779                      data=None, smearer=None,
1780                      description=None, enable2D=False,
1781                      state=None,
1782                      fid=None,
1783                      weight=None,
1784                      toggle_mode_on=False,
1785                       update_chisqr=True, source='model'):
1786        """
1787        draw model in 2D
1788
1789        :param model: instance of the model to draw
1790        :param description: the description of the model
1791        :param enable2D: when True allows to draw model 2D
1792        :param qmin: the minimum value to  draw model 2D
1793        :param qmax: the maximum value to draw model 2D
1794        :param qstep: the number of division of Qx and Qy of the model to draw
1795
1796        """
1797        if not enable2D:
1798            return None
1799        try:
1800            from model_thread import Calc2D
1801            ## If a thread is already started, stop it
1802            if (self.calc_2D is not None) and self.calc_2D.isrunning():
1803                self.calc_2D.stop()
1804                ## stop just raises a flag to tell the thread to kill
1805                ## itself -- see the fix in Calc1D implemented to fix
1806                ## an actual problem.  Seems the fix should also go here
1807                ## and may be the cause of other noted instabilities
1808                ##
1809                ##    -PDB August 12, 2014
1810                while self.calc_2D.isrunning():
1811                    time.sleep(0.1)
1812            self.calc_2D = Calc2D(model=model,
1813                                    data=data,
1814                                    page_id=page_id,
1815                                    smearer=smearer,
1816                                    qmin=qmin,
1817                                    qmax=qmax,
1818                                    weight=weight,
1819                                    fid=fid,
1820                                    toggle_mode_on=toggle_mode_on,
1821                                    state=state,
1822                                    completefn=self._complete2D,
1823                                    update_chisqr=update_chisqr, source=source)
1824            self.calc_2D.queue()
1825        except:
1826            raise
1827
1828    def _draw_model1D(self, model, page_id, data,
1829                      qmin, qmax, smearer=None,
1830                state=None,
1831                weight=None,
1832                fid=None,
1833                toggle_mode_on=False, update_chisqr=True, source='model',
1834                enable1D=True):
1835        """
1836        Draw model 1D from loaded data1D
1837
1838        :param data: loaded data
1839        :param model: the model to plot
1840
1841        """
1842        if not enable1D:
1843            return
1844        try:
1845            from model_thread import Calc1D
1846            ## If a thread is already started, stop it
1847            if (self.calc_1D is not None) and self.calc_1D.isrunning():
1848                self.calc_1D.stop()
1849                ## stop just raises the flag -- the thread is supposed to
1850                ## then kill itself but cannot.  Paul Kienzle came up with
1851                ## this fix to prevent threads from stepping on each other
1852                ## which was causing a simple custom model to crash Sasview.
1853                ## We still don't know why the fit sometimes lauched a second
1854                ## thread -- something which should also be investigated.
1855                ## The thread approach was implemented in order to be able
1856                ## to lauch a computation in a separate thread from the GUI so
1857                ## that the GUI can still respond to user input including
1858                ## a request to stop the computation.
1859                ## It seems thus that the whole thread approach used here
1860                ## May need rethinking 
1861                ##
1862                ##    -PDB August 12, 2014                 
1863                while self.calc_1D.isrunning():
1864                    time.sleep(0.1)
1865            self.calc_1D = Calc1D(data=data,
1866                                  model=model,
1867                                  page_id=page_id,
1868                                  qmin=qmin,
1869                                  qmax=qmax,
1870                                  smearer=smearer,
1871                                  state=state,
1872                                  weight=weight,
1873                                  fid=fid,
1874                                  toggle_mode_on=toggle_mode_on,
1875                                  completefn=self._complete1D,
1876                                  #updatefn = self._update1D,
1877                                  update_chisqr=update_chisqr,
1878                                  source=source)
1879            self.calc_1D.queue()
1880        except:
1881            msg = " Error occurred when drawing %s Model 1D: " % model.name
1882            msg += " %s" % sys.exc_value
1883            wx.PostEvent(self.parent, StatusEvent(status=msg))
1884
1885    def _cal_chisqr(self, page_id, data, weight, fid=None, index=None):
1886        """
1887        Get handy Chisqr using the output from draw1D and 2D,
1888        instead of calling expansive CalcChisqr in guithread
1889        """
1890        try:
1891            data_copy = deepcopy(data)
1892        except:
1893            return
1894        # default chisqr
1895        chisqr = None
1896        #to compute chisq make sure data has valid data
1897        # return None if data == None
1898        if not check_data_validity(data_copy) or data_copy == None:
1899            return chisqr
1900
1901        # Get data: data I, theory I, and data dI in order
1902        if data_copy.__class__.__name__ == "Data2D":
1903            if index == None:
1904                index = numpy.ones(len(data_copy.data), dtype=bool)
1905            if weight != None:
1906                data_copy.err_data = weight
1907            # get rid of zero error points
1908            index = index & (data_copy.err_data != 0)
1909            index = index & (numpy.isfinite(data_copy.data))
1910            fn = data_copy.data[index]
1911            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1912            if theory_data == None:
1913                return chisqr
1914            gn = theory_data.data[index]
1915            en = data_copy.err_data[index]
1916        else:
1917            # 1 d theory from model_thread is only in the range of index
1918            if index == None:
1919                index = numpy.ones(len(data_copy.y), dtype=bool)
1920            if weight != None:
1921                data_copy.dy = weight
1922            if data_copy.dy == None or data_copy.dy == []:
1923                dy = numpy.ones(len(data_copy.y))
1924            else:
[acf8e4a5]1925                ## Set consistently w/AbstractFitengine:
[f76bf17]1926                # But this should be corrected later.
1927                dy = deepcopy(data_copy.dy)
1928                dy[dy == 0] = 1
1929            fn = data_copy.y[index]
1930
1931            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1932            if theory_data == None:
1933                return chisqr
1934            gn = theory_data.y
1935            en = dy[index]
1936
1937        # residual
1938        try:
1939            res = (fn - gn) / en
1940        except ValueError:
1941            print "Unmatch lengths %s, %s, %s" % (len(fn), len(gn), len(en))
1942            return
1943
1944        residuals = res[numpy.isfinite(res)]
1945        # get chisqr only w/finite
1946        chisqr = numpy.average(residuals * residuals)
1947
1948        self._plot_residuals(page_id=page_id, data=data_copy,
1949                             fid=fid,
1950                             weight=weight, index=index)
1951
1952        return chisqr
1953
1954    def _plot_residuals(self, page_id, weight, fid=None,
1955                        data=None, index=None):
1956        """
1957        Plot the residuals
1958
1959        :param data: data
1960        :param index: index array (bool)
1961        : Note: this is different from the residuals in cal_chisqr()
1962        """
1963        data_copy = deepcopy(data)
1964        # Get data: data I, theory I, and data dI in order
1965        if data_copy.__class__.__name__ == "Data2D":
1966            # build residuals
1967            residuals = Data2D()
1968            #residuals.copy_from_datainfo(data)
1969            # Not for trunk the line below, instead use the line above
1970            data_copy.clone_without_data(len(data_copy.data), residuals)
1971            residuals.data = None
1972            fn = data_copy.data
1973            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1974            gn = theory_data.data
1975            if weight == None:
1976                en = data_copy.err_data
1977            else:
1978                en = weight
1979            residuals.data = (fn - gn) / en
1980            residuals.qx_data = data_copy.qx_data
1981            residuals.qy_data = data_copy.qy_data
1982            residuals.q_data = data_copy.q_data
1983            residuals.err_data = numpy.ones(len(residuals.data))
1984            residuals.xmin = min(residuals.qx_data)
1985            residuals.xmax = max(residuals.qx_data)
1986            residuals.ymin = min(residuals.qy_data)
1987            residuals.ymax = max(residuals.qy_data)
1988            residuals.q_data = data_copy.q_data
1989            residuals.mask = data_copy.mask
1990            residuals.scale = 'linear'
1991            # check the lengths
1992            if len(residuals.data) != len(residuals.q_data):
1993                return
1994        else:
1995            # 1 d theory from model_thread is only in the range of index
1996            if data_copy.dy == None or data_copy.dy == []:
1997                dy = numpy.ones(len(data_copy.y))
1998            else:
1999                if weight == None:
2000                    dy = numpy.ones(len(data_copy.y))
2001                ## Set consitently w/AbstractFitengine:
2002                ## But this should be corrected later.
2003                else:
2004                    dy = weight
2005                dy[dy == 0] = 1
2006            fn = data_copy.y[index]
2007            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
2008            gn = theory_data.y
2009            en = dy[index]
2010            # build residuals
2011            residuals = Data1D()
2012            try:
2013                residuals.y = (fn - gn) / en
2014            except:
2015                msg = "ResidualPlot Error: different # of data points in theory"
2016                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
2017                residuals.y = (fn - gn[index]) / en
2018            residuals.x = data_copy.x[index]
2019            residuals.dy = numpy.ones(len(residuals.y))
2020            residuals.dx = None
2021            residuals.dxl = None
2022            residuals.dxw = None
2023            residuals.ytransform = 'y'
2024            # For latter scale changes
2025            residuals.xaxis('\\rm{Q} ', 'A^{-1}')
2026            residuals.yaxis('\\rm{Residuals} ', 'normalized')
2027        theory_name = str(theory_data.name.split()[0])
2028        new_plot = residuals
2029        new_plot.name = "Residuals for " + str(theory_name) + "[" + \
2030                        str(data.name) + "]"
2031        ## allow to highlight data when plotted
2032        new_plot.interactive = True
2033        ## when 2 data have the same id override the 1 st plotted
2034        new_plot.id = "res" + str(data_copy.id) + str(theory_name)
2035        ##group_id specify on which panel to plot this data
2036        group_id = self.page_finder[page_id].get_graph_id()
2037        if group_id == None:
2038            group_id = data.group_id
2039        new_plot.group_id = "res" + str(group_id)
2040        #new_plot.is_data = True
2041        ##post data to plot
2042        title = new_plot.name
2043        self.page_finder[page_id].set_residuals(residuals=new_plot,
2044                                                fid=data.id)
2045        self.parent.update_theory(data_id=data.id, theory=new_plot)
2046        batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
2047        if not batch_on:
2048            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))
Note: See TracBrowser for help on using the repository browser.