Index: calculatorview/src/sans/perspectives/calculator/calculator.py
===================================================================
--- calculatorview/src/sans/perspectives/calculator/calculator.py	(revision ce3feca1e7d2324618e496e9578a205a745f3d33)
+++ calculatorview/src/sans/perspectives/calculator/calculator.py	(revision 318b5bbbcb046db18a8e953dccae04fc7b8a71e0)
@@ -64,4 +64,5 @@
         mass_volume_help = "Based on the chemical formular, "
         mass_volume_help += "compute the mass density or the molar volume."
+        gensans_help = "Genneric SANS"
         pyconsole_help = "Python Console."
         #data_editor_help = "Meta Data Editor"
@@ -77,4 +78,6 @@
                           ("SANS Resolution Estimator", 
                         resolution_help, self.on_calculate_resoltuion),
+                ("Generic Scattering Calculator", 
+                        gensans_help, self.on_gen_model),
                 ("Python Shell/Editor", pyconsole_help, self.on_python_console)]
               
@@ -162,4 +165,12 @@
         #if event != None:
         #    event.Skip()
+        
+    def on_gen_model(self, event):
+        from sans.perspectives.calculator.gen_scatter_panel \
+        import SasGenWindow
+        frame = SasGenWindow(parent=self.parent)
+        self.put_icon(frame)
+        frame.Show(True) 
+        
     def on_python_console(self, event):
         """
Index: calculatorview/src/sans/perspectives/calculator/gen_scatter_panel.py
===================================================================
--- calculatorview/src/sans/perspectives/calculator/gen_scatter_panel.py	(revision 318b5bbbcb046db18a8e953dccae04fc7b8a71e0)
+++ calculatorview/src/sans/perspectives/calculator/gen_scatter_panel.py	(revision 318b5bbbcb046db18a8e953dccae04fc7b8a71e0)
@@ -0,0 +1,1765 @@
+"""
+Generic Scattering panel. 
+This module relies on guiframe manager.
+"""
+
+import wx
+import sys
+import os
+import numpy
+#import math
+import wx.aui as aui
+#import wx.lib.agw.aui as aui
+import logging
+import time
+
+import matplotlib
+matplotlib.interactive(False)
+#Use the WxAgg back end. The Wx one takes too long to render
+matplotlib.use('WXAgg')
+
+from data_util.calcthread import CalcThread
+#from sans.guiframe.local_perspectives.plotting.Plotter2D import ModelPanel2D \
+#                    as PlotPanel
+#from danse.common.plottools.PlotPanel import PlotPanel
+#from danse.common.plottools.SimplePlot import PlotFrame
+from sans.guiframe.local_perspectives.plotting.SimplePlot import PlotFrame
+#from danse.common.plottools.plottables import Graph
+from sans.guiframe.dataFitting import Data2D
+#from sans.guiframe.utils import PanelMenu
+from sans.dataloader.data_info import Detector
+from sans.dataloader.data_info import Source
+from sans.guiframe.panel_base import PanelBase
+from sans.guiframe.utils import format_number
+from sans.guiframe.events import StatusEvent  
+from sans.calculator import sans_gen 
+from calculator_widgets import OutputTextCtrl
+from calculator_widgets import InputTextCtrl
+from wx.lib.scrolledpanel import ScrolledPanel
+from sans.perspectives.calculator.load_thread import GenReader
+#import matplotlib.pyplot as plt
+from mpl_toolkits.mplot3d import Axes3D
+from danse.common.plottools.arrow3d import Arrow3D
+#from danse.common.plottools.toolbar import NavigationToolBar
+_BOX_WIDTH = 76
+#Slit length panel size 
+if sys.platform.count("win32") > 0:
+    PANEL_WIDTH = 570
+    PANEL_HEIGHT = 370
+    FONT_VARIANT = 0
+else:
+    PANEL_WIDTH = 600
+    PANEL_HEIGHT = 420
+    FONT_VARIANT = 1
+_QMAX_DEFAULT = 0.1
+_NPTS_DEFAULT = 50 
+
+def add_icon(parent, frame):
+    """
+    Add icon in the frame
+    """
+    if parent != None:
+        if hasattr(frame, "IsIconized"):
+            if not frame.IsIconized():
+                try:
+                    icon = parent.GetIcon()
+                    frame.SetIcon(icon)
+                except:
+                    pass  
+
+def _set_error(panel, item, show_msg=False):
+    """ 
+    Set_error dialog
+    """
+    if item != None:
+        item.SetBackgroundColour("pink")
+        item.Refresh()
+    if show_msg:
+        msg = "Error: wrong value entered."
+        if panel.parent.parent != None:
+            wx.PostEvent(panel.parent.parent, 
+                     StatusEvent(status=msg, info='Error' )) 
+            panel.SetFocus()
+    return False
+
+class CalcGen(CalcThread):
+    """
+    Computation
+    """
+    def __init__(self,
+                 id=-1,
+                 input = None,
+                 completefn = None,
+                 updatefn   = None,
+                 elapsed = 0,
+                 yieldtime  = 0.005,
+                 worktime   = 0.01):
+        """
+        """
+        CalcThread.__init__(self,completefn,
+                 updatefn,
+                 yieldtime,
+                 worktime)
+        self.starttime = 0
+        self.id = id 
+        self.input = input 
+        
+    def compute(self):
+        """
+        excuting computation
+        """
+        elapsed = time.time() - self.starttime
+       
+        self.complete(input=self.input,
+                      elapsed = elapsed)
+            
+class SasGenPanel(ScrolledPanel, PanelBase):
+    """
+        Provides the sas gen calculator GUI.
+    """
+    ## Internal nickname for the window, used by the AUI manager
+    window_name = "Generic SANS Calculator"
+    ## Name to appear on the window title bar
+    window_caption = "Generic SANS "
+    ## Flag to tell the AUI manager to put this panel in the center pane
+    CENTER_PANE = True
+    
+    def __init__(self, parent, *args, **kwds):
+        ScrolledPanel.__init__(self, parent, style=wx.RAISED_BORDER, 
+                               *args, **kwds)
+        #kwds['style'] = wx.SUNKEN_BORDER
+        PanelBase.__init__(self)
+        #Font size 
+        self.SetWindowVariant(variant=FONT_VARIANT)
+        self.SetupScrolling()
+        #thread to read data 
+        self.reader = None
+        self.ext = None
+        self.id = 'Generic Scattering'
+        self.file_name = ''
+        self.time_text = None
+        self.omfreader = sans_gen.OMFReader()
+        self.sldreader = sans_gen.SLDReader()
+        self.model = sans_gen.GenSAS()
+        self.param_dic = self.model.params
+        self.parameters = []
+        self.data = None
+        self.scale2d = None
+        self.plot_frame = None
+        self.qmax_x = _QMAX_DEFAULT
+        self.npts_x = _NPTS_DEFAULT
+        self.sld_data = None
+        self.graph_num = 1
+        self.default_shape = 'rectangular'
+        # Object that receive status event
+        self.parent = parent
+        self._do_layout()
+        self._create_default_sld_data()
+        self._create_default_2d_data()
+        wx.CallAfter(self._set_sld_data_helper)
+        
+    def _define_structure(self):
+        """
+            Define the main sizers building to build this application.
+        """
+        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
+        self.box_source = wx.StaticBox(self, -1, str("SLD Data File"))
+        self.box_parameters = wx.StaticBox(self, -1, str("Input Parameters"))
+        self.box_qrange = wx.StaticBox(self, -1, str("Q Range"))
+        self.boxsizer_source = wx.StaticBoxSizer(self.box_source,
+                                                    wx.VERTICAL)
+        self.boxsizer_parameters = wx.StaticBoxSizer(self.box_parameters,
+                                                    wx.VERTICAL)
+        self.boxsizer_qrange = wx.StaticBoxSizer(self.box_qrange,
+                                                    wx.VERTICAL)
+        self.data_name_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.param_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.shape_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.hint_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.qrange_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
+       
+    def _layout_data_name(self):
+        """
+            Fill the sizer containing data's name
+        """
+        data_name_txt = wx.StaticText(self, -1, 'Data: ')
+        self.data_name_tcl = OutputTextCtrl(self, -1, 
+                                            size=(_BOX_WIDTH * 4, -1))
+        data_hint = "Loaded data"
+        self.data_name_tcl.SetToolTipString(data_hint)
+        #control that triggers importing data
+        id = wx.NewId()
+        self.browse_button = wx.Button(self, id, "Load")
+        hint_on_browse = "Click on this button to load a data in this panel."
+        #self.browse_button.SetToolTipString(hint_on_browse)
+        self.Bind(wx.EVT_BUTTON, self.on_load_data, id=id)
+        self.data_name_sizer.AddMany([(data_name_txt, 0, wx.LEFT, 15),
+                                      (self.data_name_tcl, 0, wx.LEFT, 10),
+                                      (self.browse_button, 0, wx.LEFT, 10)])
+    def _layout_param_size(self):
+        """
+            Fill the sizer containing slit size information
+        """
+        self.parameters = []
+        sizer = wx.GridBagSizer(3, 6)
+        model = self.model
+        details = self.model.details
+        params = self.model.params
+        ix = 0
+        iy = 0
+        param_title = wx.StaticText(self, -1, 'Parameter')
+        sizer.Add(param_title, (iy, ix), (1, 1), \
+                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        ix += 1
+        value_title = wx.StaticText(self, -1, 'Value')
+        sizer.Add(value_title, (iy, ix), (1, 1), \
+                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        ix += 1
+        unit_title = wx.StaticText(self, -1, 'Unit')
+        sizer.Add(unit_title, (iy, ix), (1, 1), \
+                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        key_list = params.keys()
+        key_list.sort()
+        for param in key_list:
+            iy += 1
+            ix = 0
+            p_name = wx.StaticText(self, -1, param)
+            sizer.Add(p_name, (iy, ix), (1, 1), \
+                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+            ## add parameter value
+            ix += 1
+            value = model.getParam(param)
+            ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH * 2, 20),
+                                style=wx.TE_PROCESS_ENTER)
+            #ctl.SetToolTipString(\
+            #            "Hit 'Enter' after typing to update the plot.")
+            ctl.SetValue(format_number(value, True))
+            sizer.Add(ctl, (iy, ix), (1, 1), wx.EXPAND)
+            ## add unit
+            ix += 1
+            unit = wx.StaticText(self, -1, details[param][0])
+            sizer.Add(unit, (iy, ix), (1, 1), \
+                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+            self.parameters.append([p_name, ctl, unit])
+                                  
+        self.param_sizer.Add(sizer, 0, wx.LEFT, 10)
+    
+    def _layout_hint(self):
+        """
+            Fill the sizer containing hint 
+        """
+        hint_msg = "We support the omf or sld data files only."
+        hint_msg +=  "           "
+        hint_msg +=  "Very slow drawing -->"
+        hint_txt = wx.StaticText(self, -1, hint_msg)
+        
+        id = wx.NewId()
+        self.draw_button = wx.Button(self, id, "Arrow Draw")
+        hint_on_draw = "Draw with arrows. Caution: it is a very slow drawing."
+        self.draw_button.SetToolTipString(hint_on_draw)
+        self.draw_button.Bind(wx.EVT_BUTTON, self.sld_draw, id=id)
+        
+        self.draw_button.Enable(False)
+        self.hint_sizer.AddMany([(hint_txt, 0, wx.LEFT, 15),
+                                 (self.draw_button, 0, wx.LEFT, 15)])
+    
+    def _layout_shape(self):
+        """
+        Fill the shape sizer
+        """
+        label_txt = wx.StaticText(self, -1, "Shape:")
+        self.shape_combo = self._fill_shape_combo()
+        self.shape_sizer.AddMany([(label_txt, 0, wx.LEFT, 15),
+                                (self.shape_combo, 0, wx.LEFT, 5)])
+    
+    def _fill_shape_combo(self):
+        """
+        Fill up the shape combo box
+        """
+        shape_combo = wx.ComboBox(self, -1, size=(150, -1), 
+                                      style=wx.CB_READONLY) 
+        shape_combo.Append('Rectangular')
+        shape_combo.Append('Ellipsoid')
+        shape_combo.Bind(wx.EVT_COMBOBOX, self._on_shape_select)
+        shape_combo.SetSelection(0)
+        return shape_combo
+    
+    def _on_shape_select(self, event):
+        """
+        On selecting a shape
+        """
+        event.Skip()
+        label = self.shape_combo.GetLabel().lower() 
+        self.default_shape = label
+        self.parent.set_omfpanel_default_shap(self.default_shape)
+        self.parent.set_omfpanel_npts()
+           
+    def _layout_qrange(self):
+        """
+        Fill the sizer containing qrange
+        """
+        sizer = wx.GridBagSizer(2, 3)
+        ix = 0
+        iy = 0
+        #key_list.sort()
+        name = wx.StaticText(self, -1, 'No. of Qx (Qy) bins: ')
+        sizer.Add(name, (iy, ix), (1, 1), \
+                        wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        ## add parameter value
+        ix += 1
+        self.npt_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH * 1.5, 20),
+                            style=wx.TE_PROCESS_ENTER)
+        self.npt_ctl.Bind(wx.EVT_TEXT, self._onparamEnter)
+        self.npt_ctl.SetValue(format_number(self.npts_x, True))
+        sizer.Add(self.npt_ctl, (iy, ix), (1, 1), wx.EXPAND)
+        ## add unit
+        ix += 1
+        unit = wx.StaticText(self, -1, '')
+        sizer.Add(unit, (iy, ix), (1, 1), \
+                        wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        iy += 1
+        ix = 0
+        name = wx.StaticText(self, -1, 'Qx (Qy) Max: ')
+        sizer.Add(name, (iy, ix), (1, 1), \
+                        wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        ## add parameter value
+        ix += 1
+        self.qmax_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH * 1.5, 20),
+                            style=wx.TE_PROCESS_ENTER)
+        self.qmax_ctl.Bind(wx.EVT_TEXT, self._onparamEnter )
+        self.qmax_ctl.SetValue(format_number(self.qmax_x, True))
+        sizer.Add(self.qmax_ctl, (iy, ix), (1, 1), wx.EXPAND)
+        ## add unit
+        ix += 1
+        unit = wx.StaticText(self, -1, '[1/A]')
+        sizer.Add(unit, (iy, ix), (1, 1), \
+                        wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        self.qrange_sizer.Add(sizer, 0, wx.LEFT, 10)
+    
+    def _layout_button(self):  
+        """
+            Do the layout for the button widgets
+        """ 
+        self.est_time = '*Estimated Computation time : %s sec'
+        self.time_text = wx.StaticText(self, -1, self.est_time% str(2) )
+        self.bt_compute = wx.Button(self, wx.NewId(),'Compute')
+        self.bt_compute.Bind(wx.EVT_BUTTON, self.on_compute)
+        self.bt_compute.SetToolTipString("Compute 2D Scattering Pattern.")
+        self.button_sizer.AddMany([(self.time_text , 0, wx.LEFT, 20),
+                                   (self.bt_compute, 0, wx.LEFT, 190)])
+        
+    def estimate_ctime(self):
+        """
+        """
+        n_qbins = float(self.npt_ctl.GetValue())
+        n_qbins *= n_qbins
+        n_pixs = float(self.parent.get_npix())
+        x_in = n_qbins * n_pixs / 100000
+        # magic equation: not very accurate
+        etime = 0.00001 + 0.085973 * x_in
+        return int(etime)
+        
+    def set_est_time(self):
+        """
+        Set text for est. computation time
+        """
+        if self.time_text != None:
+            etime = self.estimate_ctime()
+            self.time_text.SetLabel(self.est_time% str(etime))
+        
+    def _do_layout(self):
+        """
+            Draw window content
+        """
+        self._define_structure()
+        self._layout_data_name()
+        self._layout_param_size()
+        self._layout_qrange()
+        self._layout_hint()
+        self._layout_shape()
+        self._layout_button()
+        self.boxsizer_source.AddMany([(self.data_name_sizer, 0,
+                                        wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
+                                      (self.hint_sizer, 0,
+                                        wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
+                                      (self.shape_sizer, 0, 
+                                        wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
+        self.boxsizer_parameters.AddMany([(self.param_sizer, 0,
+                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5),])
+        self.boxsizer_qrange.AddMany([(self.qrange_sizer, 0,
+                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5),])
+        self.main_sizer.AddMany([(self.boxsizer_source, 0, 
+                                  wx.EXPAND|wx.ALL, 10),
+                                 (self.boxsizer_parameters, 0, 
+                                  wx.EXPAND|wx.ALL, 10),
+                                 (self.boxsizer_qrange, 0, 
+                                  wx.EXPAND|wx.ALL, 10),
+                                 (self.button_sizer, 0,
+                                  wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
+        self.SetSizer(self.main_sizer)
+        self.SetAutoLayout(True)
+    
+    def _create_default_sld_data(self):
+        """
+        Making default sld-data
+        """
+        sld_n_default = 6.97e-06
+        omfdata = sans_gen.OMFData()
+        omf2sld = sans_gen.OMF2SLD()
+        omf2sld.set_data(omfdata, self.default_shape)
+        self.sld_data = omf2sld.output
+        self.sld_data.is_data = False
+        self.sld_data.filename = "Default SLD Profile"
+        self.sld_data.set_sldn(sld_n_default)
+        self.data_name_tcl.SetValue(self.sld_data.filename)       
+               
+    def choose_data_file(self, location=None):
+        """
+        Choosing a dtata file
+        """
+        path = None
+        filename = ''
+        if location == None:
+            location = os.getcwd()
+        
+        omf_type = self.omfreader.type
+        sld_type = self.sldreader.type
+        wildcard = []
+        for type in sld_type:
+            wildcard.append(type)
+        for type in omf_type:
+            wildcard.append(type)
+        wildcard = '|'.join(wildcard)
+        dlg = wx.FileDialog(self, "Choose a file", location,
+                            "", wildcard, wx.OPEN)
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            filename = os.path.basename(path)
+        dlg.Destroy() 
+        return path
+        
+    def on_load_data(self, event):
+        """
+        Open a file dialog to allow the user to select a given file.
+        The user is only allow to load file with extension .omf, .txt, .sld.
+        Display the slit size corresponding to the loaded data.
+        """
+        location = self.parent.get_path()
+        path = self.choose_data_file(location=location)
+        if path is None:
+            return 
+        
+        self.shape_sizer.ShowItems(False)
+        self.default_shape = 'rectangular'
+        self.parent.set_omfpanel_default_shap(self.default_shape)
+        
+        self.parent.set_file_location(os.path.dirname(path))
+        try:
+            #Load data
+            self.ext = os.path.splitext(path)[-1]
+            if self.ext in self.omfreader.ext:
+                loader = self.omfreader
+            elif self.ext in self.sldreader.ext:
+                loader = self.sldreader
+            else:
+                loader = None
+            if self.reader is not None and self.reader.isrunning():
+                self.reader.stop()
+            if self.parent.parent is not None:
+                wx.PostEvent(self.parent.parent, 
+                                StatusEvent(status="Loading...",
+                                type="progress"))
+            self.reader = GenReader(path=path, loader=loader,
+                                    completefn=self.complete_loading,
+                                    updatefn=self.load_update)
+            self.reader.queue()
+            self.load_update()
+        except:
+            self.ext = None
+            raise
+            if self.parent.parent is None:
+                return 
+            msg = "Generic SANS Calculator: %s" % (sys.exc_value)
+            wx.PostEvent(self.parent.parent,
+                          StatusEvent(status=msg, type='stop'))
+            self.SetFocus()
+            return 
+        
+    def load_update(self):
+        """
+        print update on the status bar
+        """
+        if self.parent.parent is None:
+                return 
+        if self.reader.isrunning():
+            type = "progress"
+        else:
+            type = "stop"
+        wx.PostEvent(self.parent.parent, StatusEvent(status="",
+                                                  type=type))
+            
+    def complete_loading(self, data=None, filename=''):
+        """
+        Complete the loading
+        """
+        #compute the slit size
+        try:
+            filename = data.filename
+            self.data_name_tcl.SetValue(str(filename))
+            self.file_name = filename.split('.')[0]
+            if self.ext in self.omfreader.ext:
+                gen = sans_gen.OMF2SLD()
+                gen.set_data(data)
+                #omf_data = data
+                self.sld_data = gen.get_magsld()
+            elif self.ext in self.sldreader.ext:
+                self.sld_data = data
+                #omf_data = None
+            else:
+                raise
+            self._set_sld_data_helper(True)
+        except:
+            #raise
+            if self.parent.parent is None:
+                return 
+            msg = "Loading Error: This file format is not supported "
+            msg += "for GenSANS." 
+            wx.PostEvent(self.parent.parent,
+                          StatusEvent(status=msg, type='stop', info='Error'))
+            self.SetFocus()
+            return 
+        if self.parent.parent is None:
+            return 
+        msg = "Load Complete"
+        wx.PostEvent(self.parent.parent, StatusEvent(status=msg, type='stop'))
+        self.SetFocus()
+        
+    def _set_sld_data_helper(self, is_draw=False):
+        """
+        Set sld data helper
+        """
+        self.model.set_sld_data(self.sld_data)
+        self.draw_button.Enable(self.sld_data!=None)
+        #self.parent.set_omfdata(self.sld_data)#omf_data)
+        self.model.set_sld_data(self.sld_data) 
+        wx.CallAfter(self.parent.set_sld_data, self.sld_data)
+        if is_draw:
+            wx.CallAfter(self.sld_draw, False)
+            
+    def _onparamEnter(self, event):
+        """
+        On param enter
+        """
+        try:
+            item = event.GetEventObject()
+            self._check_value()
+            item.Refresh()
+        except:
+            pass
+    
+    def sld_draw(self, has_arrow=False, event=None):
+        """
+        Draw 3D sld profile
+        """
+        graph_title = self.file_name
+        graph_title += "   3D SLD Profile "
+        flag = self.parent.check_omfpanel_inputs()
+        if not flag:
+            infor = 'Error'
+            msg = 'Error: Wrong inputs in the SLD info panel.'
+            # inform msg to wx
+            wx.PostEvent(self.parent.parent,
+                    StatusEvent(status=msg, info=infor))
+            self.SetFocus()
+            return
+
+        self.sld_data = self.parent.get_sld_from_omf()
+        output = self.sld_data  
+        #frame_size = wx.Size(470, 470)    
+        self.plot_frame = PlotFrame(self, -1, 'testView')
+        frame = self.plot_frame
+        frame.Show(False)
+        add_icon(self.parent, frame)
+        panel = frame.plotpanel
+        try:
+            # mpl >= 1.0.0
+            ax = panel.figure.gca(projection='3d')
+        except:
+            # mpl < 1.0.0
+            try:
+                from mpl_toolkits.mplot3d import Axes3D
+                ax = Axes3D(panel.figure)
+            except:
+                logging.error("PlotPanel could not import Axes3D")
+                raise
+
+        pos_x = output.pos_x
+        pos_y = output.pos_y
+        pos_z = output.pos_z
+        sld_mx = output.sld_mx
+        sld_my = output.sld_my
+        sld_mz = output.sld_mz  
+         
+        sld_tot = (numpy.fabs(sld_mx) + numpy.fabs(sld_my) + 
+                   numpy.fabs(sld_mz) + numpy.fabs(output.sld_n))
+        is_nonzero = sld_tot > 0.0  
+        is_zero = sld_tot == 0.0  
+        if is_zero.any():
+            ax.plot(pos_x[is_zero], pos_y[is_zero], pos_z[is_zero], ',', c="y", 
+                        alpha=0.5, markeredgecolor='y', markersize=3) 
+            pos_x = pos_x[is_nonzero]
+            pos_y = pos_y[is_nonzero]
+            pos_z = pos_z[is_nonzero]
+            sld_mx = sld_mx[is_nonzero]
+            sld_my = sld_my[is_nonzero]
+            sld_mz = sld_mz[is_nonzero]
+        ax.plot(pos_x, pos_y, 
+                pos_z, ',', c="k", 
+                alpha=0.5, markeredgecolor='k', markersize=3) 
+
+        if has_arrow and len(pos_x) > 0:     
+            graph_title += "w/ Arrows" 
+            def _draw_arrow(input=None, elapsed=0.1):
+                """
+                draw w/arrow
+                """
+                #frame.SetSize(wx.Size(650, 570))
+                #event.Skip()
+                max_mx = max(numpy.fabs(sld_mx))
+                max_my = max(numpy.fabs(sld_my))
+                max_mz = max(numpy.fabs(sld_mz))
+                max_m = max(max_mx, max_my, max_mz)
+                try:
+                    max_step = max(output.xstepsize, output.ystepsize, 
+                                   output.zstepsize)
+                except:
+                    max_step = 0
+                if max_step <= 0:
+                    max_step = 5
+                try:
+                    if max_m != 0:
+                        unit_x2 = sld_mx / max_m
+                        unit_y2 = sld_my / max_m
+                        unit_z2 = sld_mz / max_m
+                        # 0.8 is for avoiding the color becomes white=(1,1,1))
+                        color_x = numpy.fabs(unit_x2 * 0.8)
+                        color_y = numpy.fabs(unit_y2 * 0.8)
+                        color_z = numpy.fabs(unit_z2 * 0.8)
+                        x2 = pos_x + unit_x2 * max_step
+                        y2 = pos_y + unit_y2 * max_step
+                        z2 = pos_z + unit_z2 * max_step
+                        x_arrow = numpy.column_stack((pos_x, x2))
+                        y_arrow = numpy.column_stack((pos_y, y2))
+                        z_arrow = numpy.column_stack((pos_z, z2))
+                        colors =  numpy.column_stack((color_x, 
+                                                      color_y,
+                                                      color_z))
+                        
+                        arrows = Arrow3D(panel, x_arrow, y_arrow, 
+                                                  z_arrow, colors, 
+                                                  mutation_scale=10, lw=1, 
+                                                  arrowstyle="->", alpha = 0.5)
+                        ax.add_artist(arrows)
+                        
+                except:
+                    pass 
+                msg = "Arrow Drawing completed.\n"
+                status_type = 'stop'
+                self._status_info(msg, status_type) 
+            msg = "Arrow Drawing is in progress..."
+            
+            status_type = 'progress'
+            self._status_info(msg, status_type) 
+            draw_out = CalcGen(input=ax,
+                             completefn=_draw_arrow, updatefn=self._update)
+            draw_out.queue()
+
+        panel.subplot.figure.subplots_adjust(left=0.05, right=0.95, 
+                                             bottom=0.05, top=0.96)
+        ax.set_xlabel('x ($\A%s$)'% output.pos_unit)
+        ax.set_ylabel('y ($\A%s$)'% output.pos_unit)
+        ax.set_zlabel('z ($\A%s$)'% output.pos_unit)
+        num_graph = str(self.graph_num)
+        frame.SetTitle('Graph %s: %s'% (num_graph, graph_title))        
+        wx.CallAfter(frame.Show, True)
+        self.graph_num += 1
+        
+    def set_input_params(self):
+        """
+        Set model parameters
+        """
+        for list in self.parameters:
+            param_name = list[0].GetLabelText()
+            param_value = float(list[1].GetValue())
+            self.model.setParam(param_name, param_value)
+            
+    def on_compute(self, event):
+        """
+        Compute I(qx, qy)
+        """
+        flag = self.parent.check_omfpanel_inputs()
+        if not flag and self.parent.parent != None:
+            infor = 'Error'
+            msg = 'Error: Wrong inputs in the SLD info panel.'
+            # inform msg to wx
+            wx.PostEvent(self.parent.parent,
+                    StatusEvent(status=msg, info=infor))
+            self.SetFocus()
+            return
+        self.sld_data = self.parent.get_sld_from_omf()
+        if self.sld_data == None:
+            if self.parent.parent != None:
+                infor = 'Error'
+                msg = 'Error: No data has been selected.'
+                # inform msg to wx
+                wx.PostEvent(self.parent.parent,
+                        StatusEvent(status=msg, info=infor))
+                self.SetFocus()
+            return
+        flag = self._check_value()
+        if not flag:
+            _set_error(self, None, True)
+            return
+        try:
+            vol = self.parent.get_pix_volume()
+            self.model.set_pixel_volume(vol)
+            self.model.set_sld_data(self.sld_data)
+            self.set_input_params()
+            self._create_default_2d_data()
+            i_out = numpy.zeros(len(self.data.data))
+            msg = "Computation is in progress..."
+            status_type = 'progress'
+            self._status_info(msg, status_type)
+            
+            cal_out = CalcGen(input=[self.data.qx_data, self.data.qy_data,i_out],
+                         completefn=self.complete, updatefn=self._update)
+            cal_out.queue()
+            
+        except:
+            msg = "%s."% sys.exc_value
+            status_type = 'stop'
+            self._status_info(msg, status_type)
+            wx.PostEvent(self.parent.parent,
+                        StatusEvent(status=msg, info='Error'))
+            self.SetFocus()
+
+    def _check_value(self):
+        """
+        Check input values if float
+        """
+        flag = True
+        self.npt_ctl.SetBackgroundColour("white")
+        self.qmax_ctl.SetBackgroundColour("white")
+        try:
+            npt_val = float(self.npt_ctl.GetValue())  
+            if npt_val < 2:
+                raise
+            self.npt_ctl.SetValue(str(int(npt_val)))
+            self.set_est_time()
+        except:
+           flag =  _set_error(self, self.npt_ctl)
+        try:
+            qmax_val = float(self.qmax_ctl.GetValue())  
+            if qmax_val <= 0:
+                raise
+        except:
+            flag = _set_error(self, self.qmax_ctl)       
+        for list in self.parameters:
+            list[1].SetBackgroundColour("white")
+            param_name = list[0].GetLabelText()
+            try: 
+                param_val = float(list[1].GetValue())
+                if param_name.count('frac') > 0:
+                    if param_val < 0 or param_val > 1:
+                       raise
+            except:
+                flag = _set_error(self, list[1])
+        return flag
+                   
+    def _status_info(self, msg = '', type = "update"):
+        """
+        Status msg
+        """
+        if type == "stop":
+            label = "Compute"
+            able = True
+        else:   
+            label = "Wait..."
+            able = False
+        self.bt_compute.Enable(able)
+        self.bt_compute.SetLabel(label)
+        self.bt_compute.SetToolTipString(label)
+        if self.parent.parent != None:
+            wx.PostEvent(self.parent.parent, 
+                             StatusEvent(status = msg, type = type )) 
+
+    def _update(self, time=None):
+        """
+        Update the output of plotting model
+        """
+        if self.parent.parent != None:
+            msg = "Computation/drawing is in progress..."
+            wx.PostEvent(self.parent.parent, 
+                         StatusEvent(status=msg, type="update"))
+                                
+    def complete(self, input, elapsed=None):   
+        """
+        Gen compute complete function
+        :Param input: input list [qx_data, qy_data, i_out]
+        """
+        out = self.model.runXY(input)
+        self._draw2D(out)
+        msg = "Gen computation completed.\n"
+        status_type = 'stop'
+        self._status_info(msg, status_type)
+        
+    def _create_default_2d_data(self):
+        """
+        Create 2D data by default
+        Only when the page is on theory mode.
+        :warning: This data is never plotted.
+        """
+        self.qmax_x = float(self.qmax_ctl.GetValue())
+        self.npts_x = int(float(self.npt_ctl.GetValue()))
+        self.data = Data2D()
+        qmax = self.qmax_x #/ numpy.sqrt(2)
+        self.data.xaxis('\\rm{Q_{x}}', '\AA^{-1}')
+        self.data.yaxis('\\rm{Q_{y}}', '\AA^{-1}')
+        self.data.is_data = False
+        self.data.id = str(self.uid) + " GenData"
+        self.data.group_id = str(self.uid) + " Model2D"
+        ## Default values
+        self.data.detector.append(Detector())
+        index = len(self.data.detector) - 1
+        self.data.detector[index].distance = 8000   # mm
+        self.data.source.wavelength = 6             # A
+        self.data.detector[index].pixel_size.x = 5  # mm
+        self.data.detector[index].pixel_size.y = 5  # mm
+        self.data.detector[index].beam_center.x = qmax
+        self.data.detector[index].beam_center.y = qmax
+        xmax = qmax
+        xmin = -qmax
+        ymax = qmax
+        ymin = -qmax
+        qstep = self.npts_x
+
+        x = numpy.linspace(start=xmin, stop=xmax, num=qstep, endpoint=True)
+        y = numpy.linspace(start=ymin, stop=ymax, num=qstep, endpoint=True)
+        ## use data info instead
+        new_x = numpy.tile(x, (len(y), 1))
+        new_y = numpy.tile(y, (len(x), 1))
+        new_y = new_y.swapaxes(0, 1)
+        # all data reuire now in 1d array
+        qx_data = new_x.flatten()
+        qy_data = new_y.flatten()
+        q_data = numpy.sqrt(qx_data * qx_data + qy_data * qy_data)
+        # set all True (standing for unmasked) as default
+        mask = numpy.ones(len(qx_data), dtype=bool)
+        # store x and y bin centers in q space
+        x_bins = x
+        y_bins = y
+        self.data.source = Source()
+        self.data.data = numpy.ones(len(mask))
+        self.data.err_data = numpy.ones(len(mask))
+        self.data.qx_data = qx_data
+        self.data.qy_data = qy_data
+        self.data.q_data = q_data
+        self.data.mask = mask
+        self.data.x_bins = x_bins
+        self.data.y_bins = y_bins
+        # max and min taking account of the bin sizes
+        self.data.xmin = xmin
+        self.data.xmax = xmax
+        self.data.ymin = ymin
+        self.data.ymax = ymax
+        
+    def _draw2D(self, image):
+        """
+        Complete get the result of modelthread and create model 2D
+        that can be plot.
+        """
+        page_id = self.id
+        data = self.data
+        
+        model = self.model
+        qmin = 0.0
+        state = None
+        
+        numpy.nan_to_num(image)
+        new_plot = Data2D(image=image, err_image=data.err_data)
+        new_plot.name = model.name + '2d'
+        new_plot.title = "Generic model 2D "
+        new_plot.id = str(page_id) + ': ' + self.file_name \
+                        + ' #%s'% str(self.graph_num)
+        new_plot.group_id = str(page_id) + " Model2D"
+        new_plot.detector = data.detector
+        new_plot.source = data.source
+        new_plot.is_data = False
+        new_plot.qx_data = data.qx_data
+        new_plot.qy_data = data.qy_data
+        new_plot.q_data = data.q_data
+        new_plot.mask = data.mask
+        ## plot boundaries
+        new_plot.ymin = data.ymin
+        new_plot.ymax = data.ymax
+        new_plot.xmin = data.xmin
+        new_plot.xmax = data.xmax
+        title = data.title
+        _yaxis, _yunit = data.get_yaxis()
+        _xaxis, _xunit = data.get_xaxis()
+        new_plot.xaxis(str(_xaxis), str(_xunit))
+        new_plot.yaxis(str(_yaxis), str(_yunit))
+        
+        new_plot.is_data = False
+        if data.is_data:
+            data_name = str(data.name)
+        else:
+            data_name = str(model.__class__.__name__) + '2d'
+
+        if len(title) > 1:
+            new_plot.title = "Gen Theory for %s "% model.name + data_name
+        new_plot.name = new_plot.id
+        new_plot.label = new_plot.id
+        #theory_data = deepcopy(new_plot)
+        if self.parent.parent != None:
+            self.parent.parent.update_theory(data_id=data.id,
+                                           theory=new_plot,
+                                           state=state)
+        title = new_plot.title
+        num_graph = str(self.graph_num)
+        wx.CallAfter(self.parent.draw_graph, new_plot, 
+                     title="Graph %s: "% num_graph + new_plot.id )
+        self.graph_num += 1
+        
+    def set_scale2d(self, scale):
+        """
+        Set SLD plot scale
+        """
+        self.scale2d = None
+        
+    def on_panel_close(self, event):
+        """
+        On Close SLD panel
+        """
+        #Not implemented   
+                  
+class OmfPanel(ScrolledPanel, PanelBase):
+    """
+        Provides the sas gen calculator GUI.
+    """
+    ## Internal nickname for the window, used by the AUI manager
+    window_name = "SLD Pixel Info"
+    ## Name to appear on the window title bar
+    window_caption = "SLD Pixel Info "
+    ## Flag to tell the AUI manager to put this panel in the center pane
+    CENTER_PANE = False
+    
+    def __init__(self, parent, *args, **kwds):
+        ScrolledPanel.__init__(self, parent, style=wx.RAISED_BORDER, 
+                               *args, **kwds)
+        PanelBase.__init__(self)
+        #Font size 
+        self.SetWindowVariant(variant=FONT_VARIANT)
+        self.SetupScrolling()  
+        # Object that receive status event
+        self.parent = parent
+        self.sld_data = sans_gen.MagSLD([0], [0], [0]) 
+        self.sld_ctl = None
+        self.default_shape = 'rectangular'
+        self._do_layout()
+       
+    def set_slddata(self, slddata):
+        """
+        Set sld data related items
+        """
+        self.sld_data = slddata
+        self._set_slddata_ctr_val(slddata)
+        # Make sure that self._set_slddata_ctr_val() is finished
+        wx.CallAfter(self._set_omfdata_ctr, slddata)
+    
+    def get_sld_val(self):
+        """
+        Set sld_n of slddata on sld input
+        """
+        sld_sets = {}
+        if not self.sld_data.is_data:
+            self._get_other_val()
+        for list in self.slds:
+            if list[1].IsEnabled():
+                list[1].SetBackgroundColour("white")
+                list[1].Refresh()
+                try:
+                    val = float(list[1].GetValue())
+                    sld_sets[list[0]] = val
+                except:
+                    flag = _set_error(self, list[1])
+                    if not flag:
+                        return self.sld_data
+            else:
+               sld_sets[list[0]]= None
+        for key in sld_sets.keys():
+            key_low = key.lower()
+            if key_low.count('mx') > 0:
+                if sld_sets[key] == None:
+                    sld_sets[key] = self.sld_data.sld_mx
+                mx = sld_sets[key]
+            elif key_low.count('my') > 0:
+                if sld_sets[key] == None:
+                    sld_sets[key] = self.sld_data.sld_my
+                my = sld_sets[key]
+            elif key_low.count('mz') > 0:
+                if sld_sets[key] == None:
+                    sld_sets[key] = self.sld_data.sld_mz
+                mz = sld_sets[key]
+            else:
+                if sld_sets[key] != None:
+                    self.sld_data.set_sldn(sld_sets[key])
+        self.sld_data.set_sldms(mx, my, mz)
+        self._set_slddata_ctr_val(self.sld_data)
+        
+        return self.sld_data
+    
+    def get_pix_volume(self):
+        """
+        Get the pixel volume
+        """
+        val = 1
+        for lst in self.stepsize:
+            val *= float(lst[1].GetValue())
+        
+        return numpy.fabs(val)
+                
+    def _get_other_val(self):  
+        """
+        """
+        omfdata = sans_gen.OMFData()
+        sets = {}
+        try:
+            for lst in self.stepsize:
+                if lst[1].IsEnabled():
+                    val = float(lst[1].GetValue())
+                    sets[lst[0]] = val
+                else:
+                    sets[lst[0]] = None
+                    return
+            for lst in self.nodes:
+                if lst[1].IsEnabled():
+                    val = float(lst[1].GetValue())
+                    sets[lst[0]] = val
+                else:
+                    sets[lst[0]] = None
+                    return
+                    
+            for key in sets.keys():
+                exec "omfdata.%s = sets['%s']"% (key, key)            
+
+            omf2sld = sans_gen.OMF2SLD()
+            omf2sld.set_data(omfdata, self.default_shape)
+            self.sld_data = omf2sld.output
+            self.sld_data.is_data = False
+            self.sld_data.filename = "Default SLD Profile"
+        except:
+            msg = "OMF Panel: %s"% sys.exc_value
+            infor = 'Error'
+            #logging.error(msg)
+            if self.parent.parent != None:
+                # inform msg to wx
+                wx.PostEvent(self.parent.parent,
+                        StatusEvent(status=msg, info=infor))
+                self.SetFocus()
+                
+    def _set_slddata_ctr_val(self, slddata):
+        """
+        Set slddata crl
+        """
+        try:
+            val = str(len(slddata.sld_n))
+        except:
+            val = 'Unknown'
+        self.npix_ctl.SetValue(val)
+        
+    def _set_omfdata_ctr(self, omfdata):    
+        """
+        Set the textctr box values
+        """
+        
+        if omfdata == None:
+            self._set_none_text()
+            return
+        nodes_list = self._get_nodes_key_list(omfdata)
+        step_list = self._get_step_key_list(omfdata)
+        for ctr_list in self.nodes:
+            for key in nodes_list.keys():
+                if ctr_list[0] == key:
+                    ctr_list[1].SetValue(format_number(nodes_list[key], True))
+                    ctr_list[1].Enable(not omfdata.is_data)
+                    break
+        for ctr_list in self.stepsize:
+            for key in step_list.keys():
+                if ctr_list[0] == key:
+                    ctr_list[1].SetValue(format_number(step_list[key], True))
+                    ctr_list[1].Enable(not omfdata.is_data)
+                    break   
+                
+    def _set_none_text(self):
+        """
+        Set Unknown in textctrls
+        """
+        val = 'Unknown'
+        for ctr_list in self.nodes:
+            ctr_list[1].SetValue(val)
+        for ctr_list in self.stepsize:
+            ctr_list[1].SetValue(val)
+                
+    def _define_structure(self):
+        """
+        Define the main sizers building to build this application.
+        """
+        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        self.npixels_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.box_sld = wx.StaticBox(self, -1, str("Mean SLD"))
+        self.box_node = wx.StaticBox(self, -1, str("Nodes"))
+        self.boxsizer_sld = wx.StaticBoxSizer(self.box_sld, wx.VERTICAL)
+        self.box_stepsize = wx.StaticBox(self, -1, str("Step Size"))
+        self.boxsizer_node = wx.StaticBoxSizer(self.box_node, wx.VERTICAL)
+        self.boxsizer_stepsize = wx.StaticBoxSizer(self.box_stepsize,
+                                                    wx.VERTICAL)
+        self.sld_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.node_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.step_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.hint_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
+                
+    def _layout_npix(self):
+        """
+        Build No of pixels sizer
+        """
+        num_pix_text = wx.StaticText(self, -1, "No. of Pixels: ")  
+        self.npix_ctl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
+                                style=wx.TE_PROCESS_ENTER)
+        self._set_slddata_ctr_val(self.sld_data)
+        self._set_omfdata_ctr(self.sld_data) 
+        self.npixels_sizer.AddMany([(num_pix_text, 0,
+                                          wx.EXPAND|wx.LEFT|wx.TOP, 5),
+                                     (self.npix_ctl, 0,
+                                     wx.EXPAND|wx.TOP, 5)])
+
+    def _layout_slds(self):
+        """
+        Build nuclear sld sizer
+        """
+        self.slds = []
+        omfdata = self.sld_data
+        if omfdata == None:
+            raise
+        sld_key_list = self._get_slds_key_list(omfdata)
+        # Dic is not sorted
+        key_list = [key for key in sld_key_list.keys()]
+        # Sort here
+        key_list.sort()
+        is_data = self.sld_data.is_data
+        sizer = wx.GridBagSizer(2, 3)
+        ix = 0
+        iy = -1
+        for key in key_list:
+            value = sld_key_list[key]
+            iy += 1
+            ix = 0
+            name = wx.StaticText(self, -1, key)
+            sizer.Add(name, (iy, ix), (1, 1), \
+                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+            ## add parameter value
+            ix += 1
+            ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
+                                style=wx.TE_PROCESS_ENTER)
+            ctl.SetValue(format_number(value, True))
+            ctl.Enable(not is_data)
+            sizer.Add(ctl, (iy, ix), (1, 1), wx.EXPAND)
+            ## add unit
+            ix += 1
+            s_unit = '[' + omfdata.sld_unit + ']'
+            unit = wx.StaticText(self, -1, s_unit)
+            sizer.Add(unit, (iy, ix), (1, 1), \
+                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+            self.slds.append([key, ctl, unit])
+        self.sld_sizer.Add(sizer, 0, wx.LEFT, 10) 
+               
+    def _layout_nodes(self):
+        """
+        Fill the sizer containing data's name
+        """
+        self.nodes = []
+        omfdata = self.sld_data
+        if omfdata == None:
+            raise
+        key_list = self._get_nodes_key_list(omfdata)
+        is_data = self.sld_data.is_data
+        sizer = wx.GridBagSizer(2, 3)
+        ix = 0
+        iy = -1
+        for key, value in key_list.iteritems():
+            iy += 1
+            ix = 0
+            name = wx.StaticText(self, -1, key)
+            sizer.Add(name, (iy, ix), (1, 1), \
+                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+            ## add parameter value
+            ix += 1
+            ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
+                                style=wx.TE_PROCESS_ENTER)
+            ctl.Bind(wx.EVT_TEXT, self._onparamEnter )
+            ctl.SetValue(format_number(value, True))
+            ctl.Enable(not is_data)
+            sizer.Add(ctl, (iy, ix), (1, 1), wx.EXPAND)
+            ## add unit
+            ix += 1
+            unit = wx.StaticText(self, -1, '')
+            sizer.Add(unit, (iy, ix), (1, 1), \
+                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+            self.nodes.append([key, ctl, unit])
+        self.node_sizer.Add(sizer, 0, wx.LEFT, 10)
+            
+    def _layout_stepsize(self):
+        """
+        Fill the sizer containing slit size information
+        """
+        self.stepsize = []
+        omfdata = self.sld_data
+        if omfdata == None:
+            raise
+        key_list = self._get_step_key_list(omfdata)
+        is_data = self.sld_data.is_data
+        sizer = wx.GridBagSizer(2, 3)
+        ix = 0
+        iy = -1
+        #key_list.sort()
+        for key, value in key_list.iteritems():
+            iy += 1
+            ix = 0
+            name = wx.StaticText(self, -1, key)
+            sizer.Add(name, (iy, ix), (1, 1), \
+                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+            ## add parameter value
+            ix += 1
+            ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
+                                style=wx.TE_PROCESS_ENTER)
+            ctl.SetValue(format_number(value, True))
+            ctl.Enable(not is_data)
+            sizer.Add(ctl, (iy, ix), (1, 1), wx.EXPAND)
+            ## add unit
+            ix += 1
+            p_unit = '[' + omfdata.pos_unit + ']'
+            unit = wx.StaticText(self, -1, p_unit)
+            sizer.Add(unit, (iy, ix), (1, 1), \
+                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+            self.stepsize.append([key, ctl, unit])
+        self.step_sizer.Add(sizer, 0, wx.LEFT, 10)
+    
+    def _layout_hint(self):
+        """
+        Fill the sizer containing hint 
+        """
+        hint_msg = "Load an omf or 3d sld profile data file."
+        self.hint_txt = wx.StaticText(self, -1, hint_msg)
+        self.hint_sizer.AddMany([(self.hint_txt, 0, wx.LEFT, 15)])
+    
+    def _layout_button(self):  
+        """
+        Do the layout for the button widgets
+        """ 
+        self.bt_draw = wx.Button(self, wx.NewId(),'Draw Points')
+        self.bt_draw.Bind(wx.EVT_BUTTON, self.on_sld_draw)
+        self.bt_draw.SetToolTipString("Draw a scatter plot for sld profile.")
+        self.bt_save = wx.Button(self, wx.NewId(),'Save SLD Data')
+        self.bt_save.Bind(wx.EVT_BUTTON, self.on_save)
+        self.bt_save.Enable(False)
+        self.bt_save.SetToolTipString("Save SLD data.")
+        self.button_sizer.AddMany([(self.bt_draw, 0, wx.LEFT, 10),
+                                   (self.bt_save, 0, wx.LEFT, 10)])
+        
+    def _do_layout(self):
+        """
+        Draw window content
+        """
+        self._define_structure()
+        self._layout_nodes()
+        self._layout_stepsize()
+        self._layout_npix()
+        self._layout_slds()
+        #self._layout_hint()
+        self._layout_button()
+        self.boxsizer_node.AddMany([(self.node_sizer, 0,
+                                    wx.EXPAND|wx.TOP, 5),
+                                     (self.hint_sizer, 0,
+                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
+        self.boxsizer_stepsize.AddMany([(self.step_sizer, 0,
+                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5),])
+        self.boxsizer_sld.AddMany([(self.sld_sizer, 0,
+                                     wx.EXPAND|wx.BOTTOM, 5),])
+        self.main_sizer.AddMany([(self.npixels_sizer, 0, wx.EXPAND|wx.ALL, 10),
+                        (self.boxsizer_sld, 0, wx.EXPAND|wx.ALL, 10),
+                        (self.boxsizer_node, 0, wx.EXPAND|wx.ALL, 10),
+                        (self.boxsizer_stepsize, 0, wx.EXPAND|wx.ALL, 10),
+                        (self.button_sizer, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
+        self.SetSizer(self.main_sizer)
+        self.SetAutoLayout(True)
+        
+    def _get_nodes_key_list(self, data):
+        """
+        Return nodes key list
+        
+        :Param data: OMFData
+        """
+        key_list = {'xnodes' : data.xnodes, 
+                    'ynodes' : data.ynodes,
+                    'znodes' : data.znodes}
+        return key_list
+        
+    def _get_slds_key_list(self, data):
+        """
+        Return nodes key list
+        
+        :Param data: OMFData
+        """
+        key_list = {'Nucl.' : data.sld_n, 
+                    'Mx' : data.sld_mx,
+                    'My' : data.sld_my,
+                    'Mz' : data.sld_mz}
+        return key_list
+
+    def _get_step_key_list(self, data):
+        """
+        Return step key list
+        
+        :Param data: OMFData
+        """
+        key_list = {'xstepsize' : data.xstepsize, 
+                    'ystepsize' : data.ystepsize,
+                    'zstepsize' : data.zstepsize}
+        return key_list        
+    
+    def set_sld_ctr(self, sld_data):
+        """
+        """
+        if sld_data == None:
+            for ctr_list in self.slds:
+                ctr_list[1].Enable(False)
+                #break   
+            return
+        
+        self.sld_data = sld_data
+        sld_list = self._get_slds_key_list(sld_data)
+        for ctr_list in self.slds:
+            for key in sld_list.keys():
+                if ctr_list[0] == key:
+                    min_val = numpy.min(sld_list[key])
+                    max_val = numpy.max(sld_list[key])
+                    mean_val = numpy.mean(sld_list[key])
+                    enable = (min_val == max_val)
+                    ctr_list[1].SetValue(format_number(mean_val, True))
+                    ctr_list[1].Enable(enable)
+                    break   
+
+    def on_sld_draw(self, event):
+        """
+        Draw sld profile as scattered plot
+        """
+        self.parent.sld_draw()
+         
+    def on_save(self, event):
+        """
+        Close the window containing this panel
+        """
+        flag = True
+        flag = self.check_inputs()
+        if not flag:
+            return
+        self.sld_data = self.get_sld_val()
+        self.parent.set_main_panel_sld_data(self.sld_data)
+        
+        reader = sans_gen.SLDReader() 
+        extension = '*.sld'
+        path = None
+        data= None
+        location = self.parent.get_path()
+        dlg = wx.FileDialog(self, "Save sld file",
+                            location, "sld_file",
+                             extension, 
+                             wx.SAVE)
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            self.parent.set_file_location(os.path.dirname(path))
+        else:
+            return None
+        dlg.Destroy()
+        try:
+            if path is None:
+                return
+            
+            data = self.parent.get_sld_data()
+            fName = os.path.splitext(path)[0] + '.' + extension.split('.')[-1]
+            if data != None:
+                try:
+                    reader.write(fName, data)
+                except:
+                    raise
+            else:
+                msg = "%s cannot write %s\n" % ('Generic Scattering', str(path))
+                infor = 'Error'
+                #logging.error(msg)
+                if self.parent.parent != None:
+                    # inform msg to wx
+                    wx.PostEvent(self.parent.parent,
+                            StatusEvent(status=msg, info=infor))
+                    self.SetFocus()
+            return
+        except:
+            msg = "Error occurred while saving. "
+            infor = 'Error'
+            if self.parent.parent != None:
+                # inform msg to wx
+                wx.PostEvent(self.parent.parent,
+                        StatusEvent(status=msg, info=infor)) 
+                self.SetFocus()   
+
+    def _onparamEnter(self, event):
+        """
+        """
+        flag = True
+        if event != None:
+            event.Skip()
+            ctl = event.GetEventObject()
+            ctl.SetBackgroundColour("white")
+            #_set_error(self, ctl)
+        try:
+            float(ctl.GetValue())
+        except:
+            flag = _set_error(self, ctl)
+        if flag:
+            npts = 1
+            for item in self.nodes:
+                n_val = float(item[1].GetValue())
+                if n_val <= 0:
+                    item[1].SetBackgroundColour("pink")
+                    npts = -1
+                    break
+                npts *= int(n_val)
+            if npts > 0:
+                nop = self.set_npts_from_slddata()
+                if nop == None:
+                    nop = npts
+                self.display_npts(nop)
+        ctl.Refresh()
+        return flag
+    
+    def set_npts_from_slddata(self):
+        """
+        Set total n. of points form the sld data
+        """
+        try:
+            sld_data = self.parent.get_sld_from_omf()
+            #nop = (nop * numpy.pi) / 6
+            nop = len(sld_data.sld_n)
+        except:
+            nop = None
+        return nop
+    
+    def display_npts(self, nop):
+        """
+        Displays Npts ctrl
+        """
+        try:
+            self.npix_ctl.SetValue(str(nop))
+            self.npix_ctl.Refresh()
+            self.parent.set_etime()
+        except:
+            # On Init
+            pass
+    
+    def check_inputs(self):
+        """
+        check if the inputs are valid
+        """
+        flag = self._check_input_helper(self.slds)
+        if flag:
+            flag = self._check_input_helper(self.nodes)
+        if flag:
+            flag = self._check_input_helper(self.stepsize)
+        return flag
+    
+    def _check_input_helper(self, list):
+        """
+        Check list values
+        """
+        flag = True
+        for item in list:
+            item[1].SetBackgroundColour("white")
+            item[1].Refresh()
+            try:
+                float(item[1].GetValue())
+            except:
+                flag = _set_error(self, item[1])
+                break
+        return flag
+
+class SasGenWindow(wx.Frame):
+    """
+    GEN SAS main window
+    """
+    def __init__(self, parent=None, title="Generic Scattering Calculator",
+                size=(PANEL_WIDTH * 1.3, PANEL_HEIGHT * 1.5), *args, **kwds):
+        """
+        Init
+        """
+        kwds['size']= size
+        kwds['title']= title
+        
+        wx.Frame.__init__(self, parent, *args, **kwds)
+        self.parent = parent
+        self.omfpanel = OmfPanel(parent=self)
+        self.panel = SasGenPanel(parent=self)
+        self.data = None
+        self.omfdata = sans_gen.OMFData()
+        self.sld_data = None
+        self._default_save_location = os.getcwd() 
+        self._mgr = aui.AuiManager(self)
+        self._mgr.SetDockSizeConstraint(0.5, 0.5)
+        self._plot_title = ''
+        self.scale2d = 'log_{10}'
+        
+        #self.plot_frame = PlotFrame(self, -1, 'testView')
+        self._build_menubar()
+        
+        self.build_panels()
+        self.Centre()
+        self.Show(True)
+    
+    def _build_menubar(self):
+        """
+        Menu bar
+        """
+        menu_bar = wx.MenuBar()
+        
+        menu = wx.Menu()
+        id = wx.NewId()
+        menu.Append(id, "&Open sld/omf file")
+        wx.EVT_MENU(self, id, self.on_open_file)
+        id = wx.NewId()
+        menu.Append(id, "&Save as sld file")
+        wx.EVT_MENU(self, id, self.on_save_file)
+        id = wx.NewId()
+        menu.Append(id, "&Quit")
+        menu_bar.Append(menu, "File")
+        wx.EVT_MENU(self, id, self.on_close)
+        
+        menu_help = wx.Menu()
+        id = wx.NewId()
+        menu_help.Append(id, "&Theory and GUI")
+        wx.EVT_MENU(self, id, self.on_help)
+        menu_bar.Append(menu_help, "&Help")
+        
+        self.SetMenuBar(menu_bar)
+        
+    def build_panels(self):
+        """
+        """
+        
+        self.set_sld_data(self.sld_data)
+        
+        self._mgr.AddPane(self.panel, aui.AuiPaneInfo().
+                              Name(self.panel.window_name).
+                              #Caption(self.panel.window_caption).
+                              CenterPane().
+                              # This is where we set the size of
+                              # the application window
+                              BestSize(wx.Size(PANEL_WIDTH, 
+                                               PANEL_HEIGHT)).
+                              Show()) 
+        self._mgr.AddPane(self.omfpanel, aui.AuiPaneInfo().
+                              Name(self.omfpanel.window_name).
+                              Caption(self.omfpanel.window_caption).
+                              #MinimizeButton(True).
+                              CloseButton(False).
+                              Right().
+                              Floatable(False).
+                              BestSize(wx.Size(PANEL_WIDTH/2.5, PANEL_HEIGHT)).
+                              Show())  
+        self._mgr.Update()
+
+    def get_sld_data(self):
+        """
+        Return slddata
+        """
+        return self.sld_data
+    
+    def get_sld_from_omf(self):
+        """
+        """
+        self.sld_data = self.omfpanel.get_sld_val()
+        return self.sld_data
+    
+    def set_sld_n(self, sld):
+        """
+        """
+        self.panel.sld_data = sld
+        self.panel.model.set_sld_data(sld)
+        
+    def set_sld_data(self, data):
+        """
+        Set omfdata
+        """
+        if data == None:
+            return
+        self.sld_data = data
+        
+        enable = (not data==None)
+        #print "main mx", data.sld_mx
+        self._set_omfpanel_sld_data(self.sld_data)
+        self.omfpanel.bt_save.Enable(enable)
+        self.set_etime()
+    
+    def set_omfpanel_npts(self):
+        """
+        Set Npts in omf panel
+        """
+        nop = self.omfpanel.set_npts_from_slddata()
+        self.omfpanel.display_npts(nop)
+        
+    def _set_omfpanel_sld_data(self, data):
+        """
+        Set sld_data in omf panel
+        """
+        self.omfpanel.set_slddata(data) 
+        self.omfpanel.set_sld_ctr(data)
+    
+    def check_omfpanel_inputs(self):
+        """
+        Check OMF panel inputs
+        """
+        return self.omfpanel.check_inputs()  
+       
+    def set_main_panel_sld_data(self, sld_data):
+        """
+        """
+        self.sld_data = sld_data
+        
+    def set_file_location(self, path):
+        """
+        File location
+        """
+        self._default_save_location = path
+    
+    def get_path(self):
+        """
+        File location
+        """
+        return self._default_save_location
+    
+    def draw_graph(self, plot, title=''):
+        """
+        """
+        frame = PlotFrame(self, -1, 'testView', self.scale2d)
+        add_icon(self.parent, frame)
+        frame.add_plot(plot)
+        frame.SetTitle(title)
+        frame.Show(True)
+        frame.SetFocus()
+    
+    def get_npix(self):
+        """
+        Get no. of pixels from omf panel
+        """
+        n_pix = self.omfpanel.npix_ctl.GetValue()
+        return n_pix
+    
+    def get_pix_volume(self):
+        """
+        Get a pixel volume
+        """
+        vol = self.omfpanel.get_pix_volume()
+        return vol
+    
+    def set_omfpanel_default_shap(self, shape):
+        """
+        Set default_shape in omfpanel
+        """
+        self.omfpanel.default_shape = shape
+    
+    def set_etime(self):
+        """
+        Sets est. computation time on panel
+        """
+        self.panel.set_est_time()
+    
+    def get_sld_data_from_omf(self):
+        """
+        """
+        data = self.omfpanel.get_sld_val() 
+        return data
+       
+    def set_scale2d(self, scale):
+        """
+        """
+        self.scale2d = scale
+            
+    def on_panel_close(self, event):
+        """
+        """
+        #Not implemented
+          
+    def on_open_file(self, event):
+        """
+        On Open
+        """
+        self.panel.on_load_data(event)
+    
+    def sld_draw(self):
+        """
+        sld draw
+        """
+        self.panel.sld_draw(has_arrow=False)
+        
+    def on_save_file(self, event):
+        """
+        On Close
+        """
+        self.omfpanel.on_save(event)
+        
+    def on_close(self, event):
+        """
+        Close
+        """
+        self.Destroy()
+        
+    def on_help(self, event):    
+        """
+        Gen scatter angle help panel
+        """
+        from help_panel import  HelpWindow
+        # Get models help model_function path
+        import sans.perspectives.calculator as calmedia
+
+        media = calmedia.get_data_path(media='media')
+        path = os.path.join(media,"gen_sans_help.html") 
+        name = "Polarization Angle"
+        frame = HelpWindow(self, -1, title=' Help: Polarization Angle',  
+                           pageToOpen=path, size=(865, 450))   
+        try: 
+            frame.splitter.DetachWindow(frame.lpanel)
+            # Display only the right side one
+            frame.lpanel.Hide() 
+            frame.Show(True)
+        except:
+            raise
+            frame.Destroy() 
+            msg = 'Display Error\n'
+            info = "Info"
+            wx.MessageBox(msg, info)
+           
+if __name__ == "__main__": 
+    app = wx.PySimpleApp()
+    frame = SasGenWindow()    
+    frame.Show(True)
+    app.MainLoop()     
Index: calculatorview/src/sans/perspectives/calculator/help_panel.py
===================================================================
--- calculatorview/src/sans/perspectives/calculator/help_panel.py	(revision ce3feca1e7d2324618e496e9578a205a745f3d33)
+++ calculatorview/src/sans/perspectives/calculator/help_panel.py	(revision 318b5bbbcb046db18a8e953dccae04fc7b8a71e0)
@@ -13,13 +13,14 @@
     """
     """
-    def __init__(self, parent, title="Tool Help", pageToOpen=None):
-        wx.Frame.__init__(self, parent, title, size=(700, 450))
+    def __init__(self, parent, id=-1, title="Tool Help", pageToOpen=None, 
+                                                    size=(700, 450)):
+        wx.Frame.__init__(self, parent, id, title, size=size)
         """
              contains help info
         """
-        self.SetTitle("Tool Help",) 
+        self.SetTitle(title) 
         splitter = MultiSplitterWindow(self, style=wx.SP_LIVE_UPDATE)
         rpanel = wx.Panel(splitter, -1)
-        lpanel = wx.Panel(splitter, -1, style=wx.BORDER_SUNKEN)
+        self.lpanel = wx.Panel(splitter, -1, style=wx.BORDER_SUNKEN)
         
         vbox = wx.BoxSizer(wx.VERTICAL)
@@ -37,5 +38,5 @@
        
         vboxl = wx.BoxSizer(wx.VERTICAL)
-        headerl = wx.Panel(lpanel, -1, size=(-1, 20))
+        headerl = wx.Panel(self.lpanel, -1, size=(-1, 20))
        
         headerl.SetBackgroundColour('#6666FF')
@@ -49,9 +50,12 @@
         headerl.SetSizer(hboxl)
         vboxl.Add(headerl, 0, wx.EXPAND)
-        self.lhelp = html.HtmlWindow(lpanel, -1, style=wx.NO_BORDER)
+        self.lhelp = html.HtmlWindow(self.lpanel, -1, style=wx.NO_BORDER)
         self.rhelp = html.HtmlWindow(rpanel, -1, style=wx.NO_BORDER, 
                                      size=(500,-1))
-        
-        self.path = calculator.get_data_path(media='media')
+        if pageToOpen != None:
+            path = os.path.dirname(pageToOpen)
+            self.path = os.path.join(path, "gen_sans_help.html")
+        else:
+            self.path = calculator.get_data_path(media='media')
        
         page1 = """<html>
@@ -76,4 +80,6 @@
             <li><a href ="resolution_calculator_help.html" 
             target ="showframe">Resolution Estimator</a><br></li>
+            <li><a href ="gen_sans_help.html" 
+            target ="showframe">Generic Scattering Calculator</a><br></li>
             <li><a href ="pycrust_help.html" 
             target ="showframe">Python Shell</a><br></li>
@@ -84,17 +90,19 @@
         self.lhelp.SetPage(page)
         self.lhelp.Bind(wx.html.EVT_HTML_LINK_CLICKED, self.OnLinkClicked)
-        
+        if  pageToOpen != None:
+            self.rhelp.LoadPage(str(pageToOpen))
         vbox.Add(self.rhelp, 1, wx.EXPAND)
         vboxl.Add(self.lhelp, 1, wx.EXPAND)
         rpanel.SetSizer(vbox)
-        lpanel.SetSizer(vboxl)
-        lpanel.SetFocus()
+        self.lpanel.SetSizer(vboxl)
+        self.lpanel.SetFocus()
         
         vbox1 = wx.BoxSizer(wx.HORIZONTAL)
         vbox1.Add(splitter, 1, wx.EXPAND)
-        splitter.AppendWindow(lpanel, 200)
+        splitter.AppendWindow(self.lpanel, 200)
         splitter.AppendWindow(rpanel)
         self.SetSizer(vbox1)
-       
+        
+        self.splitter = splitter
         self.Centre()
         self.Show(True)
Index: calculatorview/src/sans/perspectives/calculator/load_thread.py
===================================================================
--- calculatorview/src/sans/perspectives/calculator/load_thread.py	(revision 7343319fee4b49439bdc4c6aab393f6ab7902005)
+++ calculatorview/src/sans/perspectives/calculator/load_thread.py	(revision 318b5bbbcb046db18a8e953dccae04fc7b8a71e0)
@@ -46,3 +46,45 @@
             # Real code should not print, but this is an example...
             raise
-      
+        
+class GenReader(CalcThread):
+    """
+        Load a sld data given a filename
+    """
+    def __init__(self, path, loader, 
+                 completefn=None,
+                 updatefn   = None,
+                 yieldtime  = 0.01,
+                 worktime   = 0.01
+                 ):
+        CalcThread.__init__(self, completefn,
+                 updatefn,
+                 yieldtime,
+                 worktime)
+        self.path = path
+        #Instantiate a loader 
+        self.loader = loader
+        self.starttime = 0  
+        
+    def isquit(self):
+        """
+             @raise KeyboardInterrupt: when the thread is interrupted
+        """
+        try:
+            CalcThread.isquit(self)
+        except KeyboardInterrupt:
+            raise KeyboardInterrupt   
+        
+        
+    def compute(self):
+        """
+            read some data
+        """
+        self.starttime = time.time()
+        try:
+            data =  self.loader.read(self.path)
+            self.complete(data=data)
+        except:
+            # Thread was interrupted, just proceed and re-raise.
+            # Real code should not print, but this is an example...
+            raise
+            
Index: calculatorview/src/sans/perspectives/calculator/media/gen_sans_help.html
===================================================================
--- calculatorview/src/sans/perspectives/calculator/media/gen_sans_help.html	(revision 318b5bbbcb046db18a8e953dccae04fc7b8a71e0)
+++ calculatorview/src/sans/perspectives/calculator/media/gen_sans_help.html	(revision 318b5bbbcb046db18a8e953dccae04fc7b8a71e0)
@@ -0,0 +1,115 @@
+<body>
+
+<h4>Polarization and Magnetic Scattering</h4>
+<ul>
+<li><a href="#theory">Theory:</a></li>
+<li><a href="#gui">GUI</a></li>
+</ul>
+<br>
+<b>1. <a name="theory">Theory</a> </b>
+<br>
+In general, a particle with a volume V can be described by an ensemble containing N 
+3-dimensional rectangular pixels where each pixels are much smaller than V. 
+Assuming that
+all the pixel sizes are same, the elastic scattering intensity 
+by the particle 
+<p>
+<img src="gen_i.gif"/>
+</p>
+<br>
+where &#946;<sub>j</sub> and r<sub>j</sub> are the scattering 
+length density and the position 
+of the j'th pixel respectively. And the total volume
+<p>
+<img src="v_j.gif"/>
+</p>
+<br>
+for &#946;<sub>j</sub> &#8800; 0 where v<sub>j</sub> is the volume of the j'th pixel.
+<br>
+The scattering length density (SLD) of each pixel where the SLD is uniform, is a combination of the nuclear and magnetic SLDs 
+and depends on the spin states of the neutrons as follows:
+<br>
+<br>
+For magnetic scattering, only the magnetization component, <b>M</b><sub>perp</sub>, 
+perpendicular to the scattering vector <b>Q</b> contributes to the the magnetic 
+scattering length. (Figure below).
+<p>
+<img src="mag_vector.bmp"/>
+</p>
+<br>
+The magnetic scattering length density is then
+<p>
+<img src="dm_eq.gif"/>
+</p>
+<br>
+where &#947; = -1.913 the gyromagnetic ratio,  	&#956;<sub>B</sub> is the Bohr magneton, 
+r<sub>0</sub> is the classical radius of electron, 
+and <b>&#963;</b> is the Pauli spin.
+<br>
+For polarized neutron, the magnetic scattering is depending on the spin states.
+Let's consider that the incident neutrons are polarized parallel (+)/anti-parallel 
+(&#8211;) to the x' axis (See both Figures above).
+The possible out-coming states then are + and - states for both incident states.
+<br>
+ - Non-spin-flips:  	(+ +) and  	(- -)
+<br>
+ - Spin-flips:  	(+ -) and  	(- +)
+<br>
+<p>
+<img src="gen_mag_pic.bmp"/>
+</p>
+<br>
+<br>
+Now, let's assume that the angles of the <b>Q</b> vector and the spin-axis (x') from x-axis 
+are &#966; and  &#952;<sub>up</sub>, respectively (See Figure above).
+Then, depending upon the polarization (spin) state of neutrons, the scattering length
+densities , including the nuclear scattering length density (&#946; <sub>N</sub>) are given as, for non-spin-flips,
+<p>
+<img src="sld1.gif"/>
+</p>
+<br>
+<br>
+for spin-flips,
+<p>
+<img src="sld2.gif"/>
+</p>
+<br>
+<br>
+where
+<p>
+<img src="mxp.gif"/>
+</p>
+<p>
+<img src="myp.gif"/>
+</p>
+<p>
+<img src="mzp.gif"/>
+</p>
+<p>
+<img src="mqx.gif"/>
+</p>
+<p>
+<img src="mqy.gif"/>
+</p>
+<br>
+<br>
+Here, the M<sub>0x</sub>, M<sub>0y</sub> and M<sub>0z</sub> are the x, y and z 
+components of the magnetization vector given in the xyz lab frame.
+<br>
+<br>
+<b>2. <a name="gui">GUI</a> </b>
+<br>
+<p>
+<img src="gen_gui_help.bmp"/>
+</p>
+<br>
+<p>
+After the computation, the result will be listed in the 'Theory' box 
+in the data explorer panel on the main window.
+<br>
+The 'Up_frac_i' and 'Up_frac_f' are the ratio, (spin up) /(spin up + spin down) neutrons 
+before the sample and at the analyzer, respectively.
+</p>
+<br>
+*Note: The values of 'Up_frac_i' and 'Up_frac_f' must be in the range between 0 and 1.
+</body>
Index: calculatorview/src/sans/perspectives/calculator/model_editor.py
===================================================================
--- calculatorview/src/sans/perspectives/calculator/model_editor.py	(revision 657e52c6e801e630d2fc6f1722a752b701d20771)
+++ calculatorview/src/sans/perspectives/calculator/model_editor.py	(revision 318b5bbbcb046db18a8e953dccae04fc7b8a71e0)
@@ -11,6 +11,6 @@
 import sys
 import os
+import math
 from wx.py.editwindow import EditWindow
-import subprocess
 
 if sys.platform.count("win32") > 0:
@@ -24,5 +24,5 @@
 M_NAME = 'Model'
 EDITOR_WIDTH = 800
-EDITOR_HEIGTH = 720
+EDITOR_HEIGTH = 735
 PANEL_WIDTH = 500
 _BOX_WIDTH = 55
@@ -530,4 +530,5 @@
         self.warning = ""
         self._description = "New Custom Model"
+        self.function_tcl = None
         #self._default_save_location = os.getcwd()
         self._do_layout()
@@ -546,4 +547,5 @@
         self.param_sizer = wx.BoxSizer(wx.VERTICAL)
         self.function_sizer = wx.BoxSizer(wx.VERTICAL)
+        self.func_horizon_sizer = wx.BoxSizer(wx.HORIZONTAL)
         self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
         self.msg_sizer = wx.BoxSizer(wx.HORIZONTAL)
@@ -592,4 +594,5 @@
         """
         param_txt = wx.StaticText(self, -1, 'Fit Parameters (if any): ') 
+        
         param_tip = "#Set the parameters and initial values.\n"
         param_tip += "#Example:\n"
@@ -601,8 +604,8 @@
         self.param_tcl.setDisplayLineNumbers(True)
         self.param_tcl.SetToolTipString(param_tip)
+
         self.param_sizer.AddMany([(param_txt, 0, wx.LEFT, 10),
                         (self.param_tcl, 1, wx.EXPAND|wx.ALL, 10)])
-
-   
+                
     def _layout_function(self):
         """
@@ -616,4 +619,7 @@
         hint_function += "    y = A + B * cos(2 * pi * x)\n"
         hint_function += "return y\n"
+        math_txt = wx.StaticText(self, -1, '*Useful math functions: ')
+        math_combo = self._fill_math_combo()
+        
         id  = wx.NewId() 
         self.function_tcl = EditWindow(self, id, wx.DefaultPosition, 
@@ -621,5 +627,10 @@
         self.function_tcl.setDisplayLineNumbers(True)
         self.function_tcl.SetToolTipString(hint_function)
-        self.function_sizer.Add(function_txt, 0, wx.LEFT, 10)
+        
+        self.func_horizon_sizer.Add(function_txt)
+        self.func_horizon_sizer.Add(math_txt, 0, wx.LEFT, 250)
+        self.func_horizon_sizer.Add(math_combo, 0, wx.LEFT, 10)
+
+        self.function_sizer.Add(self.func_horizon_sizer, 0, wx.LEFT, 10)
         self.function_sizer.Add( self.function_tcl, 1, wx.EXPAND|wx.ALL, 10)
         
@@ -682,5 +693,39 @@
         self.SetSizer(self.main_sizer)
         self.SetAutoLayout(True)
-    
+
+    def _fill_math_combo(self):
+        """
+        Fill up the math combo box
+        """
+        self.math_combo = wx.ComboBox(self, -1, size=(100, -1), 
+                                      style=wx.CB_READONLY) 
+        for item in dir(math):
+            if item.count("_") < 1:
+                try:
+                    exec "float(math.%s)"% item
+                    self.math_combo.Append(str(item))
+                except:
+                    self.math_combo.Append(str(item) + "()" )
+        self.math_combo.Bind(wx.EVT_COMBOBOX, self._on_math_select)
+        self.math_combo.SetSelection(0)
+        return self.math_combo
+        
+    def _on_math_select(self, event):
+        """
+        On math selection on ComboBox
+        """
+        event.Skip()
+        label = self.math_combo.GetLabel() 
+        self.function_tcl.SetFocus()
+        # Put the text at the cursor position
+        pos = self.function_tcl.GetCurrentPos()
+        self.function_tcl.InsertText(pos, label)  
+        # Put the cursor at appropriate postion
+        length = len(label)
+        if label[-1] == ')':
+            length -= 1
+        f_pos = pos + length
+        self.function_tcl.GotoPos(f_pos)   
+
     def get_notes(self):
         """
@@ -1071,5 +1116,6 @@
         ## Parameter details [units, min, max]
         self.details = {}
-        
+        ## Magnetic Panrameters
+        self.magnetic_params = []
         # non-fittable parameters
         self.non_fittable = p_model1.non_fittable  
@@ -1105,4 +1151,14 @@
             if not new_item in self.orientation_params:
                 self.orientation_params.append(new_item)
+        ## magnetic params
+        for item in self.p_model1.magnetic_params:
+            new_item = "p1_" + item
+            if not new_item in self.magnetic_params:
+                self.magnetic_params.append(new_item)
+            
+        for item in self.p_model2.magnetic_params:
+            new_item = "p2_" + item
+            if not new_item in self.magnetic_params:
+                self.magnetic_params.append(new_item)
         # get multiplicity if model provide it, else 1.
         try:
