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

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

better handling of unsuccessful bumps fits

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