source: sasview/src/sas/perspectives/fitting/fitting.py @ 27f8dac

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 27f8dac was 386ffe1, checked in by pkienzle, 10 years ago

remove scipy levenburg marquardt and park from ui

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