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

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 c7e12b6 was 8b1d759, checked in by Jae Cho <jhjcho@…>, 13 years ago

this might fix the problems with crash

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