Ignore:
Timestamp:
Jul 5, 2017 5:28:55 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:
1386b2f
Parents:
251ef684
Message:

clean up plugin-model handling code; preserve active parameter values when plugin is updated

File:
1 edited

Legend:

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

    r65f3930 r277257f  
    3131import re 
    3232import logging 
     33import datetime 
     34 
    3335from wx.py.editwindow import EditWindow 
     36 
    3437from sas.sasgui.guiframe.documentation_window import DocumentationWindow 
     38 
    3539from .pyconsole import show_model_output, check_model 
    3640 
    3741logger = logging.getLogger(__name__) 
    38  
    3942 
    4043if sys.platform.count("win32") > 0: 
     
    7881    a Modal Dialog. 
    7982 
    80     :TODO the build in compiler currently balks at when it tries to import 
     83    :TODO the built in compiler currently balks at when it tries to import 
    8184    a model whose name contains spaces or symbols (such as + ... underscore 
    8285    should be fine).  Have fixed so the editor cannot save such a file name 
     
    336339            list_fnames = os.listdir(self.plugin_dir) 
    337340            # fake existing regular model name list 
    338             m_list = [model + ".py" for model in self.model_list] 
     341            m_list = [model.name + ".py" for model in self.model_list] 
    339342            list_fnames.append(m_list) 
    340343            if t_fname in list_fnames and title != mname: 
     
    855858                    exec "float(math.%s)" % item 
    856859                    self.math_combo.Append(str(item)) 
    857                 except: 
     860                except Exception: 
    858861                    self.math_combo.Append(str(item) + "()") 
    859862        self.math_combo.Bind(wx.EVT_COMBOBOX, self._on_math_select) 
     
    980983            msg = "Name exists already." 
    981984 
    982         # Prepare the messagebox 
     985        # 
    983986        if self.base is not None and not msg: 
    984987            self.base.update_custom_combo() 
    985             # Passed exception in import test as it will fail for sasmodels.sasview_model class 
    986             # Should add similar test for new style? 
    987             Model = None 
    988             try: 
    989                 exec "from %s import Model" % name 
    990             except: 
    991                 logger.error(sys.exc_value) 
    992988 
    993989        # Prepare the messagebox 
     
    10201016        :param func_str: content of func; Strings 
    10211017        """ 
    1022         try: 
    1023             out_f = open(fname, 'w') 
    1024         except: 
    1025             raise 
    1026         # Prepare the content of the function 
    1027         lines = CUSTOM_TEMPLATE.split('\n') 
    1028  
    1029         has_scipy = func_str.count("scipy.") 
    1030         if has_scipy: 
    1031             lines.insert(0, 'import scipy') 
    1032  
    1033         # Think about 2D later 
    1034         #self.is_2d = func_str.count("#self.ndim = 2") 
    1035         #line_2d = '' 
    1036         #if self.is_2d: 
    1037         #    line_2d = CUSTOM_2D_TEMP.split('\n') 
    1038  
    1039         # Also think about test later 
    1040         #line_test = TEST_TEMPLATE.split('\n') 
    1041         #local_params = '' 
    1042         #spaces = '        '#8spaces 
    1043         spaces4  = ' '*4 
    1044         spaces13 = ' '*13 
    1045         spaces16 = ' '*16 
     1018        out_f = open(fname, 'w') 
     1019 
     1020        out_f.write(CUSTOM_TEMPLATE % { 
     1021            'name': name, 
     1022            'title': 'User model for ' + name, 
     1023            'description': desc_str, 
     1024            'date': datetime.datetime.now().strftime('%YYYY-%mm-%dd'), 
     1025        }) 
     1026 
     1027        # Write out parameters 
    10461028        param_names = []    # to store parameter names 
    1047         has_scipy = func_str.count("scipy.") 
    1048         if has_scipy: 
    1049             lines.insert(0, 'import scipy') 
    1050  
    1051         # write function here 
    1052         for line in lines: 
    1053             # The location where to put the strings is 
    1054             # hard-coded in the template as shown below. 
    1055             out_f.write(line + '\n') 
    1056             if line.count('#name'): 
    1057                 out_f.write('name = "%s" \n' % name) 
    1058             elif line.count('#title'): 
    1059                 out_f.write('title = "User model for %s"\n' % name) 
    1060             elif line.count('#description'): 
    1061                 out_f.write('description = "%s"\n' % desc_str) 
    1062             elif line.count('#parameters'): 
    1063                 out_f.write('parameters = [ \n') 
    1064                 for param_line in param_str.split('\n'): 
    1065                     p_line = param_line.lstrip().rstrip() 
    1066                     if p_line: 
    1067                         pname, pvalue = self.get_param_helper(p_line) 
    1068                         param_names.append(pname) 
    1069                         out_f.write("%s['%s', '', %s, [-numpy.inf, numpy.inf], '', ''],\n" % (spaces16, pname, pvalue)) 
    1070                 for param_line in pd_param_str.split('\n'): 
    1071                     p_line = param_line.lstrip().rstrip() 
    1072                     if p_line: 
    1073                         pname, pvalue = self.get_param_helper(p_line) 
    1074                         param_names.append(pname) 
    1075                         out_f.write("%s['%s', '', %s, [-numpy.inf, numpy.inf], 'volume', ''],\n" % (spaces16, pname, pvalue)) 
    1076                 out_f.write('%s]\n' % spaces13) 
    1077  
    1078         # No form_volume or ER available in simple model editor 
    1079         out_f.write('def form_volume(*arg): \n') 
    1080         out_f.write('    return 1.0 \n') 
    1081         out_f.write('\n') 
    1082         out_f.write('def ER(*arg): \n') 
    1083         out_f.write('    return 1.0 \n') 
    1084  
    1085         # function to compute 
    1086         out_f.write('\n') 
    1087         out_f.write('def Iq(x ') 
    1088         for name in param_names: 
    1089             out_f.write(', %s' % name) 
    1090         out_f.write('):\n') 
     1029        pd_params = [] 
     1030        out_f.write('parameters = [ \n') 
     1031        out_f.write('#   ["name", "units", default, [lower, upper], "type", "description"],\n') 
     1032        for pname, pvalue in self.get_param_helper(param_str): 
     1033            param_names.append(pname) 
     1034            out_f.write("    ['%s', '', %s, [-inf, inf], '', ''],\n" 
     1035                        % (pname, pvalue)) 
     1036        for pname, pvalue in self.get_param_helper(pd_param_str): 
     1037            param_names.append(pname) 
     1038            pd_params.append(pname) 
     1039            out_f.write("    ['%s', '', %s, [-inf, inf], 'volume', ''],\n" 
     1040                        % (pname, pvalue)) 
     1041        out_f.write('    ]\n') 
     1042 
     1043        # Write out function definition 
     1044        out_f.write('def Iq(%s):\n' % ', '.join(['x'] + param_names)) 
     1045        out_f.write('    """Absolute scattering"""\n') 
     1046        if "scipy." in func_str: 
     1047            out_f.write('    import scipy') 
     1048        if "numpy." in func_str: 
     1049            out_f.write('    import numpy') 
     1050        if "np." in func_str: 
     1051            out_f.write('    import numpy as np') 
    10911052        for func_line in func_str.split('\n'): 
    10921053            out_f.write('%s%s\n' % (spaces4, func_line)) 
    1093  
    1094         Iqxy_string = 'return Iq(numpy.sqrt(x**2+y**2) ' 
    1095  
     1054        out_f.write('## uncomment the following if Iq works for vector x\n') 
     1055        out_f.write('#Iq.vectorized = True\n') 
     1056 
     1057        # If polydisperse, create place holders for form_volume, ER and VR 
     1058        if pd_params: 
     1059            out_f.write('\n') 
     1060            out_f.write(CUSTOM_TEMPLATE_PD % {'args': ', '.join(pd_params)}) 
     1061 
     1062        # Create place holder for Iqxy 
    10961063        out_f.write('\n') 
    1097         out_f.write('def Iqxy(x, y ') 
    1098         for name in param_names: 
    1099             out_f.write(', %s' % name) 
    1100             Iqxy_string += ', ' + name 
    1101         out_f.write('):\n') 
    1102         Iqxy_string += ')' 
    1103         out_f.write('%s%s\n' % (spaces4, Iqxy_string)) 
     1064        out_f.write('#def Iqxy(%s):\n' % ', '.join(["x", "y"] + param_names)) 
     1065        out_f.write('#    """Absolute scattering of oriented particles."""\n') 
     1066        out_f.write('#    ...\n') 
     1067        out_f.write('#    return oriented_form(x, y, args)\n') 
     1068        out_f.write('## uncomment the following if Iqxy works for vector x, y\n') 
     1069        out_f.write('#Iqxy.vectorized = True\n') 
    11041070 
    11051071        out_f.close() 
    11061072 
    1107     def get_param_helper(self, line): 
    1108         """ 
    1109         Get string in line to define the params dictionary 
    1110  
    1111         :param line: one line of string got from the param_str 
    1112         """ 
    1113         items = line.split(";") 
    1114         for item in items: 
    1115             name = item.split("=")[0].lstrip().rstrip() 
    1116             try: 
    1117                 value = item.split("=")[1].lstrip().rstrip() 
    1118                 float(value) 
    1119             except: 
    1120                 value = 1.0 # default 
    1121  
    1122         return name, value 
     1073    def get_param_helper(self, param_str): 
     1074        """ 
     1075        yield a sequence of name, value pairs for the parameters in param_str 
     1076 
     1077        Parameters can be defined by one per line by name=value, or multiple 
     1078        on the same line by separating the pairs by semicolon or comma.  The 
     1079        value is optional and defaults to "1.0". 
     1080        """ 
     1081        for line in param_str.replace(';', ',').split('\n'): 
     1082            for item in line.split(','): 
     1083                parts = item.plit('=') 
     1084                name = parts[0].strip() 
     1085                value = parts[1] if len(parts) > 0 else '1.0' 
     1086                if name: 
     1087                    yield name, value 
    11231088 
    11241089    def set_function_helper(self, line): 
     
    11541119        running "file:///...." 
    11551120 
    1156     :param evt: Triggers on clicking the help button 
    1157     """ 
     1121        :param evt: Triggers on clicking the help button 
     1122        """ 
    11581123 
    11591124        _TreeLocation = "user/sasgui/perspectives/fitting/fitting_help.html" 
     
    11981163## Templates for plugin models 
    11991164 
    1200 CUSTOM_TEMPLATE = """ 
     1165CUSTOM_TEMPLATE = '''\ 
     1166r""" 
     1167Definition 
     1168---------- 
     1169 
     1170Calculates %(name)s. 
     1171 
     1172%(description)s 
     1173 
     1174References 
     1175---------- 
     1176 
     1177Authorship and Verification 
     1178--------------------------- 
     1179 
     1180* **Author:** --- **Date:** %(date)s 
     1181* **Last Modified by:** --- **Date:** %(date)s 
     1182* **Last Reviewed by:** --- **Date:** %(date)s 
     1183""" 
     1184 
    12011185from math import * 
    1202 import os 
    1203 import sys 
    1204 import numpy 
    1205  
    1206 #name 
    1207  
    1208 #title 
    1209  
    1210 #description 
    1211  
    1212 #parameters 
    1213  
    1214 """ 
    1215  
    1216 CUSTOM_2D_TEMP = """ 
    1217     def run(self, x=0.0, y=0.0): 
    1218         if x.__class__.__name__ == 'list': 
    1219             x_val = x[0] 
    1220             y_val = y[0]*0.0 
    1221             return self.function(x_val, y_val) 
    1222         elif x.__class__.__name__ == 'tuple': 
    1223             msg = "Tuples are not allowed as input to BaseComponent models" 
    1224             raise ValueError, msg 
    1225         else: 
    1226             return self.function(x, 0.0) 
    1227     def runXY(self, x=0.0, y=0.0): 
    1228         if x.__class__.__name__ == 'list': 
    1229             return self.function(x, y) 
    1230         elif x.__class__.__name__ == 'tuple': 
    1231             msg = "Tuples are not allowed as input to BaseComponent models" 
    1232             raise ValueError, msg 
    1233         else: 
    1234             return self.function(x, y) 
    1235     def evalDistribution(self, qdist): 
    1236         if qdist.__class__.__name__ == 'list': 
    1237             msg = "evalDistribution expects a list of 2 ndarrays" 
    1238             if len(qdist)!=2: 
    1239                 raise RuntimeError, msg 
    1240             if qdist[0].__class__.__name__ != 'ndarray': 
    1241                 raise RuntimeError, msg 
    1242             if qdist[1].__class__.__name__ != 'ndarray': 
    1243                 raise RuntimeError, msg 
    1244             v_model = numpy.vectorize(self.runXY, otypes=[float]) 
    1245             iq_array = v_model(qdist[0], qdist[1]) 
    1246             return iq_array 
    1247         elif qdist.__class__.__name__ == 'ndarray': 
    1248             v_model = numpy.vectorize(self.runXY, otypes=[float]) 
    1249             iq_array = v_model(qdist) 
    1250             return iq_array 
    1251 """ 
    1252 TEST_TEMPLATE = """ 
    1253 ###################################################################### 
    1254 ## THIS IS FOR TEST. DO NOT MODIFY THE FOLLOWING LINES!!!!!!!!!!!!!!!! 
    1255 if __name__ == "__main__": 
    1256     m= Model() 
    1257     out1 = m.runXY(0.0) 
    1258     out2 = m.runXY(0.01) 
    1259     isfine1 = numpy.isfinite(out1) 
    1260     isfine2 = numpy.isfinite(out2) 
    1261     print "Testing the value at Q = 0.0:" 
    1262     print out1, " : finite? ", isfine1 
    1263     print "Testing the value at Q = 0.01:" 
    1264     print out2, " : finite? ", isfine2 
    1265     if isfine1 and isfine2: 
    1266         print "===> Simple Test: Passed!" 
    1267     else: 
    1268         print "===> Simple Test: Failed!" 
    1269 """ 
     1186from numpy import inf 
     1187 
     1188name = "%(name)s" 
     1189title = "%(title)s" 
     1190description = """%(description)s""" 
     1191 
     1192''' 
     1193 
     1194CUSTOM_TEMPLATE_PD = '''\ 
     1195def form_volume(%(args)s): 
     1196    """ 
     1197    Volume of the particles used to compute absolute scattering intensity 
     1198    and to weight polydisperse parameter contributions. 
     1199    """ 
     1200    return 0.0 
     1201 
     1202def ER(%(args)s): 
     1203    """ 
     1204    Effective radius of particles to be used when computing structure factors. 
     1205 
     1206    Input parameters are vectors ranging over the mesh of polydispersity values. 
     1207    """ 
     1208    return 0.0 
     1209 
     1210def VR(%(args)s): 
     1211    """ 
     1212    Volume ratio of particles to be used when computing structure factors. 
     1213 
     1214    Input parameters are vectors ranging over the mesh of polydispersity values. 
     1215    """ 
     1216    return 1.0 
     1217''' 
     1218 
    12701219SUM_TEMPLATE = """ 
    12711220# A sample of an experimental model function for Sum/Multiply(Pmodel1,Pmodel2) 
     
    15921541    main_app = wx.App() 
    15931542    main_frame = TextDialog(id=1, model_list=["SphereModel", "CylinderModel"], 
    1594                        plugin_dir='../fitting/plugin_models') 
     1543                            plugin_dir='../fitting/plugin_models') 
    15951544    main_frame.ShowModal() 
    15961545    main_app.MainLoop() 
Note: See TracChangeset for help on using the changeset viewer.