Changeset 835937b5 in sasview for src


Ignore:
Timestamp:
Sep 18, 2017 3:00:13 PM (7 years ago)
Author:
Paul Kienzle <pkienzle@…>
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, magnetic_scatt, release-4.2.2, ticket-1009, ticket-1094-headless, ticket-1242-2d-resolution, ticket-1243, ticket-1249, ticket885, unittest-saveload
Children:
53b8266
Parents:
af1187a (diff), cfd27dd (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 branch 'master' into ticket-869

Location:
src/sas
Files:
3 added
14 deleted
13 edited

Legend:

Unmodified
Added
Removed
  • src/sas/sascalc/dataloader/data_info.py

    r5a8cdbb r17e257b5  
    11611161    final_dataset = None 
    11621162    if isinstance(data, plottable_1D): 
    1163         final_dataset = Data1D(data.x, data.y) 
     1163        final_dataset = Data1D(data.x, data.y, isSesans=datainfo.isSesans) 
    11641164        final_dataset.dx = data.dx 
    11651165        final_dataset.dy = data.dy 
    11661166        final_dataset.dxl = data.dxl 
    11671167        final_dataset.dxw = data.dxw 
     1168        final_dataset.x_unit = data._xunit 
     1169        final_dataset.y_unit = data._yunit 
    11681170        final_dataset.xaxis(data._xaxis, data._xunit) 
    11691171        final_dataset.yaxis(data._yaxis, data._yunit) 
  • src/sas/sascalc/dataloader/file_reader_base_class.py

    rdcb91cf ra78a02f  
    66 
    77import os 
     8import re 
    89import logging 
    910import numpy as np 
     
    106107        for data in self.output: 
    107108            if isinstance(data, Data1D): 
     109                # Normalize the units for 
     110                data.x_unit = self.format_unit(data.x_unit) 
     111                data.y_unit = self.format_unit(data.y_unit) 
    108112                # Sort data by increasing x and remove 1st point 
    109113                ind = np.lexsort((data.y, data.x)) 
     
    131135        for dataset in self.output: 
    132136            if isinstance(dataset, Data2D): 
     137                # Normalize the units for 
     138                dataset.x_unit = self.format_unit(dataset.Q_unit) 
     139                dataset.y_unit = self.format_unit(dataset.I_unit) 
    133140                dataset.data = dataset.data.astype(np.float64) 
    134141                dataset.qx_data = dataset.qx_data.astype(np.float64) 
     
    155162                dataset.data = dataset.data.flatten() 
    156163 
     164    def format_unit(self, unit=None): 
     165        """ 
     166        Format units a common way 
     167        :param unit: 
     168        :return: 
     169        """ 
     170        if unit: 
     171            split = unit.split("/") 
     172            if len(split) == 1: 
     173                return unit 
     174            elif split[0] == '1': 
     175                return "{0}^".format(split[1]) + "{-1}" 
     176            else: 
     177                return "{0}*{1}^".format(split[0], split[1]) + "{-1}" 
     178 
    157179    def set_all_to_none(self): 
    158180        """ 
  • src/sas/sascalc/dataloader/readers/cansas_reader.py

    rdcb91cf ra78a02f  
    299299                    self.current_dataset.dx = np.append(self.current_dataset.dx, data_point) 
    300300                elif tagname == 'dQw': 
    301                     if self.current_dataset.dqw is None: self.current_dataset.dqw = np.empty(0) 
     301                    if self.current_dataset.dxw is None: 
     302                        self.current_dataset.dxw = np.empty(0) 
    302303                    self.current_dataset.dxw = np.append(self.current_dataset.dxw, data_point) 
    303304                elif tagname == 'dQl': 
    304                     if self.current_dataset.dxl is None: self.current_dataset.dxl = np.empty(0) 
     305                    if self.current_dataset.dxl is None: 
     306                        self.current_dataset.dxl = np.empty(0) 
    305307                    self.current_dataset.dxl = np.append(self.current_dataset.dxl, data_point) 
    306308                elif tagname == 'Qmean': 
  • src/sas/sascalc/dataloader/readers/danse_reader.py

    r713a047 ra78a02f  
    189189        x_vals = np.tile(x_vals, (size_y, 1)).flatten() 
    190190        y_vals = np.tile(y_vals, (size_x, 1)).T.flatten() 
    191         if self.current_dataset.err_data == np.all(np.array(None)) or np.any(self.current_dataset.err_data <= 0): 
     191        if (np.all(self.current_dataset.err_data == None) 
     192                or np.any(self.current_dataset.err_data <= 0)): 
    192193            new_err_data = np.sqrt(np.abs(self.current_dataset.data)) 
    193194        else: 
  • src/sas/sasgui/guiframe/local_perspectives/plotting/plotting.py

    r235f514 r2d9526d  
    1414import wx 
    1515import sys 
     16from copy import deepcopy 
    1617from sas.sasgui.guiframe.events import EVT_NEW_PLOT 
    1718from sas.sasgui.guiframe.events import EVT_PLOT_QRANGE 
     
    275276                action_check = True 
    276277            else: 
     278                if action_string == 'update': 
     279                    # Update all existing plots of data with this ID 
     280                    for data in event.plots: 
     281                        for panel in self.plot_panels.values(): 
     282                            if data.id in panel.plots.keys(): 
     283                                plot_exists = True 
     284                                # Pass each panel it's own copy of the data 
     285                                # that's being updated, otherwise things like 
     286                                # colour and line thickness are unintentionally 
     287                                # synced across panels 
     288                                self.update_panel(deepcopy(data), panel) 
     289                    return 
     290                     
    277291                group_id = event.group_id 
    278                 if group_id in self.plot_panels.keys(): 
     292                if group_id in self.plot_panels: 
    279293                    #remove data from panel 
    280294                    if action_string == 'remove': 
  • src/sas/sasgui/perspectives/calculator/model_editor.py

    r07ec714 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): 
     
    12781211""" 
    12791212SUM_TEMPLATE = """ 
    1280 # A sample of an experimental model function for Sum/Multiply(Pmodel1,Pmodel2) 
    1281 import os 
    1282 import sys 
    1283 import copy 
    1284 import collections 
    1285  
    1286 import numpy 
    1287  
    1288 from sas.sascalc.fit.pluginmodel import Model1DPlugin 
    1289 from sasmodels.sasview_model import find_model 
    1290  
    1291 class Model(Model1DPlugin): 
    1292     name = os.path.splitext(os.path.basename(__file__))[0] 
    1293     is_multiplicity_model = False 
    1294     def __init__(self, multiplicity=1): 
    1295         Model1DPlugin.__init__(self, name='') 
    1296         P1 = find_model('%s') 
    1297         P2 = find_model('%s') 
    1298         p_model1 = P1() 
    1299         p_model2 = P2() 
    1300         ## Setting  model name model description 
    1301         self.description = '%s' 
    1302         if self.name.rstrip().lstrip() == '': 
    1303             self.name = self._get_name(p_model1.name, p_model2.name) 
    1304         if self.description.rstrip().lstrip() == '': 
    1305             self.description = p_model1.name 
    1306             self.description += p_model2.name 
    1307             self.fill_description(p_model1, p_model2) 
    1308  
    1309         ## Define parameters 
    1310         self.params = collections.OrderedDict() 
    1311  
    1312         ## Parameter details [units, min, max] 
    1313         self.details = {} 
    1314         ## Magnetic Panrameters 
    1315         self.magnetic_params = [] 
    1316         # non-fittable parameters 
    1317         self.non_fittable = p_model1.non_fittable 
    1318         self.non_fittable += p_model2.non_fittable 
    1319  
    1320         ##models 
    1321         self.p_model1= p_model1 
    1322         self.p_model2= p_model2 
    1323  
    1324  
    1325         ## dispersion 
    1326         self._set_dispersion() 
    1327         ## Define parameters 
    1328         self._set_params() 
    1329         ## New parameter:scaling_factor 
    1330         self.params['scale_factor'] = %s 
    1331  
    1332         ## Parameter details [units, min, max] 
    1333         self._set_details() 
    1334         self.details['scale_factor'] = ['', 0.0, numpy.inf] 
    1335  
    1336  
    1337         #list of parameter that can be fitted 
    1338         self._set_fixed_params() 
    1339  
    1340         ## parameters with orientation 
    1341         self.orientation_params = [] 
    1342         for item in self.p_model1.orientation_params: 
    1343             new_item = "p1_" + item 
    1344             if not new_item in self.orientation_params: 
    1345                 self.orientation_params.append(new_item) 
    1346  
    1347         for item in self.p_model2.orientation_params: 
    1348             new_item = "p2_" + item 
    1349             if not new_item in self.orientation_params: 
    1350                 self.orientation_params.append(new_item) 
    1351         ## magnetic params 
    1352         self.magnetic_params = [] 
    1353         for item in self.p_model1.magnetic_params: 
    1354             new_item = "p1_" + item 
    1355             if not new_item in self.magnetic_params: 
    1356                 self.magnetic_params.append(new_item) 
    1357  
    1358         for item in self.p_model2.magnetic_params: 
    1359             new_item = "p2_" + item 
    1360             if not new_item in self.magnetic_params: 
    1361                 self.magnetic_params.append(new_item) 
    1362         # get multiplicity if model provide it, else 1. 
    1363         try: 
    1364             multiplicity1 = p_model1.multiplicity 
    1365             try: 
    1366                 multiplicity2 = p_model2.multiplicity 
    1367             except: 
    1368                 multiplicity2 = 1 
    1369         except: 
    1370             multiplicity1 = 1 
    1371             multiplicity2 = 1 
    1372         ## functional multiplicity of the model 
    1373         self.multiplicity1 = multiplicity1 
    1374         self.multiplicity2 = multiplicity2 
    1375         self.multiplicity_info = [] 
    1376  
    1377     def _clone(self, obj): 
    1378         import copy 
    1379         obj.params     = copy.deepcopy(self.params) 
    1380         obj.description     = copy.deepcopy(self.description) 
    1381         obj.details    = copy.deepcopy(self.details) 
    1382         obj.dispersion = copy.deepcopy(self.dispersion) 
    1383         obj.p_model1  = self.p_model1.clone() 
    1384         obj.p_model2  = self.p_model2.clone() 
    1385         #obj = copy.deepcopy(self) 
    1386         return obj 
    1387  
    1388     def _get_name(self, name1, name2): 
    1389         p1_name = self._get_upper_name(name1) 
    1390         if not p1_name: 
    1391             p1_name = name1 
    1392         name = p1_name 
    1393         name += "_and_" 
    1394         p2_name = self._get_upper_name(name2) 
    1395         if not p2_name: 
    1396             p2_name = name2 
    1397         name += p2_name 
    1398         return name 
    1399  
    1400     def _get_upper_name(self, name=None): 
    1401         if name is None: 
    1402             return "" 
    1403         upper_name = "" 
    1404         str_name = str(name) 
    1405         for index in range(len(str_name)): 
    1406             if str_name[index].isupper(): 
    1407                 upper_name += str_name[index] 
    1408         return upper_name 
    1409  
    1410     def _set_dispersion(self): 
    1411         self.dispersion = collections.OrderedDict() 
    1412         ##set dispersion only from p_model 
    1413         for name , value in self.p_model1.dispersion.iteritems(): 
    1414             #if name.lower() not in self.p_model1.orientation_params: 
    1415             new_name = "p1_" + name 
    1416             self.dispersion[new_name]= value 
    1417         for name , value in self.p_model2.dispersion.iteritems(): 
    1418             #if name.lower() not in self.p_model2.orientation_params: 
    1419             new_name = "p2_" + name 
    1420             self.dispersion[new_name]= value 
    1421  
    1422     def function(self, x=0.0): 
    1423         return 0 
    1424  
    1425     def getProfile(self): 
    1426         try: 
    1427             x,y = self.p_model1.getProfile() 
    1428         except: 
    1429             x = None 
    1430             y = None 
    1431  
    1432         return x, y 
    1433  
    1434     def _set_params(self): 
    1435         for name , value in self.p_model1.params.iteritems(): 
    1436             # No 2D-supported 
    1437             #if name not in self.p_model1.orientation_params: 
    1438             new_name = "p1_" + name 
    1439             self.params[new_name]= value 
    1440  
    1441         for name , value in self.p_model2.params.iteritems(): 
    1442             # No 2D-supported 
    1443             #if name not in self.p_model2.orientation_params: 
    1444             new_name = "p2_" + name 
    1445             self.params[new_name]= value 
    1446  
    1447         # Set "scale" as initializing 
    1448         self._set_scale_factor() 
    1449  
    1450  
    1451     def _set_details(self): 
    1452         for name ,detail in self.p_model1.details.iteritems(): 
    1453             new_name = "p1_" + name 
    1454             #if new_name not in self.orientation_params: 
    1455             self.details[new_name]= detail 
    1456  
    1457         for name ,detail in self.p_model2.details.iteritems(): 
    1458             new_name = "p2_" + name 
    1459             #if new_name not in self.orientation_params: 
    1460             self.details[new_name]= detail 
    1461  
    1462     def _set_scale_factor(self): 
    1463         pass 
    1464  
    1465  
    1466     def setParam(self, name, value): 
    1467         # set param to this (p1, p2) model 
    1468         self._setParamHelper(name, value) 
    1469  
    1470         ## setParam to p model 
    1471         model_pre = '' 
    1472         new_name = '' 
    1473         name_split = name.split('_', 1) 
    1474         if len(name_split) == 2: 
    1475             model_pre = name.split('_', 1)[0] 
    1476             new_name = name.split('_', 1)[1] 
    1477         if model_pre == "p1": 
    1478             if new_name in self.p_model1.getParamList(): 
    1479                 self.p_model1.setParam(new_name, value) 
    1480         elif model_pre == "p2": 
    1481              if new_name in self.p_model2.getParamList(): 
    1482                 self.p_model2.setParam(new_name, value) 
    1483         elif name == 'scale_factor': 
    1484             self.params['scale_factor'] = value 
    1485         else: 
    1486             raise ValueError, "Model does not contain parameter %s" % name 
    1487  
    1488     def getParam(self, name): 
    1489         # Look for dispersion parameters 
    1490         toks = name.split('.') 
    1491         if len(toks)==2: 
    1492             for item in self.dispersion.keys(): 
    1493                 # 2D not supported 
    1494                 if item.lower()==toks[0].lower(): 
    1495                     for par in self.dispersion[item]: 
    1496                         if par.lower() == toks[1].lower(): 
    1497                             return self.dispersion[item][par] 
    1498         else: 
    1499             # Look for standard parameter 
    1500             for item in self.params.keys(): 
    1501                 if item.lower()==name.lower(): 
    1502                     return self.params[item] 
    1503         return 
    1504         #raise ValueError, "Model does not contain parameter %s" % name 
    1505  
    1506     def _setParamHelper(self, name, value): 
    1507         # Look for dispersion parameters 
    1508         toks = name.split('.') 
    1509         if len(toks)== 2: 
    1510             for item in self.dispersion.keys(): 
    1511                 if item.lower()== toks[0].lower(): 
    1512                     for par in self.dispersion[item]: 
    1513                         if par.lower() == toks[1].lower(): 
    1514                             self.dispersion[item][par] = value 
    1515                             return 
    1516         else: 
    1517             # Look for standard parameter 
    1518             for item in self.params.keys(): 
    1519                 if item.lower()== name.lower(): 
    1520                     self.params[item] = value 
    1521                     return 
    1522  
    1523         raise ValueError, "Model does not contain parameter %s" % name 
    1524  
    1525  
    1526     def _set_fixed_params(self): 
    1527         self.fixed = [] 
    1528         for item in self.p_model1.fixed: 
    1529             new_item = "p1" + item 
    1530             self.fixed.append(new_item) 
    1531         for item in self.p_model2.fixed: 
    1532             new_item = "p2" + item 
    1533             self.fixed.append(new_item) 
    1534  
    1535         self.fixed.sort() 
    1536  
    1537  
    1538     def run(self, x = 0.0): 
    1539         self._set_scale_factor() 
    1540         return self.params['scale_factor'] %s \ 
    1541 (self.p_model1.run(x) %s self.p_model2.run(x)) 
    1542  
    1543     def runXY(self, x = 0.0): 
    1544         self._set_scale_factor() 
    1545         return self.params['scale_factor'] %s \ 
    1546 (self.p_model1.runXY(x) %s self.p_model2.runXY(x)) 
    1547  
    1548     ## Now (May27,10) directly uses the model eval function 
    1549     ## instead of the for-loop in Base Component. 
    1550     def evalDistribution(self, x = []): 
    1551         self._set_scale_factor() 
    1552         return self.params['scale_factor'] %s \ 
    1553 (self.p_model1.evalDistribution(x) %s \ 
    1554 self.p_model2.evalDistribution(x)) 
    1555  
    1556     def set_dispersion(self, parameter, dispersion): 
    1557         value= None 
    1558         new_pre = parameter.split("_", 1)[0] 
    1559         new_parameter = parameter.split("_", 1)[1] 
    1560         try: 
    1561             if new_pre == 'p1' and \ 
    1562 new_parameter in self.p_model1.dispersion.keys(): 
    1563                 value= self.p_model1.set_dispersion(new_parameter, dispersion) 
    1564             if new_pre == 'p2' and \ 
    1565 new_parameter in self.p_model2.dispersion.keys(): 
    1566                 value= self.p_model2.set_dispersion(new_parameter, dispersion) 
    1567             self._set_dispersion() 
    1568             return value 
    1569         except: 
    1570             raise 
    1571  
    1572     def fill_description(self, p_model1, p_model2): 
    1573         description = "" 
    1574         description += "This model gives the summation or multiplication of" 
    1575         description += "%s and %s. "% ( p_model1.name, p_model2.name ) 
    1576         self.description += description 
    1577  
    1578 if __name__ == "__main__": 
    1579     m1= Model() 
    1580     #m1.setParam("p1_scale", 25) 
    1581     #m1.setParam("p1_length", 1000) 
    1582     #m1.setParam("p2_scale", 100) 
    1583     #m1.setParam("p2_rg", 100) 
    1584     out1 = m1.runXY(0.01) 
    1585  
    1586     m2= Model() 
    1587     #m2.p_model1.setParam("scale", 25) 
    1588     #m2.p_model1.setParam("length", 1000) 
    1589     #m2.p_model2.setParam("scale", 100) 
    1590     #m2.p_model2.setParam("rg", 100) 
    1591     out2 = m2.p_model1.runXY(0.01) %s m2.p_model2.runXY(0.01)\n 
    1592     print "My name is %s."% m1.name 
    1593     print out1, " = ", out2 
    1594     if out1 == out2: 
    1595         print "===> Simple Test: Passed!" 
    1596     else: 
    1597         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) 
    15981219""" 
    1599  
    16001220if __name__ == "__main__": 
    16011221#    app = wx.PySimpleApp() 
  • src/sas/sasgui/perspectives/fitting/fitting.py

    r489f53a r2d9526d  
    17421742            @param unsmeared_error: data error, rescaled to unsmeared model 
    17431743        """ 
    1744  
    17451744        number_finite = np.count_nonzero(np.isfinite(y)) 
    17461745        np.nan_to_num(y) 
     
    17481747                                         data_description=model.name, 
    17491748                                         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 
    17501751        if unsmeared_model is not None: 
    1751             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, 
    17521754                                  data_description=model.name + " unsmeared", 
    17531755                                  data_id=str(page_id) + " " + data.name + " unsmeared") 
     1756            plots_to_update.append(unsmeared_model_plot) 
    17541757 
    17551758            if unsmeared_data is not None and unsmeared_error is not None: 
    1756                 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, 
    17571761                                      data_description="Data unsmeared", 
    17581762                                      data_id="Data  " + data.name + " unsmeared", 
    17591763                                      dy=unsmeared_error) 
    1760         # Comment this out until we can get P*S models with correctly populated parameters 
    1761         #if sq_model is not None and pq_model is not None: 
    1762         #    self.create_theory_1D(x, sq_model, page_id, model, data, state, 
    1763         #                          data_description=model.name + " S(q)", 
    1764         #                          data_id=str(page_id) + " " + data.name + " S(q)") 
    1765         #    self.create_theory_1D(x, pq_model, page_id, model, data, state, 
    1766         #                          data_description=model.name + " P(q)", 
    1767         #                          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')) 
    17681779 
    17691780        current_pg = self.fit_panel.get_page_by_id(page_id) 
  • 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/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/plottools/plottables.py

    r45dffa69 r2d9526d  
    239239    def replace(self, plottable): 
    240240        """Replace an existing plottable from the graph""" 
    241         selected_color = None 
     241        # If the user has set a custom color, ensure the new plot is the same color 
     242        selected_color = plottable.custom_color 
    242243        selected_plottable = None 
    243244        for p in self.plottables.keys(): 
    244245            if plottable.id == p.id: 
    245246                selected_plottable = p 
    246                 selected_color = self.plottables[p] 
     247                if selected_color is None: 
     248                    selected_color = self.plottables[p] 
    247249                break 
    248         if  selected_plottable is not None and selected_color is not None: 
     250        if selected_plottable is not None and selected_color is not None: 
    249251            del self.plottables[selected_plottable] 
     252            plottable.custom_color = selected_color 
    250253            self.plottables[plottable] = selected_color 
    251254 
  • src/sas/sasgui/perspectives/fitting/basepage.py

    r914c49d5 raf1187a  
    1515import traceback 
    1616 
     17from Queue import Queue 
     18from threading import Thread 
    1719from collections import defaultdict 
    1820from wx.lib.scrolledpanel import ScrolledPanel 
     
    241243        self.set_layout() 
    242244 
     245        # Setting up a thread for the fitting 
     246        self.threaded_draw_queue = Queue() 
     247 
     248        self.draw_worker_thread = Thread(target = self._threaded_draw_worker, 
     249                                         args = (self.threaded_draw_queue,)) 
     250        self.draw_worker_thread.setDaemon(True) 
     251        self.draw_worker_thread.start() 
     252 
     253        # And a home for the thread submission times 
     254        self.last_time_fit_submitted = 0.00 
     255 
    243256    def set_index_model(self, index): 
    244257        """ 
     
    16931706        :param chisqr: update chisqr value [bool] 
    16941707        """ 
    1695         wx.CallAfter(self._draw_model_after, update_chisqr, source) 
     1708        self.threaded_draw_queue.put([copy.copy(update_chisqr), copy.copy(source)]) 
     1709 
     1710    def _threaded_draw_worker(self, threaded_draw_queue): 
     1711        while True: 
     1712            # sit and wait for the next task 
     1713            next_task = threaded_draw_queue.get() 
     1714 
     1715            # sleep for 1/10th second in case some other tasks accumulate 
     1716            time.sleep(0.1) 
     1717 
     1718            # skip all intermediate tasks 
     1719            while self.threaded_draw_queue.qsize() > 0: 
     1720                self.threaded_draw_queue.task_done() 
     1721                next_task = self.threaded_draw_queue.get() 
     1722 
     1723            # and finally, do the task 
     1724            self._draw_model_after(*next_task) 
     1725            threaded_draw_queue.task_done() 
    16961726 
    16971727    def _draw_model_after(self, update_chisqr=True, source='model'): 
     
    17161746            toggle_mode_on = self.model_view.IsEnabled() 
    17171747            is_2d = self._is_2D() 
     1748 
    17181749            self._manager.draw_model(self.model, 
    17191750                                     data=self.data, 
Note: See TracChangeset for help on using the changeset viewer.