source: sasview/src/sas/perspectives/fitting/fitting.py @ a3f125f0

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 a3f125f0 was a3f125f0, checked in by Paul Kienzle <pkienzle@…>, 9 years ago

refactor resolution calculation to enable sasmodels resolution calcuator for pinhole smearing, but don't enable it yet

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