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

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 c4f6851 was 824e488, checked in by Mathieu Doucet <doucetm@…>, 10 years ago

pylint fixes and remove old code

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