Changeset b01ac8f in sasview for src/sas/sasgui/perspectives


Ignore:
Timestamp:
Sep 16, 2017 8:26:13 PM (7 years ago)
Author:
butler
Branches:
master, ESS_GUI, ESS_GUI_Docs, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_iss879, ESS_GUI_iss959, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc, costrafo411, magnetic_scatt, release-4.2.2, ticket-1009, ticket-1094-headless, ticket-1242-2d-resolution, ticket-1243, ticket-1249, ticket885, unittest-saveload
Children:
7b15990
Parents:
a6ee176 (diff), ca383a0 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge remote-tracking branch 'origin/master' into 969_about_box_logo

Location:
src/sas/sasgui/perspectives
Files:
3 added
15 edited

Legend:

Unmodified
Added
Removed
  • src/sas/sasgui/perspectives/calculator/model_editor.py

    ra1b8fee r23359ccb  
    106106        self.model2_string = "cylinder" 
    107107        self.name = 'Sum' + M_NAME 
    108         self.factor = 'scale_factor' 
    109108        self._notes = '' 
    110109        self._operator = '+' 
     
    133132        self.model2_name = str(self.model2.GetValue()) 
    134133        self.good_name = True 
    135         self.fill_oprator_combox() 
     134        self.fill_operator_combox() 
    136135 
    137136    def _layout_name(self): 
     
    491490        a sum or multiply model then create the appropriate string 
    492491        """ 
    493  
    494492        name = '' 
    495  
    496493        if operator == '*': 
    497494            name = 'Multi' 
    498             factor = 'BackGround' 
    499             f_oper = '+' 
     495            factor = 'background' 
    500496        else: 
    501497            name = 'Sum' 
    502498            factor = 'scale_factor' 
    503             f_oper = '*' 
    504  
    505         self.factor = factor 
     499 
    506500        self._operator = operator 
    507         self.explanation = "  Plugin Model = %s %s (model1 %s model2)\n" % \ 
    508                            (self.factor, f_oper, self._operator) 
     501        self.explanation = ("  Plugin_model = scale_factor * (model_1 {} " 
     502            "model_2) + background").format(operator) 
    509503        self.explanationctr.SetLabel(self.explanation) 
    510504        self.name = name + M_NAME 
    511505 
    512506 
    513     def fill_oprator_combox(self): 
     507    def fill_operator_combox(self): 
    514508        """ 
    515509        fill the current combobox with the operator 
     
    527521        return [self.model1_name, self.model2_name] 
    528522 
    529     def write_string(self, fname, name1, name2): 
     523    def write_string(self, fname, model1_name, model2_name): 
    530524        """ 
    531525        Write and Save file 
     
    533527        self.fname = fname 
    534528        description = self.desc_tcl.GetValue().lstrip().rstrip() 
    535         if description == '': 
    536             description = name1 + self._operator + name2 
    537         text = self._operator_choice.GetValue() 
    538         if text.count('+') > 0: 
    539             factor = 'scale_factor' 
    540             f_oper = '*' 
    541             default_val = '1.0' 
    542         else: 
    543             factor = 'BackGround' 
    544             f_oper = '+' 
    545             default_val = '0.0' 
    546         path = self.fname 
    547         try: 
    548             out_f = open(path, 'w') 
    549         except: 
    550             raise 
    551         lines = SUM_TEMPLATE.split('\n') 
    552         for line in lines: 
    553             try: 
    554                 if line.count("scale_factor"): 
    555                     line = line.replace('scale_factor', factor) 
    556                     #print "scale_factor", line 
    557                 if line.count("= %s"): 
    558                     out_f.write(line % (default_val) + "\n") 
    559                 elif line.count("import Model as P1"): 
    560                     if self.is_p1_custom: 
    561                         line = line.replace('#', '') 
    562                         out_f.write(line % name1 + "\n") 
    563                     else: 
    564                         out_f.write(line + "\n") 
    565                 elif line.count("import %s as P1"): 
    566                     if not self.is_p1_custom: 
    567                         line = line.replace('#', '') 
    568                         out_f.write(line % (name1) + "\n") 
    569                     else: 
    570                         out_f.write(line + "\n") 
    571                 elif line.count("import Model as P2"): 
    572                     if self.is_p2_custom: 
    573                         line = line.replace('#', '') 
    574                         out_f.write(line % name2 + "\n") 
    575                     else: 
    576                         out_f.write(line + "\n") 
    577                 elif line.count("import %s as P2"): 
    578                     if not self.is_p2_custom: 
    579                         line = line.replace('#', '') 
    580                         out_f.write(line % (name2) + "\n") 
    581                     else: 
    582                         out_f.write(line + "\n") 
    583                 elif line.count("P1 = find_model"): 
    584                     out_f.write(line % (name1) + "\n") 
    585                 elif line.count("P2 = find_model"): 
    586                     out_f.write(line % (name2) + "\n") 
    587  
    588                 elif line.count("self.description = '%s'"): 
    589                     out_f.write(line % description + "\n") 
    590                 #elif line.count("run") and line.count("%s"): 
    591                 #    out_f.write(line % self._operator + "\n") 
    592                 #elif line.count("evalDistribution") and line.count("%s"): 
    593                 #    out_f.write(line % self._operator + "\n") 
    594                 elif line.count("return") and line.count("%s") == 2: 
    595                     #print "line return", line 
    596                     out_f.write(line % (f_oper, self._operator) + "\n") 
    597                 elif line.count("out2")and line.count("%s"): 
    598                     out_f.write(line % self._operator + "\n") 
    599                 else: 
    600                     out_f.write(line + "\n") 
    601             except: 
    602                 raise 
    603         out_f.close() 
    604         #else: 
    605         #    msg = "Name exists already." 
     529        desc_line = '' 
     530        if description.strip() != '': 
     531            # Sasmodels generates a description for us. If the user provides 
     532            # their own description, add a line to overwrite the sasmodels one 
     533            desc_line = "\nmodel_info.description = '{}'".format(description) 
     534        name = os.path.splitext(os.path.basename(self.fname))[0] 
     535        output = SUM_TEMPLATE.format(name=name, model1=model1_name,  
     536            model2=model2_name, operator=self._operator, desc_line=desc_line) 
     537        with open(self.fname, 'w') as out_f: 
     538            out_f.write(output) 
    606539 
    607540    def compile_file(self, path): 
     
    643576        self.name_hsizer = None 
    644577        self.name_tcl = None 
     578        self.overwrite_cb = None 
    645579        self.desc_sizer = None 
    646580        self.desc_tcl = None 
     
    657591        self.warning = "" 
    658592        #This does not seem to be used anywhere so commenting out for now 
    659         #    -- PDB 2/26/17  
     593        #    -- PDB 2/26/17 
    660594        #self._description = "New Plugin Model" 
    661595        self.function_tcl = None 
     
    689623        #title name [string] 
    690624        name_txt = wx.StaticText(self, -1, 'Function Name : ') 
    691         overwrite_cb = wx.CheckBox(self, -1, "Overwrite existing plugin model of this name?", (10, 10)) 
    692         overwrite_cb.SetValue(False) 
    693         overwrite_cb.SetToolTipString("Overwrite it if already exists?") 
    694         wx.EVT_CHECKBOX(self, overwrite_cb.GetId(), self.on_over_cb) 
     625        self.overwrite_cb = wx.CheckBox(self, -1, "Overwrite existing plugin model of this name?", (10, 10)) 
     626        self.overwrite_cb.SetValue(False) 
     627        self.overwrite_cb.SetToolTipString("Overwrite it if already exists?") 
     628        wx.EVT_CHECKBOX(self, self.overwrite_cb.GetId(), self.on_over_cb) 
    695629        self.name_tcl = wx.TextCtrl(self, -1, size=(PANEL_WIDTH * 3 / 5, -1)) 
    696630        self.name_tcl.Bind(wx.EVT_TEXT_ENTER, self.on_change_name) 
     
    700634        self.name_tcl.SetToolTipString(hint_name) 
    701635        self.name_hsizer.AddMany([(self.name_tcl, 0, wx.LEFT | wx.TOP, 0), 
    702                                   (overwrite_cb, 0, wx.LEFT, 20)]) 
     636                                  (self.overwrite_cb, 0, wx.LEFT, 20)]) 
    703637        self.name_sizer.AddMany([(name_txt, 0, wx.LEFT | wx.TOP, 10), 
    704638                                 (self.name_hsizer, 0, 
     
    740674        self.param_sizer.AddMany([(param_txt, 0, wx.LEFT, 10), 
    741675                                  (self.param_tcl, 1, wx.EXPAND | wx.ALL, 10)]) 
    742          
     676 
    743677        # Parameters with polydispersity 
    744678        pd_param_txt = wx.StaticText(self, -1, 'Fit Parameters requiring ' + \ 
     
    755689        self.pd_param_tcl.setDisplayLineNumbers(True) 
    756690        self.pd_param_tcl.SetToolTipString(pd_param_tip) 
    757          
     691 
    758692        self.param_sizer.AddMany([(pd_param_txt, 0, wx.LEFT, 10), 
    759693                                  (self.pd_param_tcl, 1, wx.EXPAND | wx.ALL, 10)]) 
     
    995929            info = 'Error' 
    996930            color = 'red' 
     931            self.overwrite_cb.SetValue(True) 
     932            self.overwrite_name = True 
    997933        else: 
    998934            self._notes = result 
     
    1030966        if has_scipy: 
    1031967            lines.insert(0, 'import scipy') 
    1032          
    1033         # Think about 2D later         
     968 
     969        # Think about 2D later 
    1034970        #self.is_2d = func_str.count("#self.ndim = 2") 
    1035971        #line_2d = '' 
    1036972        #if self.is_2d: 
    1037973        #    line_2d = CUSTOM_2D_TEMP.split('\n') 
    1038          
    1039         # Also think about test later         
     974 
     975        # Also think about test later 
    1040976        #line_test = TEST_TEMPLATE.split('\n') 
    1041977        #local_params = '' 
     
    1043979        spaces4  = ' '*4 
    1044980        spaces13 = ' '*13 
    1045         spaces16 = ' '*16      
     981        spaces16 = ' '*16 
    1046982        param_names = []    # to store parameter names 
    1047983        has_scipy = func_str.count("scipy.") 
     
    1055991            out_f.write(line + '\n') 
    1056992            if line.count('#name'): 
    1057                 out_f.write('name = "%s" \n' % name)                
     993                out_f.write('name = "%s" \n' % name) 
    1058994            elif line.count('#title'): 
    1059                 out_f.write('title = "User model for %s"\n' % name)                
     995                out_f.write('title = "User model for %s"\n' % name) 
    1060996            elif line.count('#description'): 
    1061                 out_f.write('description = "%s"\n' % desc_str)                
     997                out_f.write('description = "%s"\n' % desc_str) 
    1062998            elif line.count('#parameters'): 
    1063999                out_f.write('parameters = [ \n') 
     
    10651001                    p_line = param_line.lstrip().rstrip() 
    10661002                    if p_line: 
    1067                         pname, pvalue = self.get_param_helper(p_line) 
     1003                        pname, pvalue, desc = self.get_param_helper(p_line) 
    10681004                        param_names.append(pname) 
    1069                         out_f.write("%s['%s', '', %s, [-numpy.inf, numpy.inf], '', ''],\n" % (spaces16, pname, pvalue)) 
     1005                        out_f.write("%s['%s', '', %s, [-numpy.inf, numpy.inf], '', '%s'],\n" % (spaces16, pname, pvalue, desc)) 
    10701006                for param_line in pd_param_str.split('\n'): 
    10711007                    p_line = param_line.lstrip().rstrip() 
    10721008                    if p_line: 
    1073                         pname, pvalue = self.get_param_helper(p_line) 
     1009                        pname, pvalue, desc = self.get_param_helper(p_line) 
    10741010                        param_names.append(pname) 
    1075                         out_f.write("%s['%s', '', %s, [-numpy.inf, numpy.inf], 'volume', ''],\n" % (spaces16, pname, pvalue)) 
     1011                        out_f.write("%s['%s', '', %s, [-numpy.inf, numpy.inf], 'volume', '%s'],\n" % (spaces16, pname, pvalue, desc)) 
    10761012                out_f.write('%s]\n' % spaces13) 
    1077              
     1013 
    10781014        # No form_volume or ER available in simple model editor 
    10791015        out_f.write('def form_volume(*arg): \n') 
     
    10821018        out_f.write('def ER(*arg): \n') 
    10831019        out_f.write('    return 1.0 \n') 
    1084          
     1020 
    10851021        # function to compute 
    10861022        out_f.write('\n') 
     
    10911027        for func_line in func_str.split('\n'): 
    10921028            out_f.write('%s%s\n' % (spaces4, func_line)) 
    1093          
     1029 
    10941030        Iqxy_string = 'return Iq(numpy.sqrt(x**2+y**2) ' 
    1095              
     1031 
    10961032        out_f.write('\n') 
    10971033        out_f.write('def Iqxy(x, y ') 
     
    11131049        items = line.split(";") 
    11141050        for item in items: 
    1115             name = item.split("=")[0].lstrip().rstrip() 
     1051            name = item.split("=")[0].strip() 
     1052            description = "" 
    11161053            try: 
    1117                 value = item.split("=")[1].lstrip().rstrip() 
     1054                value = item.split("=")[1].strip() 
     1055                if value.count("#"): 
     1056                    # If line ends in a comment, remove it before parsing float 
     1057                    index = value.index("#") 
     1058                    description = value[(index + 1):].strip() 
     1059                    value = value[:value.index("#")].strip() 
    11181060                float(value) 
    1119             except: 
     1061            except ValueError: 
    11201062                value = 1.0 # default 
    11211063 
    1122         return name, value 
     1064        return name, value, description 
    11231065 
    11241066    def set_function_helper(self, line): 
     
    12041146import numpy 
    12051147 
    1206 #name  
     1148#name 
    12071149 
    12081150#title 
     
    12101152#description 
    12111153 
    1212 #parameters  
     1154#parameters 
    12131155 
    12141156""" 
     
    12691211""" 
    12701212SUM_TEMPLATE = """ 
    1271 # A sample of an experimental model function for Sum/Multiply(Pmodel1,Pmodel2) 
    1272 import os 
    1273 import sys 
    1274 import copy 
    1275 import collections 
    1276  
    1277 import numpy 
    1278  
    1279 from sas.sascalc.fit.pluginmodel import Model1DPlugin 
    1280 from sasmodels.sasview_model import find_model 
    1281  
    1282 class Model(Model1DPlugin): 
    1283     name = os.path.splitext(os.path.basename(__file__))[0] 
    1284     is_multiplicity_model = False 
    1285     def __init__(self, multiplicity=1): 
    1286         Model1DPlugin.__init__(self, name='') 
    1287         P1 = find_model('%s') 
    1288         P2 = find_model('%s') 
    1289         p_model1 = P1() 
    1290         p_model2 = P2() 
    1291         ## Setting  model name model description 
    1292         self.description = '%s' 
    1293         if self.name.rstrip().lstrip() == '': 
    1294             self.name = self._get_name(p_model1.name, p_model2.name) 
    1295         if self.description.rstrip().lstrip() == '': 
    1296             self.description = p_model1.name 
    1297             self.description += p_model2.name 
    1298             self.fill_description(p_model1, p_model2) 
    1299  
    1300         ## Define parameters 
    1301         self.params = collections.OrderedDict() 
    1302  
    1303         ## Parameter details [units, min, max] 
    1304         self.details = {} 
    1305         ## Magnetic Panrameters 
    1306         self.magnetic_params = [] 
    1307         # non-fittable parameters 
    1308         self.non_fittable = p_model1.non_fittable 
    1309         self.non_fittable += p_model2.non_fittable 
    1310  
    1311         ##models 
    1312         self.p_model1= p_model1 
    1313         self.p_model2= p_model2 
    1314  
    1315  
    1316         ## dispersion 
    1317         self._set_dispersion() 
    1318         ## Define parameters 
    1319         self._set_params() 
    1320         ## New parameter:scaling_factor 
    1321         self.params['scale_factor'] = %s 
    1322  
    1323         ## Parameter details [units, min, max] 
    1324         self._set_details() 
    1325         self.details['scale_factor'] = ['', 0.0, numpy.inf] 
    1326  
    1327  
    1328         #list of parameter that can be fitted 
    1329         self._set_fixed_params() 
    1330  
    1331         ## parameters with orientation 
    1332         self.orientation_params = [] 
    1333         for item in self.p_model1.orientation_params: 
    1334             new_item = "p1_" + item 
    1335             if not new_item in self.orientation_params: 
    1336                 self.orientation_params.append(new_item) 
    1337  
    1338         for item in self.p_model2.orientation_params: 
    1339             new_item = "p2_" + item 
    1340             if not new_item in self.orientation_params: 
    1341                 self.orientation_params.append(new_item) 
    1342         ## magnetic params 
    1343         self.magnetic_params = [] 
    1344         for item in self.p_model1.magnetic_params: 
    1345             new_item = "p1_" + item 
    1346             if not new_item in self.magnetic_params: 
    1347                 self.magnetic_params.append(new_item) 
    1348  
    1349         for item in self.p_model2.magnetic_params: 
    1350             new_item = "p2_" + item 
    1351             if not new_item in self.magnetic_params: 
    1352                 self.magnetic_params.append(new_item) 
    1353         # get multiplicity if model provide it, else 1. 
    1354         try: 
    1355             multiplicity1 = p_model1.multiplicity 
    1356             try: 
    1357                 multiplicity2 = p_model2.multiplicity 
    1358             except: 
    1359                 multiplicity2 = 1 
    1360         except: 
    1361             multiplicity1 = 1 
    1362             multiplicity2 = 1 
    1363         ## functional multiplicity of the model 
    1364         self.multiplicity1 = multiplicity1 
    1365         self.multiplicity2 = multiplicity2 
    1366         self.multiplicity_info = [] 
    1367  
    1368     def _clone(self, obj): 
    1369         import copy 
    1370         obj.params     = copy.deepcopy(self.params) 
    1371         obj.description     = copy.deepcopy(self.description) 
    1372         obj.details    = copy.deepcopy(self.details) 
    1373         obj.dispersion = copy.deepcopy(self.dispersion) 
    1374         obj.p_model1  = self.p_model1.clone() 
    1375         obj.p_model2  = self.p_model2.clone() 
    1376         #obj = copy.deepcopy(self) 
    1377         return obj 
    1378  
    1379     def _get_name(self, name1, name2): 
    1380         p1_name = self._get_upper_name(name1) 
    1381         if not p1_name: 
    1382             p1_name = name1 
    1383         name = p1_name 
    1384         name += "_and_" 
    1385         p2_name = self._get_upper_name(name2) 
    1386         if not p2_name: 
    1387             p2_name = name2 
    1388         name += p2_name 
    1389         return name 
    1390  
    1391     def _get_upper_name(self, name=None): 
    1392         if name is None: 
    1393             return "" 
    1394         upper_name = "" 
    1395         str_name = str(name) 
    1396         for index in range(len(str_name)): 
    1397             if str_name[index].isupper(): 
    1398                 upper_name += str_name[index] 
    1399         return upper_name 
    1400  
    1401     def _set_dispersion(self): 
    1402         self.dispersion = collections.OrderedDict() 
    1403         ##set dispersion only from p_model 
    1404         for name , value in self.p_model1.dispersion.iteritems(): 
    1405             #if name.lower() not in self.p_model1.orientation_params: 
    1406             new_name = "p1_" + name 
    1407             self.dispersion[new_name]= value 
    1408         for name , value in self.p_model2.dispersion.iteritems(): 
    1409             #if name.lower() not in self.p_model2.orientation_params: 
    1410             new_name = "p2_" + name 
    1411             self.dispersion[new_name]= value 
    1412  
    1413     def function(self, x=0.0): 
    1414         return 0 
    1415  
    1416     def getProfile(self): 
    1417         try: 
    1418             x,y = self.p_model1.getProfile() 
    1419         except: 
    1420             x = None 
    1421             y = None 
    1422  
    1423         return x, y 
    1424  
    1425     def _set_params(self): 
    1426         for name , value in self.p_model1.params.iteritems(): 
    1427             # No 2D-supported 
    1428             #if name not in self.p_model1.orientation_params: 
    1429             new_name = "p1_" + name 
    1430             self.params[new_name]= value 
    1431  
    1432         for name , value in self.p_model2.params.iteritems(): 
    1433             # No 2D-supported 
    1434             #if name not in self.p_model2.orientation_params: 
    1435             new_name = "p2_" + name 
    1436             self.params[new_name]= value 
    1437  
    1438         # Set "scale" as initializing 
    1439         self._set_scale_factor() 
    1440  
    1441  
    1442     def _set_details(self): 
    1443         for name ,detail in self.p_model1.details.iteritems(): 
    1444             new_name = "p1_" + name 
    1445             #if new_name not in self.orientation_params: 
    1446             self.details[new_name]= detail 
    1447  
    1448         for name ,detail in self.p_model2.details.iteritems(): 
    1449             new_name = "p2_" + name 
    1450             #if new_name not in self.orientation_params: 
    1451             self.details[new_name]= detail 
    1452  
    1453     def _set_scale_factor(self): 
    1454         pass 
    1455  
    1456  
    1457     def setParam(self, name, value): 
    1458         # set param to this (p1, p2) model 
    1459         self._setParamHelper(name, value) 
    1460  
    1461         ## setParam to p model 
    1462         model_pre = '' 
    1463         new_name = '' 
    1464         name_split = name.split('_', 1) 
    1465         if len(name_split) == 2: 
    1466             model_pre = name.split('_', 1)[0] 
    1467             new_name = name.split('_', 1)[1] 
    1468         if model_pre == "p1": 
    1469             if new_name in self.p_model1.getParamList(): 
    1470                 self.p_model1.setParam(new_name, value) 
    1471         elif model_pre == "p2": 
    1472              if new_name in self.p_model2.getParamList(): 
    1473                 self.p_model2.setParam(new_name, value) 
    1474         elif name == 'scale_factor': 
    1475             self.params['scale_factor'] = value 
    1476         else: 
    1477             raise ValueError, "Model does not contain parameter %s" % name 
    1478  
    1479     def getParam(self, name): 
    1480         # Look for dispersion parameters 
    1481         toks = name.split('.') 
    1482         if len(toks)==2: 
    1483             for item in self.dispersion.keys(): 
    1484                 # 2D not supported 
    1485                 if item.lower()==toks[0].lower(): 
    1486                     for par in self.dispersion[item]: 
    1487                         if par.lower() == toks[1].lower(): 
    1488                             return self.dispersion[item][par] 
    1489         else: 
    1490             # Look for standard parameter 
    1491             for item in self.params.keys(): 
    1492                 if item.lower()==name.lower(): 
    1493                     return self.params[item] 
    1494         return 
    1495         #raise ValueError, "Model does not contain parameter %s" % name 
    1496  
    1497     def _setParamHelper(self, name, value): 
    1498         # Look for dispersion parameters 
    1499         toks = name.split('.') 
    1500         if len(toks)== 2: 
    1501             for item in self.dispersion.keys(): 
    1502                 if item.lower()== toks[0].lower(): 
    1503                     for par in self.dispersion[item]: 
    1504                         if par.lower() == toks[1].lower(): 
    1505                             self.dispersion[item][par] = value 
    1506                             return 
    1507         else: 
    1508             # Look for standard parameter 
    1509             for item in self.params.keys(): 
    1510                 if item.lower()== name.lower(): 
    1511                     self.params[item] = value 
    1512                     return 
    1513  
    1514         raise ValueError, "Model does not contain parameter %s" % name 
    1515  
    1516  
    1517     def _set_fixed_params(self): 
    1518         self.fixed = [] 
    1519         for item in self.p_model1.fixed: 
    1520             new_item = "p1" + item 
    1521             self.fixed.append(new_item) 
    1522         for item in self.p_model2.fixed: 
    1523             new_item = "p2" + item 
    1524             self.fixed.append(new_item) 
    1525  
    1526         self.fixed.sort() 
    1527  
    1528  
    1529     def run(self, x = 0.0): 
    1530         self._set_scale_factor() 
    1531         return self.params['scale_factor'] %s \ 
    1532 (self.p_model1.run(x) %s self.p_model2.run(x)) 
    1533  
    1534     def runXY(self, x = 0.0): 
    1535         self._set_scale_factor() 
    1536         return self.params['scale_factor'] %s \ 
    1537 (self.p_model1.runXY(x) %s self.p_model2.runXY(x)) 
    1538  
    1539     ## Now (May27,10) directly uses the model eval function 
    1540     ## instead of the for-loop in Base Component. 
    1541     def evalDistribution(self, x = []): 
    1542         self._set_scale_factor() 
    1543         return self.params['scale_factor'] %s \ 
    1544 (self.p_model1.evalDistribution(x) %s \ 
    1545 self.p_model2.evalDistribution(x)) 
    1546  
    1547     def set_dispersion(self, parameter, dispersion): 
    1548         value= None 
    1549         new_pre = parameter.split("_", 1)[0] 
    1550         new_parameter = parameter.split("_", 1)[1] 
    1551         try: 
    1552             if new_pre == 'p1' and \ 
    1553 new_parameter in self.p_model1.dispersion.keys(): 
    1554                 value= self.p_model1.set_dispersion(new_parameter, dispersion) 
    1555             if new_pre == 'p2' and \ 
    1556 new_parameter in self.p_model2.dispersion.keys(): 
    1557                 value= self.p_model2.set_dispersion(new_parameter, dispersion) 
    1558             self._set_dispersion() 
    1559             return value 
    1560         except: 
    1561             raise 
    1562  
    1563     def fill_description(self, p_model1, p_model2): 
    1564         description = "" 
    1565         description += "This model gives the summation or multiplication of" 
    1566         description += "%s and %s. "% ( p_model1.name, p_model2.name ) 
    1567         self.description += description 
    1568  
    1569 if __name__ == "__main__": 
    1570     m1= Model() 
    1571     #m1.setParam("p1_scale", 25) 
    1572     #m1.setParam("p1_length", 1000) 
    1573     #m1.setParam("p2_scale", 100) 
    1574     #m1.setParam("p2_rg", 100) 
    1575     out1 = m1.runXY(0.01) 
    1576  
    1577     m2= Model() 
    1578     #m2.p_model1.setParam("scale", 25) 
    1579     #m2.p_model1.setParam("length", 1000) 
    1580     #m2.p_model2.setParam("scale", 100) 
    1581     #m2.p_model2.setParam("rg", 100) 
    1582     out2 = m2.p_model1.runXY(0.01) %s m2.p_model2.runXY(0.01)\n 
    1583     print "My name is %s."% m1.name 
    1584     print out1, " = ", out2 
    1585     if out1 == out2: 
    1586         print "===> Simple Test: Passed!" 
    1587     else: 
    1588         print "===> Simple Test: Failed!" 
     1213from sasmodels.core import load_model_info 
     1214from sasmodels.sasview_model import make_model_from_info 
     1215 
     1216model_info = load_model_info('{model1}{operator}{model2}') 
     1217model_info.name = '{name}'{desc_line} 
     1218Model = make_model_from_info(model_info) 
    15891219""" 
    1590  
    15911220if __name__ == "__main__": 
    15921221#    app = wx.PySimpleApp() 
  • src/sas/sasgui/perspectives/calculator/pyconsole.py

    r7432acb r4627657  
    3737    Iqxy = model.evalDistribution([qx, qy]) 
    3838 
    39     result = """ 
    40     Iq(%s) = %s 
    41     Iqxy(%s, %s) = %s 
    42     """%(q, Iq, qx, qy, Iqxy) 
     39    # check the model's unit tests run 
     40    from sasmodels.model_test import run_one 
     41    result = run_one(path) 
    4342 
    4443    return result 
     
    8988        ok = wx.Button(self, wx.ID_OK, "OK") 
    9089 
    91         # Mysterious constraint layouts from  
     90        # Mysterious constraint layouts from 
    9291        # https://www.wxpython.org/docs/api/wx.lib.layoutf.Layoutf-class.html 
    9392        lc = layoutf.Layoutf('t=t5#1;b=t5#2;l=l5#1;r=r5#1', (self,ok)) 
  • src/sas/sasgui/perspectives/file_converter/converter_panel.py

    red9f872 r19296dc  
    2424from sas.sascalc.file_converter.otoko_loader import OTOKOLoader 
    2525from sas.sascalc.file_converter.bsl_loader import BSLLoader 
     26from sas.sascalc.file_converter.ascii2d_loader import ASCII2DLoader 
    2627from sas.sascalc.file_converter.nxcansas_writer import NXcanSASWriter 
    2728from sas.sascalc.dataloader.data_info import Detector 
     
    3536    _STATICBOX_WIDTH = 410 
    3637    _BOX_WIDTH = 200 
    37     PANEL_SIZE = 480 
     38    PANEL_SIZE = 520 
    3839    FONT_VARIANT = 0 
    3940else: 
     
    4142    _STATICBOX_WIDTH = 430 
    4243    _BOX_WIDTH = 200 
    43     PANEL_SIZE = 500 
     44    PANEL_SIZE = 540 
    4445    FONT_VARIANT = 1 
    4546 
     
    352353            w.write(frame_data, output_path) 
    353354 
     355    def convert_2d_data(self, dataset): 
     356        metadata = self.get_metadata() 
     357        for key, value in metadata.iteritems(): 
     358            setattr(dataset[0], key, value) 
     359 
     360        w = NXcanSASWriter() 
     361        w.write(dataset, self.output.GetPath()) 
     362 
    354363    def on_convert(self, event): 
    355364        """Called when the Convert button is clicked""" 
     
    367376                qdata, iqdata = self.extract_otoko_data(self.q_input.GetPath()) 
    368377                self.convert_1d_data(qdata, iqdata) 
     378            elif self.data_type == 'ascii2d': 
     379                loader = ASCII2DLoader(self.iq_input.GetPath()) 
     380                data = loader.load() 
     381                dataset = [data] # ASCII 2D only ever contains 1 frame 
     382                self.convert_2d_data(dataset) 
    369383            else: # self.data_type == 'bsl' 
    370384                dataset = self.extract_bsl_data(self.iq_input.GetPath()) 
     
    372386                    # Cancelled by user 
    373387                    return 
    374  
    375                 metadata = self.get_metadata() 
    376                 for key, value in metadata.iteritems(): 
    377                     setattr(dataset[0], key, value) 
    378  
    379                 w = NXcanSASWriter() 
    380                 w.write(dataset, self.output.GetPath()) 
     388                self.convert_2d_data(dataset) 
     389 
    381390        except Exception as ex: 
    382391            msg = str(ex) 
     
    399408    def validate_inputs(self): 
    400409        msg = "You must select a" 
    401         if self.q_input.GetPath() == '' and self.data_type != 'bsl': 
     410        if self.q_input.GetPath() == '' and self.data_type != 'bsl' \ 
     411            and self.data_type != 'ascii2d': 
    402412            msg += " Q Axis input file." 
    403413        elif self.iq_input.GetPath() == '': 
     
    472482        dtype = event.GetEventObject().GetName() 
    473483        self.data_type = dtype 
    474         if dtype == 'bsl': 
     484        if dtype == 'bsl' or dtype == 'ascii2d': 
    475485            self.q_input.SetPath("") 
    476486            self.q_input.Disable() 
     
    500510 
    501511        instructions = ( 
    502         "Select linked single column 1D ASCII files containing the Q-axis and " 
    503         "Intensity-axis data, or 1D BSL/OTOKO files, or a 2D BSL/OTOKO file, " 
    504         "then choose where to save the converted file, and click Convert.\n" 
    505         "1D ASCII and BSL/OTOKO files can be converted to CanSAS (XML) or " 
    506         "NXcanSAS (HDF5) formats. 2D BSL/OTOKO files can only be converted to " 
    507         "the NXcanSAS format.\n" 
    508         "Metadata can be optionally added for the CanSAS XML format." 
     512        "If converting a 1D dataset, select linked single-column ASCII files " 
     513        "containing the Q-axis and intensity-axis data, or a 1D BSL/OTOKO file." 
     514        " If converting 2D data, select an ASCII file in the ISIS 2D file " 
     515        "format, or a 2D BSL/OTOKO file. Choose where to save the converted " 
     516        "file and click convert.\n" 
     517        "One dimensional ASCII and BSL/OTOKO files can be converted to CanSAS " 
     518        "(XML) or NXcanSAS (HDF5) formats. Two dimensional datasets can only be" 
     519        " converted to the NXcanSAS format.\n" 
     520        "Metadata can also be optionally added to the output file." 
    509521        ) 
    510522 
     
    526538            wx.ALIGN_CENTER_VERTICAL, 5) 
    527539        radio_sizer = wx.BoxSizer(wx.HORIZONTAL) 
    528         ascii_btn = wx.RadioButton(self, -1, "ASCII", name="ascii", 
     540        ascii_btn = wx.RadioButton(self, -1, "ASCII 1D", name="ascii", 
    529541            style=wx.RB_GROUP) 
    530542        ascii_btn.Bind(wx.EVT_RADIOBUTTON, self.datatype_changed) 
    531543        radio_sizer.Add(ascii_btn) 
     544        ascii2d_btn = wx.RadioButton(self, -1, "ASCII 2D", name="ascii2d") 
     545        ascii2d_btn.Bind(wx.EVT_RADIOBUTTON, self.datatype_changed) 
     546        radio_sizer.Add(ascii2d_btn) 
    532547        otoko_btn = wx.RadioButton(self, -1, "BSL 1D", name="otoko") 
    533548        otoko_btn.Bind(wx.EVT_RADIOBUTTON, self.datatype_changed) 
    534549        radio_sizer.Add(otoko_btn) 
    535         input_grid.Add(radio_sizer, (y,1), (1,1), wx.ALL, 5) 
    536550        bsl_btn = wx.RadioButton(self, -1, "BSL 2D", name="bsl") 
    537551        bsl_btn.Bind(wx.EVT_RADIOBUTTON, self.datatype_changed) 
    538552        radio_sizer.Add(bsl_btn) 
     553        input_grid.Add(radio_sizer, (y,1), (1,1), wx.ALL, 5) 
    539554        y += 1 
    540555 
     
    549564        y += 1 
    550565 
    551         iq_label = wx.StaticText(self, -1, "Intensity-Axis Data: ") 
     566        iq_label = wx.StaticText(self, -1, "Intensity Data: ") 
    552567        input_grid.Add(iq_label, (y,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5) 
    553568 
     
    647662 
    648663    def __init__(self, parent=None, title='File Converter', base=None, 
    649         manager=None, size=(PANEL_SIZE * 1.05, PANEL_SIZE / 1.1), 
     664        manager=None, size=(PANEL_SIZE * 0.96, PANEL_SIZE * 0.9), 
    650665        *args, **kwargs): 
    651666        kwargs['title'] = title 
  • src/sas/sasgui/perspectives/file_converter/file_converter.py

    r463e7ffc r94e3572  
    2525        Returns a set of menu entries 
    2626        """ 
    27         help_txt = "Convert single column ASCII data to CanSAS format" 
     27        help_txt = "Convert ASCII or BSL/OTOKO data to CanSAS or NXcanSAS formats" 
    2828        return [("File Converter", help_txt, self.on_file_converter)] 
    2929 
  • src/sas/sasgui/perspectives/file_converter/media/file_converter_help.rst

    rd73998c r59decb81  
    1818*   Single-column ASCII data, with lines that end without any delimiter, 
    1919    or with a comma or semi-colon delimiter 
     20*   2D `ISIS ASCII formatted 
     21    <http://www.isis.stfc.ac.uk/instruments/loq/software/ 
     22    colette-ascii-file-format-descriptions9808.pdf>`_ data 
    2023*   `1D BSL/OTOKO format 
    2124    <http://www.diamond.ac.uk/Beamlines/Soft-Condensed-Matter/small-angle/ 
     
    3639 
    37401) Select the files containing your Q-axis and Intensity-axis data 
    38 2) Choose whether the files are in ASCII, 1D BSL/OTOKO or 2D BSL/OTOKO format 
     412) Choose whether the files are in ASCII 1D, ASCII 2D, 1D BSL/OTOKO or 2D BSL/OTOKO format 
    39423) Choose where you would like to save the converted file 
    40434) Optionally, input some metadata such as sample size, detector name, etc 
     
    4750file, a dialog will appear asking which frames you would like converted. You 
    4851may enter a start frame, end frame & increment, and all frames in that subset 
    49 will be converted. For example, entering 0, 50 and 10 will convert frames 0,  
     52will be converted. For example, entering 0, 50 and 10 will convert frames 0, 
    505310, 20, 30, 40 & 50. 
    5154 
     
    5659single file, so there is an option in the *Select Frame* dialog to output each 
    5760frame to its own file. The single file option will produce one file with 
    58 multiple `<SASdata>` elements. The multiple file option will output a separate  
    59 file with one `<SASdata>` element for each frame. The frame number will also be  
     61multiple `<SASdata>` elements. The multiple file option will output a separate 
     62file with one `<SASdata>` element for each frame. The frame number will also be 
    6063appended to the file name. 
    6164 
    62 The multiple file option is not available when exporting to NXcanSAS because  
     65The multiple file option is not available when exporting to NXcanSAS because 
    6366the HDF5 format is more efficient at handling large amounts of data. 
    6467 
  • src/sas/sasgui/perspectives/fitting/fitting.py

    ra534432 r2d9526d  
    257257        toks = os.path.splitext(label) 
    258258        path = os.path.join(models.find_plugins_dir(), toks[0]) 
     259        message = "Are you sure you want to delete the file {}?".format(path) 
     260        dlg = wx.MessageDialog(self.frame, message, '', wx.YES_NO | wx.ICON_QUESTION) 
     261        if not dlg.ShowModal() == wx.ID_YES: 
     262            return 
    259263        try: 
    260264            for ext in ['.py', '.pyc']: 
    261265                p_path = path + ext 
     266                if ext == '.pyc' and not os.path.isfile(path + ext): 
     267                    # If model is invalid, .pyc file may not exist as model has 
     268                    # never been compiled. Don't try and delete it 
     269                    continue 
    262270                os.remove(p_path) 
    263271            self.update_custom_combo() 
     
    361369                                   'Add a new model function') 
    362370        wx.EVT_MENU(owner, wx_id, self.make_new_model) 
    363          
     371 
    364372        wx_id = wx.NewId() 
    365373        self.edit_model_menu.Append(wx_id, 'Sum|Multi(p1, p2)', 
     
    383391          '(Re)Load all models present in user plugin_models folder') 
    384392        wx.EVT_MENU(owner, wx_id, self.load_plugin_models) 
    385                  
     393 
    386394    def set_edit_menu_helper(self, owner=None, menu=None): 
    387395        """ 
     
    17341742            @param unsmeared_error: data error, rescaled to unsmeared model 
    17351743        """ 
    1736              
    1737         number_finite = np.count_nonzero(np.isfinite(y))  
     1744        number_finite = np.count_nonzero(np.isfinite(y)) 
    17381745        np.nan_to_num(y) 
    17391746        new_plot = self.create_theory_1D(x, y, page_id, model, data, state, 
    17401747                                         data_description=model.name, 
    17411748                                         data_id=str(page_id) + " " + data.name) 
     1749        plots_to_update = [] # List of plottables that have changed since last calculation 
     1750        # Create the new theories 
    17421751        if unsmeared_model is not None: 
    1743             self.create_theory_1D(x, unsmeared_model, page_id, model, data, state, 
     1752            unsmeared_model_plot = self.create_theory_1D(x, unsmeared_model,  
     1753                                  page_id, model, data, state, 
    17441754                                  data_description=model.name + " unsmeared", 
    17451755                                  data_id=str(page_id) + " " + data.name + " unsmeared") 
     1756            plots_to_update.append(unsmeared_model_plot) 
    17461757 
    17471758            if unsmeared_data is not None and unsmeared_error is not None: 
    1748                 self.create_theory_1D(x, unsmeared_data, page_id, model, data, state, 
     1759                unsmeared_data_plot = self.create_theory_1D(x, unsmeared_data,  
     1760                                      page_id, model, data, state, 
    17491761                                      data_description="Data unsmeared", 
    17501762                                      data_id="Data  " + data.name + " unsmeared", 
    17511763                                      dy=unsmeared_error) 
    1752         # Comment this out until we can get P*S models with correctly populated parameters 
    1753         #if sq_model is not None and pq_model is not None: 
    1754         #    self.create_theory_1D(x, sq_model, page_id, model, data, state, 
    1755         #                          data_description=model.name + " S(q)", 
    1756         #                          data_id=str(page_id) + " " + data.name + " S(q)") 
    1757         #    self.create_theory_1D(x, pq_model, page_id, model, data, state, 
    1758         #                          data_description=model.name + " P(q)", 
    1759         #                          data_id=str(page_id) + " " + data.name + " P(q)") 
     1764                plots_to_update.append(unsmeared_data_plot) 
     1765        if sq_model is not None and pq_model is not None: 
     1766            sq_id = str(page_id) + " " + data.name + " S(q)" 
     1767            sq_plot = self.create_theory_1D(x, sq_model, page_id, model, data, state, 
     1768                                  data_description=model.name + " S(q)", 
     1769                                  data_id=sq_id) 
     1770            plots_to_update.append(sq_plot) 
     1771            pq_id = str(page_id) + " " + data.name + " P(q)" 
     1772            pq_plot = self.create_theory_1D(x, pq_model, page_id, model, data, state, 
     1773                                  data_description=model.name + " P(q)", 
     1774                                  data_id=pq_id) 
     1775            plots_to_update.append(pq_plot) 
     1776        # Update the P(Q), S(Q) and unsmeared theory plots if they exist 
     1777        wx.PostEvent(self.parent, NewPlotEvent(plots=plots_to_update,  
     1778                                              action='update')) 
    17601779 
    17611780        current_pg = self.fit_panel.get_page_by_id(page_id) 
     
    17941813            msg = "Computing Error: Model did not return any finite value." 
    17951814            wx.PostEvent(self.parent, StatusEvent(status = msg, info="error")) 
    1796         else:                  
     1815        else: 
    17971816            msg = "Computation  completed!" 
    17981817            if number_finite != y.size: 
     
    18241843        that can be plot. 
    18251844        """ 
    1826         number_finite = np.count_nonzero(np.isfinite(image))  
     1845        number_finite = np.count_nonzero(np.isfinite(image)) 
    18271846        np.nan_to_num(image) 
    18281847        new_plot = Data2D(image=image, err_image=data.err_data) 
     
    19271946                ## and may be the cause of other noted instabilities 
    19281947                ## 
    1929                 ##    -PDB August 12, 2014  
     1948                ##    -PDB August 12, 2014 
    19301949                while self.calc_2D.isrunning(): 
    19311950                    time.sleep(0.1) 
     
    19691988            if (self.calc_1D is not None) and self.calc_1D.isrunning(): 
    19701989                self.calc_1D.stop() 
    1971                 ## stop just raises the flag -- the thread is supposed to  
     1990                ## stop just raises the flag -- the thread is supposed to 
    19721991                ## then kill itself but cannot.  Paul Kienzle came up with 
    19731992                ## this fix to prevent threads from stepping on each other 
     
    19812000                ## a request to stop the computation. 
    19822001                ## It seems thus that the whole thread approach used here 
    1983                 ## May need rethinking   
     2002                ## May need rethinking 
    19842003                ## 
    19852004                ##    -PDB August 12, 2014 
     
    21462165            residuals.dxw = None 
    21472166            residuals.ytransform = 'y' 
    2148             # For latter scale changes  
     2167            # For latter scale changes 
    21492168            residuals.xaxis('\\rm{Q} ', 'A^{-1}') 
    21502169            residuals.yaxis('\\rm{Residuals} ', 'normalized') 
  • src/sas/sasgui/perspectives/fitting/media/fitting_help.rst

    r5295cf5 rca383a0  
    195195the :ref:`Advanced_Plugin_Editor` . 
    196196 
     197**SasView version 4.2** made it possible to specify whether a plugin created with  
     198the *New Plugin Model* dialog is actually a form factor P(Q) or a structure factor  
     199S(Q). To do this, simply add one or other of the following lines under the *import*  
     200statements. 
     201 
     202For a form factor:: 
     203 
     204     form_factor = True 
     205          
     206or for a structure factor:: 
     207 
     208     structure_factor = True 
     209          
     210If the plugin is a structure factor it is *also* necessary to add two variables to  
     211the parameter list:: 
     212 
     213     parameters = [  
     214                     ['radius_effective', '', 1, [0.0, numpy.inf], 'volume', ''], 
     215                     ['volfraction', '', 1, [0.0, 1.0], '', ''], 
     216                     [...], 
     217 
     218and to the declarations of the functions Iq and Iqxy::: 
     219 
     220     def Iq(x , radius_effective, volfraction, ...): 
     221 
     222     def Iqxy(x, y, radius_effective, volfraction, ...): 
     223 
     224Such a plugin should then be available in the S(Q) drop-down box on a FitPage (once  
     225a P(Q) model has been selected). 
     226 
    197227Sum|Multi(p1,p2) 
    198228^^^^^^^^^^^^^^^^ 
     
    206236or:: 
    207237 
    208      Plugin Model = scale_factor * model_1 /* model_2 + background 
     238     Plugin Model = scale_factor * (model1 * model2) + background 
    209239 
    210240In the *Easy Sum/Multi Editor* give the new model a function name and brief 
    211241description (to appear under the *Details* button on the *FitPage*). Then select 
    212242two existing models, as p1 and p2, and the required operator, '+' or '*' between 
    213 them. Finally, click the *Apply* button to generate the model and then click *Close*. 
    214  
    215 Any changes to a plugin model generated in this way only become effective *after* it is re-selected from the model drop-down menu on the FitPage. 
     243them. Finally, click the *Apply* button to generate and test the model and then click *Close*. 
     244 
     245Any changes to a plugin model generated in this way only become effective *after* it is re-selected  
     246from the plugin models drop-down menu on the FitPage. If the model is not listed you can force a  
     247recompilation of the plugins by selecting *Fitting* > *Plugin Model Operations* > *Load Plugin Models*. 
     248 
     249**SasView version 4.2** introduced a much simplified and more extensible structure for plugin models  
     250generated through the Easy Sum/Multi Editor. For example, the code for a combination of a sphere model  
     251with a power law model now looks like this:: 
     252 
     253     from sasmodels.core import load_model_info 
     254     from sasmodels.sasview_model import make_model_from_info 
     255      
     256     model_info = load_model_info('sphere+power_law') 
     257     model_info.name = 'MyPluginModel' 
     258     model_info.description = 'sphere + power_law' 
     259     Model = make_model_from_info(model_info) 
     260 
     261To change the models or operators contributing to this plugin it is only necessary to edit the string  
     262in the brackets after *load_model_info*, though it would also be a good idea to update the model name  
     263and description too!!! 
     264 
     265The model specification string can handle multiple models and combinations of operators (+ or *) which  
     266are processed according to normal conventions. Thus 'model1+model2*model3' would be valid and would  
     267multiply model2 by model3 before adding model1. In this example, parameters in the *FitPage* would be  
     268prefixed A (for model2), B (for model3) and C (for model1). Whilst this might appear a little  
     269confusing, unless you were creating a plugin model from multiple instances of the same model the parameter  
     270assignments ought to be obvious when you load the plugin. 
     271 
     272If you need to include another plugin model in the model specification string, just prefix the name of  
     273that model with *custom*. For instance:: 
     274 
     275     sphere+custom.MyPluginModel 
     276 
     277To create a P(Q)*\S(Q) model use the @ symbol instead of * like this:: 
     278 
     279     sphere@hardsphere 
     280      
     281This streamlined approach to building complex plugin models from existing library models, or models  
     282available on the *Model Marketplace*, also permits the creation of P(Q)*\S(Q) plugin models, something  
     283that was not possible in earlier versions of SasView.  
    216284 
    217285.. _Advanced_Plugin_Editor: 
     
    484552.. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 
    485553 
     554.. _Batch_Fit_Mode: 
     555 
    486556Batch Fit Mode 
    487557-------------- 
     
    636706 
    637707     Example: radius [2 : 5] , radius [10 : 25] 
    638  
    639 .. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 
    640  
    641 .. note::  This help document was last changed by Steve King, 10Oct2016 
     708      
     709.. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 
     710 
     711Combined Batch Fit Mode 
     712----------------------- 
     713 
     714The purpose of the Combined Batch Fit is to allow running two or more batch 
     715fits in sequence without overwriting the output table of results.  This may be 
     716of interest for example if one is fitting a series of data sets where there is 
     717a shape change occurring in the series that requires changing the model part 
     718way through the series; for example a sphere to rod transition.  Indeed the 
     719regular batch mode does not allow for multiple models and requires all the 
     720files in the series to be fit with single model and set of parameters.  While 
     721it is of course possible to just run part of the series as a batch fit using 
     722model one followed by running another batch fit on the rest of the series with 
     723model two (and/or model three etc), doing so will overwrite the table of 
     724outputs from the previous batch fit(s).  This may not be desirable if one is 
     725interested in comparing the parameters: for example the sphere radius of set 
     726one and the cylinder radius of set two. 
     727 
     728Method 
     729^^^^^^ 
     730 
     731In order to use the *Combined Batch Fit*, first load all the data needed as 
     732described in :ref:`Loading_data`. Next start up two or more *BatchPage* fits 
     733following the instructions in :ref:`Batch_Fit_Mode` but **DO NOT PRESS FIT**. 
     734At this point the *Combine Batch Fit* menu item under the *Fitting menu* should 
     735be active (if there is one or no *BatchPage* the menu item will be greyed out 
     736and inactive).  Clicking on *Combine Batch Fit* will bring up a new panel, 
     737similar to the *Const & Simult Fit* panel. In this case there will be a 
     738checkbox for each *BatchPage* instead of each *FitPage* that should be included 
     739in the fit.  Once all are selected, click the Fit button on 
     740the *BatchPage* to run each batch fit in *sequence* 
     741 
     742.. image:: combine_batch_page.png 
     743 
     744The batch table will then pop up at the end as for the case of the simple Batch 
     745Fitting with the following caveats: 
     746 
     747.. note:: 
     748   The order matters.  The parameters in the table will be taken from the model 
     749   used in the first *BatchPage* of the list.  Any parameters from the 
     750   second and later *BatchPage* s that have the same name as a parameter in the 
     751   first will show up allowing for plotting of that parameter across the 
     752   models. The other parameters will not be available in the grid. 
     753.. note:: 
     754   a corralary of the above is that currently models created as a sum|multiply 
     755   model will not work as desired because the generated model parameters have a 
     756   p#_ appended to the beginning and thus radius and p1_radius will not be 
     757   recognized as the same parameter. 
     758    
     759.. image:: combine_batch_grid.png 
     760 
     761In the example shown above the data is a time series with a shifting peak. 
     762The first part of the series was fitted using the *broad_peak* model, while 
     763the rest of the data were fit using the *gaussian_peak* model. Unfortunately the 
     764time is not listed in the file but the file name contains the information. As 
     765described in :ref:`Grid_Window`, a column can be added manually, in this case 
     766called time, and the peak position plotted against time.  
     767 
     768.. image:: combine_batch_plot.png 
     769 
     770Note the discontinuity in the peak position.  This reflects the fact that the 
     771Gaussian fit is a rather poor model for the data and is not actually 
     772finding the peak. 
     773 
     774.. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 
     775 
     776.. note::  This help document was last changed by Paul Butler, 10 September 
     777   2017 
  • src/sas/sasgui/perspectives/fitting/media/plugin.rst

    r72100ee re081946  
    1818* By writing a model from scratch outside of SasView (only recommended for 
    1919  code monkeys!) 
     20 
     21**What follows below is quite technical. If you just want a helping hand to get  
     22started creating your own models see** :ref:`Adding_your_own_models`. 
    2023 
    2124Overview 
  • src/sas/sasgui/perspectives/fitting/model_thread.py

    r7432acb r0f9ea1c  
    7171                    (self.data.qy_data * self.data.qy_data)) 
    7272 
    73         # For theory, qmax is based on 1d qmax  
     73        # For theory, qmax is based on 1d qmax 
    7474        # so that must be mulitified by sqrt(2) to get actual max for 2d 
    7575        index_model = (self.qmin <= radius) & (radius <= self.qmax) 
     
    9191                self.data.qy_data[index_model] 
    9292            ]) 
    93         output = np.zeros(len(self.data.qx_data)) 
     93        # Initialize output to NaN so masked elements do not get plotted. 
     94        output = np.empty_like(self.data.qx_data) 
    9495        # output default is None 
    9596        # This method is to distinguish between masked 
    9697        #point(nan) and data point = 0. 
    97         output = output / output 
     98        output[:] = np.NaN 
    9899        # set value for self.mask==True, else still None to Plottools 
    99100        output[index_model] = value 
     
    198199            output[index] = self.model.evalDistribution(self.data.x[index]) 
    199200 
     201        x=self.data.x[index] 
     202        y=output[index] 
    200203        sq_values = None 
    201204        pq_values = None 
    202         s_model = None 
    203         p_model = None 
    204205        if isinstance(self.model, MultiplicationModel): 
    205206            s_model = self.model.s_model 
    206207            p_model = self.model.p_model 
    207         elif hasattr(self.model, "get_composition_models"): 
    208             p_model, s_model = self.model.get_composition_models() 
    209  
    210         if p_model is not None and s_model is not None: 
    211             sq_values = np.zeros((len(self.data.x))) 
    212             pq_values = np.zeros((len(self.data.x))) 
    213             sq_values[index] = s_model.evalDistribution(self.data.x[index]) 
    214             pq_values[index] = p_model.evalDistribution(self.data.x[index]) 
     208            sq_values = s_model.evalDistribution(x) 
     209            pq_values = p_model.evalDistribution(x) 
     210        elif hasattr(self.model, "calc_composition_models"): 
     211            results = self.model.calc_composition_models(x) 
     212            if results is not None: 
     213                pq_values, sq_values = results 
     214 
    215215 
    216216        elapsed = time.time() - self.starttime 
    217217 
    218         self.complete(x=self.data.x[index], y=output[index], 
     218        self.complete(x=x, y=y, 
    219219                      page_id=self.page_id, 
    220220                      state=self.state, 
  • src/sas/sasgui/perspectives/fitting/models.py

    r8cec26b rb1c2011  
    186186            try: 
    187187                model = load_custom_model(path) 
    188                 model.name = PLUGIN_NAME_BASE + model.name 
     188                if not model.name.count(PLUGIN_NAME_BASE): 
     189                    model.name = PLUGIN_NAME_BASE + model.name 
    189190                plugins[model.name] = model 
    190191            except Exception: 
     
    297298        for name, plug in self.stored_plugins.iteritems(): 
    298299            self.model_dictionary[name] = plug 
    299          
     300 
    300301        self._get_multifunc_models() 
    301302 
  • src/sas/sasgui/perspectives/fitting/simfitpage.py

    r959eb01 ra9f9ca4  
    11""" 
    2     Simultaneous fit page 
     2    Simultaneous or Batch fit page 
    33""" 
     4# Note that this is used for both Simultaneous/Constrained fit AND for  
     5# combined batch fit.  This is done through setting of the batch_on parameter. 
     6# There are the a half dozen or so places where an if statement is used as in  
     7# if not batch_on: 
     8#     xxxx 
     9# else: 
     10#     xxxx 
     11# This is just wrong but dont have time to fix this go. Proper approach would be 
     12# to strip all parts of the code that depend on batch_on and create the top 
     13# level class from which a contrained/simultaneous fit page and a combined  
     14# batch page inherit. 
     15# 
     16#            04/09/2017   --PDB 
     17 
    418import sys 
    519from collections import namedtuple 
     
    400414        # General Help button 
    401415        self.btHelp = wx.Button(self, wx.ID_HELP, 'HELP') 
    402         self.btHelp.SetToolTipString("Simultaneous/Constrained Fitting help.") 
     416        if self.batch_on: 
     417            self.btHelp.SetToolTipString("Combined Batch Fitting help.") 
     418        else: 
     419            self.btHelp.SetToolTipString("Simultaneous/Constrained Fitting help.") 
    403420        self.btHelp.Bind(wx.EVT_BUTTON, self._on_help) 
    404421 
     
    527544    """ 
    528545        _TreeLocation = "user/sasgui/perspectives/fitting/fitting_help.html" 
    529         _PageAnchor = "#simultaneous-fit-mode" 
    530         _doc_viewer = DocumentationWindow(self, self.ID_DOC, _TreeLocation, 
     546        if not self.batch_on: 
     547            _PageAnchor = "#simultaneous-fit-mode" 
     548            _doc_viewer = DocumentationWindow(self, self.ID_DOC, _TreeLocation, 
    531549                                          _PageAnchor, 
    532550                                          "Simultaneous/Constrained Fitting Help") 
     551        else: 
     552            _PageAnchor = "#combined-batch-fit-mode" 
     553            _doc_viewer = DocumentationWindow(self, self.ID_DOC, _TreeLocation, 
     554                                          _PageAnchor, 
     555                                          "Combined Batch Fit Help") 
    533556 
    534557    def set_manager(self, manager): 
  • src/sas/sasgui/perspectives/pr/inversion_panel.py

    r7432acb rcb62bd5  
    7070        self.rg_ctl = None 
    7171        self.iq0_ctl = None 
    72         self.bck_chk = None 
     72        self.bck_value = None 
     73        self.bck_est_ctl = None 
     74        self.bck_man_ctl = None 
     75        self.est_bck = True 
     76        self.bck_input = None 
    7377        self.bck_ctl = None 
    7478 
     
    312316        # Read the panel's parameters 
    313317        flag, alpha, dmax, nfunc, qmin, \ 
    314         qmax, height, width = self._read_pars() 
     318        qmax, height, width, bck = self._read_pars() 
    315319 
    316320        state.nfunc = nfunc 
     
    326330 
    327331        # Background evaluation checkbox 
    328         state.estimate_bck = self.bck_chk.IsChecked() 
     332        state.estimate_bck = self.est_bck 
     333        state.bck_value = bck 
    329334 
    330335        # Estimates 
     
    371376        self.plot_data.SetValue(str(state.file)) 
    372377 
    373         # Background evaluation checkbox 
    374         self.bck_chk.SetValue(state.estimate_bck) 
     378        # Background value 
     379        self.bck_est_ctl.SetValue(state.estimate_bck) 
     380        self.bck_man_ctl.SetValue(not state.estimate_bck) 
     381        if not state.estimate_bck: 
     382            self.bck_input.Enable() 
     383            self.bck_input.SetValue(str(state.bck_value)) 
     384        self.est_bck = state.estimate_bck 
     385        self.bck_value = state.bck_value 
    375386 
    376387        # Estimates 
     
    431442                       wx.EXPAND | wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 15) 
    432443 
    433         self.bck_chk = wx.CheckBox(self, -1, "Estimate background level") 
    434         hint_msg = "Check box to let the fit estimate " 
    435         hint_msg += "the constant background level." 
    436         self.bck_chk.SetToolTipString(hint_msg) 
    437         self.bck_chk.Bind(wx.EVT_CHECKBOX, self._on_pars_changed) 
     444        radio_sizer = wx.GridBagSizer(5, 5) 
     445 
     446        self.bck_est_ctl = wx.RadioButton(self, -1, "Estimate background level", 
     447            name="estimate_bck", style=wx.RB_GROUP) 
     448        self.bck_man_ctl = wx.RadioButton(self, -1, "Input manual background level", 
     449            name="manual_bck") 
     450 
     451        self.bck_est_ctl.Bind(wx.EVT_RADIOBUTTON, self._on_bck_changed) 
     452        self.bck_man_ctl.Bind(wx.EVT_RADIOBUTTON, self._on_bck_changed) 
     453 
     454        radio_sizer.Add(self.bck_est_ctl, (0,0), (1,1), wx.LEFT | wx.EXPAND) 
     455        radio_sizer.Add(self.bck_man_ctl, (0,1), (1,1), wx.RIGHT | wx.EXPAND) 
     456 
    438457        iy += 1 
    439         pars_sizer.Add(self.bck_chk, (iy, 0), (1, 2), 
     458        pars_sizer.Add(radio_sizer, (iy, 0), (1, 2), 
    440459                       wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15) 
     460 
     461        background_label = wx.StaticText(self, -1, "Background: ") 
     462        self.bck_input = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, 
     463            size=(60, 20), value="0.0") 
     464        self.bck_input.Disable() 
     465        self.bck_input.Bind(wx.EVT_TEXT, self._read_pars) 
     466        background_units = wx.StaticText(self, -1, "[A^(-1)]", size=(55, 20)) 
     467        iy += 1 
     468 
     469        background_sizer = wx.GridBagSizer(5, 5) 
     470 
     471        background_sizer.Add(background_label, (0, 0), (1,1), 
     472            wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 23) 
     473        background_sizer.Add(self.bck_input, (0, 1), (1,1), 
     474            wx.LEFT | wx.ADJUST_MINSIZE, 5) 
     475        background_sizer.Add(background_units, (0, 2), (1,1), 
     476            wx.LEFT | wx.ADJUST_MINSIZE, 5) 
     477        pars_sizer.Add(background_sizer, (iy, 0), (1, 2), 
     478            wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15) 
     479 
    441480        boxsizer1.Add(pars_sizer, 0, wx.EXPAND) 
    442481        vbox.Add(boxsizer1, (iy_vb, 0), (1, 1), 
     
    764803        self._on_pars_changed() 
    765804 
     805    def _on_bck_changed(self, evt=None): 
     806        self.est_bck = self.bck_est_ctl.GetValue() 
     807        if self.est_bck: 
     808            self.bck_input.Disable() 
     809        else: 
     810            self.bck_input.Enable() 
     811 
    766812    def _on_pars_changed(self, evt=None): 
    767813        """ 
     
    770816        scenes. 
    771817        """ 
    772         flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars() 
    773         has_bck = self.bck_chk.IsChecked() 
     818        flag, alpha, dmax, nfunc, qmin, qmax, height, width, bck = self._read_pars() 
    774819 
    775820        # If the pars are valid, estimate alpha 
     
    783828                                                      d_max=dmax, 
    784829                                                      q_min=qmin, q_max=qmax, 
    785                                                       bck=has_bck, 
     830                                                      est_bck=self.est_bck, 
     831                                                      bck_val=bck, 
    786832                                                      height=height, 
    787833                                                      width=width) 
     
    797843        height = 0 
    798844        width = 0 
     845        background = 0 
    799846        flag = True 
    800847        # Read slit height 
     
    890937            self.qmax_ctl.Refresh() 
    891938 
    892         return flag, alpha, dmax, nfunc, qmin, qmax, height, width 
     939        # Read background 
     940        if not self.est_bck: 
     941            try: 
     942                bck_str = self.bck_input.GetValue() 
     943                if len(bck_str.strip()) == 0: 
     944                    background = 0.0 
     945                else: 
     946                    background = float(bck_str) 
     947                    self.bck_input.SetBackgroundColour(wx.WHITE) 
     948            except ValueError: 
     949                background = 0.0 
     950                self.bck_input.SetBackgroundColour("pink") 
     951            self.bck_input.Refresh() 
     952 
     953        return flag, alpha, dmax, nfunc, qmin, qmax, height, width, background 
    893954 
    894955    def _on_explore(self, evt): 
     
    915976        # Push it to the manager 
    916977 
    917         flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars() 
    918         has_bck = self.bck_chk.IsChecked() 
     978        flag, alpha, dmax, nfunc, qmin, qmax, height, width, bck = self._read_pars() 
    919979 
    920980        if flag: 
     
    928988                                                   d_max=dmax, 
    929989                                                   q_min=qmin, q_max=qmax, 
    930                                                    bck=has_bck, 
     990                                                   est_bck=self.est_bck, 
     991                                                   bck_val = bck, 
    931992                                                   height=height, 
    932993                                                   width=width) 
  • src/sas/sasgui/perspectives/pr/inversion_state.py

    r7432acb ra0e6b1b  
    3636           ["qmin", "qmin"], 
    3737           ["qmax", "qmax"], 
    38            ["estimate_bck", "estimate_bck"]] 
     38           ["estimate_bck", "estimate_bck"], 
     39           ["bck_value", "bck_value"]] 
    3940 
    4041## List of P(r) inversion outputs 
     
    6263        self.estimate_bck = False 
    6364        self.timestamp = time.time() 
     65        self.bck_value = 0.0 
    6466 
    6567        # Inversion parameters 
     
    109111        state += "Timestamp:    %s\n" % self.timestamp 
    110112        state += "Estimate bck: %s\n" % str(self.estimate_bck) 
     113        state += "Bck Value:    %s\n" % str(self.bck_value) 
    111114        state += "No. terms:    %s\n" % str(self.nfunc) 
    112115        state += "D_max:        %s\n" % str(self.d_max) 
     
    296299                            self.coefficients.append(float(c)) 
    297300                        except: 
    298                             # Bad data, skip. We will count the number of  
    299                             # coefficients at the very end and deal with  
     301                            # Bad data, skip. We will count the number of 
     302                            # coefficients at the very end and deal with 
    300303                            # inconsistencies then. 
    301304                            pass 
     
    329332                                cov_row.append(float(c)) 
    330333                            except: 
    331                                 # Bad data, skip. We will count the number of  
    332                                 # coefficients at the very end and deal with  
     334                                # Bad data, skip. We will count the number of 
     335                                # coefficients at the very end and deal with 
    333336                                # inconsistencies then. 
    334337                                pass 
     
    461464                tree = etree.parse(path, parser=etree.ETCompatXMLParser()) 
    462465                # Check the format version number 
    463                 # Specifying the namespace will take care of the file  
    464                 #format version  
     466                # Specifying the namespace will take care of the file 
     467                #format version 
    465468                root = tree.getroot() 
    466469 
  • src/sas/sasgui/perspectives/pr/media/pr_help.rst

    r1221196 r1abd19c  
    4949P(r) inversion requires that the background be perfectly subtracted.  This is 
    5050often difficult to do well and thus many data sets will include a background. 
    51 For those cases, the user should check the "estimate background" box and the 
    52 module will do its best to estimate it. 
     51For those cases, the user should check the "Estimate background level" option 
     52and the module will do its best to estimate it. If you know the background value 
     53for your data, select the "Input manual background level" option. Note that 
     54this value will be treated as having 0 error. 
    5355 
    5456The P(r) module is constantly computing in the background what the optimum 
  • src/sas/sasgui/perspectives/pr/pr.py

    ra1b8fee rcb62bd5  
    6868        self.q_min = None 
    6969        self.q_max = None 
    70         self.has_bck = False 
     70        self.est_bck = False 
     71        self.bck_val = 0 
    7172        self.slit_height = 0 
    7273        self.slit_width = 0 
     
    828829        self.control_panel.iq0 = pr.iq0(out) 
    829830        self.control_panel.bck = pr.background 
     831        self.control_panel.bck_input.SetValue("{:.2g}".format(pr.background)) 
    830832 
    831833        # Show I(q) fit 
     
    907909 
    908910    def setup_plot_inversion(self, alpha, nfunc, d_max, q_min=None, q_max=None, 
    909                              bck=False, height=0, width=0): 
     911                             est_bck=False, bck_val=0, height=0, width=0): 
    910912        """ 
    911913            Set up inversion from plotted data 
     
    916918        self.q_min = q_min 
    917919        self.q_max = q_max 
    918         self.has_bck = bck 
     920        self.est_bck = est_bck 
     921        self.bck_val = bck_val 
    919922        self.slit_height = height 
    920923        self.slit_width = width 
     
    930933    def estimate_plot_inversion(self, alpha, nfunc, d_max, 
    931934                                q_min=None, q_max=None, 
    932                                 bck=False, height=0, width=0): 
     935                                est_bck=False, bck_val=0, height=0, width=0): 
    933936        """ 
    934937            Estimate parameters from plotted data 
     
    939942        self.q_min = q_min 
    940943        self.q_max = q_max 
    941         self.has_bck = bck 
     944        self.est_bck = est_bck 
     945        self.bck_val = bck_val 
    942946        self.slit_height = height 
    943947        self.slit_width = width 
     
    973977        pr.x = self.current_plottable.x 
    974978        pr.y = self.current_plottable.y 
    975         pr.has_bck = self.has_bck 
     979        pr.est_bck = self.est_bck 
    976980        pr.slit_height = self.slit_height 
    977981        pr.slit_width = self.slit_width 
     982        pr.background = self.bck_val 
    978983 
    979984        # Keep track of the plot window title to ensure that 
     
    10191024        self.q_min = q_min 
    10201025        self.q_max = q_max 
    1021         self.has_bck = bck 
     1026        self.est_bck = bck 
    10221027        self.slit_height = height 
    10231028        self.slit_width = width 
     
    10421047        self.q_min = q_min 
    10431048        self.q_max = q_max 
    1044         self.has_bck = bck 
     1049        self.est_bck = bck 
    10451050        self.slit_height = height 
    10461051        self.slit_width = width 
     
    11151120            pr.y = y 
    11161121            pr.err = err 
    1117             pr.has_bck = self.has_bck 
     1122            pr.est_bck = self.est_bck 
    11181123            pr.slit_height = self.slit_height 
    11191124            pr.slit_width = self.slit_width 
Note: See TracChangeset for help on using the changeset viewer.