source: sasview/fittingview/src/sans/perspectives/fitting/fitting.py @ f32d144

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 f32d144 was f32d144, checked in by Mathieu Doucet <doucetm@…>, 12 years ago

Pep-8-ification

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