Ticket #751: sum_p1_p2.py

File sum_p1_p2.py, 16.0 KB (added by richardh, 8 years ago)
Line 
1# A sample of an experimental model function for Sum(Pmodel1,Pmodel2)
2import copy
3from sas.models.pluginmodel import Model1DPlugin
4import os
5import sys
6"""
7## *****************************************************************************
8Please select the 'Compile' from the menubar after the modification and saving.
9Note that we recommend to save the file as a different file name.
10Otherwise, it could be removed in the future on re-installation of the SasView.
11## *****************************************************************************
12"""
13
14# Available model names for this sum model
15"""
16BCCrystalModel, BEPolyelectrolyte, BarBellModel, BinaryHSModel, BroadPeakModel,
17CSParallelepipedModel, CappedCylinderModel, CoreShellCylinderModel,
18CoreShellEllipsoidModel, CoreShellModel, CorrLengthModel, CylinderModel,
19DABModel, DebyeModel, EllipsoidModel, EllipticalCylinderModel, FCCrystalModel,
20FlexCylEllipXModel, FlexibleCylinderModel, FractalCoreShellModel, FractalModel,
21FuzzySphereModel, GaussLorentzGelModel, GuinierModel, GuinierPorodModel,
22HardsphereStructure, HayterMSAStructure, HollowCylinderModel, LamellarFFHGModel,
23LamellarModel, LamellarPCrystalModel, LamellarPSHGModel, LamellarPSModel,
24LineModel, LorentzModel, MultiShellModel, ParallelepipedModel, PeakGaussModel,
25PeakLorentzModel, PearlNecklaceModel, Poly_GaussCoil, PolymerExclVolume,
26PorodModel, PowerLawAbsModel, SCCrystalModel, SphereModel, SquareWellStructure,
27StackedDisksModel, StickyHSStructure, TeubnerStreyModel, TriaxialEllipsoidModel,
28TwoLorentzianModel, TwoPowerLawModel, VesicleModel
29"""
30## This is same as the Easy Custom Sum(p1 + p2)
31#
32#     Custom model = scale_factor * (P1 + P2)
33#
34## User can REPLACE model names below two arrowed lines (two names per line)
35from sas.models.CylinderModel import CylinderModel as P1          #<========
36from sas.models.PolymerExclVolume import PolymerExclVolume as P2  #<========
37
38# If you want to add your custom model, change the filename to your custom model
39# FILE NAME without the extension(.py). And un-comment the line(ie., remove '#')
40
41# from filename import Model as P2      #<========
42
43
44#####DO NOT CHANGE ANYTHING BELOW THIS LINE
45#####---------------------------------------------------------------------------
46class Model(Model1DPlugin):
47    """
48    Use for p1(Q)+p2(Q);
49    Note: P(Q) refers to 'form factor' model.
50    """
51    name = "" 
52    def __init__(self):
53        Model1DPlugin.__init__(self, name=self.name)
54        """
55        :param p_model1: a form factor, P(Q)
56        :param p_model2: another form factor, P(Q)
57        """
58        p_model1 = P1()
59        p_model2 = P2()
60        ## Setting  model name model description
61        self.description=""
62       
63        self.description = p_model1.name+"\n"
64        self.description += p_model2.name+"\n"
65        self.fill_description(p_model1, p_model2)
66        # Set the name same as the file name
67        self.name = self.get_fname()     ##DO NOT CHANGE THIS LINE!!!
68        ## Define parameters
69        self.params = {}
70
71        ## Parameter details [units, min, max]
72        self.details = {}
73       
74        # non-fittable parameters
75        self.non_fittable = p_model1.non_fittable 
76        self.non_fittable += p_model2.non_fittable 
77           
78        ##models
79        self.p_model1= p_model1
80        self.p_model2= p_model2
81       
82       
83        ## dispersion
84        self._set_dispersion()
85        ## Define parameters
86        self._set_params()
87        ## New parameter:Scaling factor
88        self.params['scale_factor'] = 1
89       
90        ## Parameter details [units, min, max]
91        self._set_details()
92        self.details['scale_factor'] = ['',  None, None]
93        ## Magnetic Panrameters
94        self.magnetic_params = []
95       
96        #list of parameter that can be fitted
97        self._set_fixed_params() 
98        ## parameters with orientation
99        for item in self.p_model1.orientation_params:
100            new_item = "p1_" + item
101            if not new_item in self.orientation_params:
102                self.orientation_params.append(new_item)
103           
104        for item in self.p_model2.orientation_params:
105            new_item = "p2_" + item
106            if not new_item in self.orientation_params:
107                self.orientation_params.append(new_item)
108        ## magnetic params
109        for item in self.p_model1.magnetic_params:
110            new_item = "p1_" + item
111            if not new_item in self.magnetic_params:
112                self.magnetic_params.append(new_item)
113           
114        for item in self.p_model2.magnetic_params:
115            new_item = "p2_" + item
116            if not new_item in self.magnetic_params:
117                self.magnetic_params.append(new_item)
118        # set multiplicity 1: muti_func Not supported.
119        multiplicity1 = 1
120        multiplicity2 = 1
121        ## functional multiplicity of the model
122        self.multiplicity1 = multiplicity1 
123        self.multiplicity2 = multiplicity2   
124        self.multiplicity_info = []   
125       
126    def _clone(self, obj):
127        """
128            Internal utility function to copy the internal
129            data members to a fresh copy.
130        """
131        obj.params     = copy.deepcopy(self.params)
132        obj.description     = copy.deepcopy(self.description)
133        obj.details    = copy.deepcopy(self.details)
134        obj.dispersion = copy.deepcopy(self.dispersion)
135        obj.p_model1  = self.p_model1.clone()
136        obj.p_model2  = self.p_model2.clone()
137        #obj = copy.deepcopy(self)
138        return obj
139   
140    def _get_name(self, name1, name2):
141        """
142        Get combined name from two model names
143        """
144        p1_name = self._get_upper_name(name1)
145        if not p1_name:
146            p1_name = name1
147        name = p1_name
148        name += "+"
149        p2_name = self._get_upper_name(name2)
150        if not p2_name:
151            p2_name = name2
152        name += p2_name
153        return name
154   
155    def _get_upper_name(self, name=None):
156        """
157        Get uppercase string from model name
158        """
159        if name == None:
160            return ""
161        upper_name = ""
162        str_name = str(name)
163        for index in range(len(str_name)):
164            if str_name[index].isupper():
165                upper_name += str_name[index]
166        return upper_name
167       
168    def _set_dispersion(self):
169        """
170           combined the two models dispersions
171           Polydispersion should not be applied to s_model
172        """
173        ##set dispersion only from p_model
174        for name , value in self.p_model1.dispersion.iteritems():
175            #if name.lower() not in self.p_model1.orientation_params:
176            new_name = "p1_" + name
177            self.dispersion[new_name]= value
178        for name , value in self.p_model2.dispersion.iteritems():
179            #if name.lower() not in self.p_model2.orientation_params:
180            new_name = "p2_" + name
181            self.dispersion[new_name]= value
182           
183    def function(self, x=0.0): 
184        """
185        """
186        return 0
187                               
188    def getProfile(self):
189        """
190        Get SLD profile of p_model if exists
191       
192        : return: (r, beta) where r is a list of radius of the transition points
193                beta is a list of the corresponding SLD values
194        : Note: This works only for func_shell# = 2 (exp function)
195                and is not supporting for p2
196        """
197        try:
198            x,y = self.p_model1.getProfile()
199        except:
200            x = None
201            y = None
202           
203        return x, y
204   
205    def _set_params(self):
206        """
207            Concatenate the parameters of the two models to create
208            this model parameters
209        """
210
211        for name , value in self.p_model1.params.iteritems():
212            # No 2D-supported
213            #if name not in self.p_model1.orientation_params:
214            new_name = "p1_" + name
215            self.params[new_name]= value
216           
217        for name , value in self.p_model2.params.iteritems():
218            # No 2D-supported
219            #if name not in self.p_model2.orientation_params:
220            new_name = "p2_" + name
221            self.params[new_name]= value
222               
223        # Set "scale" as initializing
224        self._set_scale_factor()
225     
226           
227    def _set_details(self):
228        """
229            Concatenate details of the two models to create
230            this model details
231        """
232        for name ,detail in self.p_model1.details.iteritems():
233            new_name = "p1_" + name
234            #if new_name not in self.orientation_params:
235            self.details[new_name]= detail
236           
237        for name ,detail in self.p_model2.details.iteritems():
238            new_name = "p2_" + name
239            #if new_name not in self.orientation_params:
240            self.details[new_name]= detail
241   
242    def _set_scale_factor(self):
243        """
244        Not implemented
245        """
246        pass
247       
248               
249    def setParam(self, name, value):
250        """
251        Set the value of a model parameter
252   
253        :param name: name of the parameter
254        :param value: value of the parameter
255        """
256        # set param to p1+p2 model
257        self._setParamHelper(name, value)
258       
259        ## setParam to p model
260        model_pre = name.split('_', 1)[0]
261        new_name = name.split('_', 1)[1]
262        if model_pre == "p1":
263            if new_name in self.p_model1.getParamList():
264                self.p_model1.setParam(new_name, value)
265        elif model_pre == "p2":
266             if new_name in self.p_model2.getParamList():
267                self.p_model2.setParam(new_name, value)
268        elif name.lower() == 'scale_factor':
269            self.params['scale_factor'] = value
270        else:
271            raise ValueError, "Model does not contain parameter %s" % name
272           
273    def getParam(self, name):
274        """
275        Set the value of a model parameter
276
277        :param name: name of the parameter
278       
279        """
280        # Look for dispersion parameters
281        toks = name.split('.')
282        if len(toks)==2:
283            for item in self.dispersion.keys():
284                # 2D not supported
285                if item.lower()==toks[0].lower():# and \
286                            #item.lower() not in self.orientation_params \
287                            #and toks[0].lower() not in self.orientation_params:
288                    for par in self.dispersion[item]:
289                        if par.lower() == toks[1].lower():
290                            return self.dispersion[item][par]
291        else:
292            # Look for standard parameter
293            for item in self.params.keys():
294                if item.lower()==name.lower():#and \
295                            #item.lower() not in self.orientation_params \
296                            #and toks[0].lower() not in self.orientation_params:
297                    return self.params[item]
298        return 
299        #raise ValueError, "Model does not contain parameter %s" % name
300       
301    def _setParamHelper(self, name, value):
302        """
303        Helper function to setparam
304        """
305        # Look for dispersion parameters
306        toks = name.split('.')
307        if len(toks)== 2:
308            for item in self.dispersion.keys():
309                if item.lower()== toks[0].lower():# and \
310                            #item.lower() not in self.orientation_params:
311                    for par in self.dispersion[item]:
312                        if par.lower() == toks[1].lower():#and \
313                                #item.lower() not in self.orientation_params:
314                            self.dispersion[item][par] = value
315                            return
316        else:
317            # Look for standard parameter
318            for item in self.params.keys():
319                if item.lower()== name.lower():#and \
320                            #item.lower() not in self.orientation_params:
321                    self.params[item] = value
322                    return
323           
324        raise ValueError, "Model does not contain parameter %s" % name
325             
326   
327    def _set_fixed_params(self):
328        """
329             fill the self.fixed list with the p_model fixed list
330        """
331        for item in self.p_model1.fixed:
332            new_item = "p1" + item
333            self.fixed.append(new_item)
334        for item in self.p_model2.fixed:
335            new_item = "p2" + item
336            self.fixed.append(new_item)
337
338        self.fixed.sort()
339               
340                   
341    def run(self, x = 0.0):
342        """
343        Evaluate the model
344       
345        :param x: input q-value (float or [float, float] as [r, theta])
346        :return: (scattering function value)
347        """
348        self._set_scale_factor()
349        return self.params['scale_factor'] * \
350                (self.p_model1.run(x) + self.p_model2.run(x))
351   
352    def runXY(self, x = 0.0):
353        """
354        Evaluate the model
355       
356        :param x: input q-value (float or [float, float] as [qx, qy])
357        :return: scattering function value
358        """ 
359        self._set_scale_factor()
360        return self.params['scale_factor'] * \
361                (self.p_model1.runXY(x) + self.p_model2.runXY(x))
362   
363    ## Now (May27,10) directly uses the model eval function
364    ## instead of the for-loop in Base Component.
365    def evalDistribution(self, x = []):
366        """
367        Evaluate the model in cartesian coordinates
368       
369        :param x: input q[], or [qx[], qy[]]
370        :return: scattering function P(q[])
371        """
372        self._set_scale_factor()
373        return self.params['scale_factor'] * \
374                    (self.p_model1.evalDistribution(x) + \
375                                 self.p_model2.evalDistribution(x))
376
377    def set_dispersion(self, parameter, dispersion):
378        """
379        Set the dispersion object for a model parameter
380       
381        :param parameter: name of the parameter [string]
382        :dispersion: dispersion object of type DispersionModel
383        """
384        value= None
385        new_pre = parameter.split("_", 1)[0]
386        new_parameter = parameter.split("_", 1)[1]
387        try:
388            if new_pre == 'p1' and \
389                            new_parameter in self.p_model1.dispersion.keys():
390                value= self.p_model1.set_dispersion(new_parameter, dispersion)
391            if new_pre == 'p2' and \
392                             new_parameter in self.p_model2.dispersion.keys():
393                value= self.p_model2.set_dispersion(new_parameter, dispersion)
394            self._set_dispersion()
395            return value
396        except:
397            raise 
398
399    def fill_description(self, p_model1, p_model2):
400        """
401        Fill the description for P(Q)+P(Q)
402        """
403        description = ""
404        description +="This model gives the summation of  %s and %s.\n"% \
405                                        ( p_model1.name, p_model2.name )
406        self.description += description
407   
408    ## DO NOT MODIFY THE FOLLOWING LINES!!!!!!!!!!!!!!!!       
409    def get_fname(self):
410        """
411        Get the model name same as the file name
412        """
413        path = sys._getframe().f_code.co_filename
414        basename  = os.path.basename(path)
415        name, _ = os.path.splitext(basename)
416        return name
417           
418if __name__ == "__main__": 
419    m1= Model() 
420    #m1.setParam("p1_scale", 25) 
421    #m1.setParam("p1_length", 1000)
422    #m1.setParam("p2_scale", 100)
423    #m1.setParam("p2_rg", 100)
424    out1 = m1.runXY(0.01)
425
426    m2= Model()
427    #m2.p_model1.setParam("scale", 25)
428    #m2.p_model1.setParam("length", 1000)
429    #m2.p_model2.setParam("scale", 100)
430    #m2.p_model2.setParam("rg", 100)
431    out2 = m2.p_model1.runXY(0.01) + m2.p_model2.runXY(0.01)
432    print "Testing at Q = 0.01:"
433    print out1, " = ", out2
434    if out1 == out2:
435        print "===> Simple Test: Passed!"
436    else:
437        print "===> Simple Test: Failed!"