source: sasview/sansmodels/src/sans/models/MultiplicationModel.py @ 2a66329

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 2a66329 was a8a55f2, checked in by Jae Cho <jhjcho@…>, 13 years ago

minor change: reorder the if statement

  • Property mode set to 100644
File size: 11.1 KB
RevLine 
[c9636f7]1
2from sans.models.BaseComponent import BaseComponent
3import numpy, math
[a68efd1]4import copy
[3740b11]5from sans.models.pluginmodel import Model1DPlugin
[c9636f7]6class MultiplicationModel(BaseComponent):
7    """
[1affe64]8        Use for P(Q)*S(Q); function call must be in the order of P(Q) and then S(Q):
[c52f66f]9        The model parameters are combined from both models, P(Q) and S(Q), except 1) 'effect_radius' of S(Q)
10        which will be calculated from P(Q) via calculate_ER(),
11        and 2) 'scale' in P model which is synchronized w/ volfraction in S
12        then P*S is multiplied by a new param, 'scale_factor'.
[1affe64]13        The polydispersion is applicable only to P(Q), not to S(Q).
14        Note: P(Q) refers to 'form factor' model while S(Q) does to 'structure factor'.
[c9636f7]15    """
[1affe64]16    def __init__(self, p_model, s_model ):
[c9636f7]17        BaseComponent.__init__(self)
[1affe64]18        """
19            @param p_model: form factor, P(Q)
20            @param s_model: structure factor, S(Q)
21        """
[c9636f7]22
[8cfdd5e]23        ## Setting  model name model description
[996fd35]24        self.description=""
[1affe64]25        self.name = p_model.name +" * "+ s_model.name
26        self.description= self.name+"\n"
27        self.fill_description(p_model, s_model)
[c52f66f]28
29        ## Define parameters
30        self.params = {}
31
32        ## Parameter details [units, min, max]
33        self.details = {}
[35aface]34       
[1affe64]35        ##models
36        self.p_model= p_model
[8960479]37        self.s_model= s_model       
[5eb9154]38       
[c9636f7]39        ## dispersion
40        self._set_dispersion()
41        ## Define parameters
42        self._set_params()
[c52f66f]43        ## New parameter:Scaling factor
44        self.params['scale_factor'] = 1
45       
[c9636f7]46        ## Parameter details [units, min, max]
47        self._set_details()
[c52f66f]48        self.details['scale_factor'] = ['',     None, None]
49       
[c9636f7]50        #list of parameter that can be fitted
51        self._set_fixed_params() 
[5fc8e22]52        ## parameters with orientation
[1affe64]53        for item in self.p_model.orientation_params:
[5fc8e22]54            self.orientation_params.append(item)
55           
[1affe64]56        for item in self.s_model.orientation_params:
[5fc8e22]57            if not item in self.orientation_params:
[8b677ec]58                self.orientation_params.append(item)
[35aface]59        # get multiplicity if model provide it, else 1.
60        try:
61            multiplicity = p_model.multiplicity
62        except:
63            multiplicity = 1
64        ## functional multiplicity of the model
[8960479]65        self.multiplicity = multiplicity   
66         
67        # non-fittable parameters
68        self.non_fittable = p_model.non_fittable 
69        self.multiplicity_info = [] 
70        self.fun_list = {}
71        if self.non_fittable > 1:
72            try:
73                self.multiplicity_info = p_model.multiplicity_info
74                self.fun_list = p_model.fun_list
75            except:
76                pass
77        else:
78            self.multiplicity_info = []
79           
[a68efd1]80    def _clone(self, obj):
81        """
82            Internal utility function to copy the internal
83            data members to a fresh copy.
84        """
85        obj.params     = copy.deepcopy(self.params)
86        obj.description     = copy.deepcopy(self.description)
87        obj.details    = copy.deepcopy(self.details)
88        obj.dispersion = copy.deepcopy(self.dispersion)
[1affe64]89        obj.p_model  = self.p_model.clone()
90        obj.s_model  = self.s_model.clone()
[fe9c19b4]91        #obj = copy.deepcopy(self)
[a68efd1]92        return obj
93   
94   
[c9636f7]95    def _set_dispersion(self):
96        """
97           combined the two models dispersions
[1affe64]98           Polydispersion should not be applied to s_model
[c9636f7]99        """
[1affe64]100        ##set dispersion only from p_model
101        for name , value in self.p_model.dispersion.iteritems():
[a1b2471]102            self.dispersion[name]= value
103                                     
104    def getProfile(self):
105        """
106        Get SLD profile of p_model if exists
107       
108        : return: (r, beta) where r is a list of radius of the transition points
109                beta is a list of the corresponding SLD values
110        : Note: This works only for func_shell# = 2 (exp function).
111        """
112        try:
113            x,y = self.p_model.getProfile()
114        except:
115            x = None
116            y = None
117           
118        return x, y
119   
[c9636f7]120    def _set_params(self):
121        """
122            Concatenate the parameters of the two models to create
123            this model parameters
124        """
[1affe64]125
126        for name , value in self.p_model.params.iteritems():
[c52f66f]127            if not name in self.params.keys() and name != 'scale':
128                self.params[name]= value
[3740b11]129           
[1affe64]130        for name , value in self.s_model.params.iteritems():
131            #Remove the effect_radius from the (P*S) model parameters.
132            if not name in self.params.keys() and name != 'effect_radius':
133                self.params[name]= value
[c52f66f]134               
135        # Set "scale and effec_radius to P and S model as initializing
136        # since run P*S comes from P and S separately.
137        self._set_scale_factor()
138        self._set_effect_radius()       
[c9636f7]139           
140    def _set_details(self):
141        """
142            Concatenate details of the two models to create
143            this model details
144        """
[1affe64]145        for name ,detail in self.p_model.details.iteritems():
[c52f66f]146            if name != 'scale':
147                self.details[name]= detail
[c9636f7]148           
[1affe64]149        for name , detail in self.s_model.details.iteritems():
[c52f66f]150            if not name in self.details.keys() or name != 'effect_radius':
[1affe64]151                self.details[name]= detail
[c52f66f]152   
153    def _set_scale_factor(self):
154        """
155            Set scale=volfraction to P model
156        """
157        value = self.params['volfraction']
158        if value != None: 
[e08bd5b]159            factor = self.p_model.calculate_VR()
[a8a55f2]160            if factor == None or factor == NotImplemented or factor == 0.0:
[2d6f1f1]161                val= value
162            else:
163                val = value / factor
[a8a55f2]164            self.p_model.setParam('scale', value)
165            self.s_model.setParam('volfraction', val)
[c52f66f]166           
167    def _set_effect_radius(self):
168        """
169            Set effective radius to S(Q) model
170        """
[ccb7363]171        if not 'effect_radius' in self.s_model.params.keys():
172            return
[c52f66f]173        effective_radius = self.p_model.calculate_ER()
174        #Reset the effective_radius of s_model just before the run
175        if effective_radius != None and effective_radius != NotImplemented:
176            self.s_model.setParam('effect_radius',effective_radius)
[c9636f7]177               
[8cfdd5e]178    def setParam(self, name, value):
179        """
180            Set the value of a model parameter
181       
182            @param name: name of the parameter
183            @param value: value of the parameter
184        """
[c52f66f]185        # set param to P*S model
[3740b11]186        self._setParamHelper( name, value)
[c52f66f]187       
188        ## setParam to p model
189        # set 'scale' in P(Q) equal to volfraction
190        if name == 'volfraction':
191            self._set_scale_factor()
192        elif name in self.p_model.getParamList():
[1affe64]193            self.p_model.setParam( name, value)
[c52f66f]194       
195        ## setParam to s model
196        # This is a little bit abundant: Todo: find better way         
197        self._set_effect_radius()
[1affe64]198        if name in self.s_model.getParamList():
[2d6f1f1]199            if name != 'volfraction':
200                self.s_model.setParam( name, value)
[c52f66f]201           
[5eb9154]202
[c52f66f]203        #self._setParamHelper( name, value)
[8cfdd5e]204       
205    def _setParamHelper(self, name, value):
206        """
207            Helper function to setparam
208        """
209        # Look for dispersion parameters
210        toks = name.split('.')
211        if len(toks)==2:
212            for item in self.dispersion.keys():
213                if item.lower()==toks[0].lower():
214                    for par in self.dispersion[item]:
215                        if par.lower() == toks[1].lower():
216                            self.dispersion[item][par] = value
217                            return
218        else:
219            # Look for standard parameter
220            for item in self.params.keys():
221                if item.lower()==name.lower():
222                    self.params[item] = value
223                    return
224           
225        raise ValueError, "Model does not contain parameter %s" % name
226             
227   
[c9636f7]228    def _set_fixed_params(self):
229        """
[1affe64]230             fill the self.fixed list with the p_model fixed list
[c9636f7]231        """
[1affe64]232        for item in self.p_model.fixed:
[c9636f7]233            self.fixed.append(item)
[8b677ec]234
[c9636f7]235        self.fixed.sort()
[5eb9154]236               
237               
[c9636f7]238    def run(self, x = 0.0):
239        """ Evaluate the model
240            @param x: input q-value (float or [float, float] as [r, theta])
[35aface]241            @return: (scattering function value)
[c9636f7]242        """
[c52f66f]243        # set effective radius and scaling factor before run
244        self._set_effect_radius()
245        self._set_scale_factor()
246        return self.params['scale_factor']*self.p_model.run(x)*self.s_model.run(x)
[1affe64]247
[c9636f7]248    def runXY(self, x = 0.0):
249        """ Evaluate the model
250            @param x: input q-value (float or [float, float] as [qx, qy])
[35aface]251            @return: scattering function value
[c52f66f]252        """ 
253        # set effective radius and scaling factor before run
254        self._set_effect_radius()
255        self._set_scale_factor()
256        return self.params['scale_factor']*self.p_model.runXY(x)* self.s_model.runXY(x)
[06c7fcc]257   
258    ## Now (May27,10) directly uses the model eval function
259    ## instead of the for-loop in Base Component.
260    def evalDistribution(self, x = []):
261        """ Evaluate the model in cartesian coordinates
262            @param x: input q[], or [qx[], qy[]]
263            @return: scattering function P(q[])
264        """
265        # set effective radius and scaling factor before run
266        self._set_effect_radius()
267        self._set_scale_factor()
268        return self.params['scale_factor']*self.p_model.evalDistribution(x)* self.s_model.evalDistribution(x)
[5eb9154]269
[c9636f7]270    def set_dispersion(self, parameter, dispersion):
271        """
272            Set the dispersion object for a model parameter
273            @param parameter: name of the parameter [string]
274            @dispersion: dispersion object of type DispersionModel
275        """
[db39b2a]276        value= None
277        try:
[1affe64]278            if parameter in self.p_model.dispersion.keys():
279                value= self.p_model.set_dispersion(parameter, dispersion)
[8077fc4]280            self._set_dispersion()
[db39b2a]281            return value
282        except:
283            raise 
[c9636f7]284
[1affe64]285    def fill_description(self, p_model, s_model):
[8b677ec]286        """
287            Fill the description for P(Q)*S(Q)
288        """
289        description = ""
[1affe64]290        description += "Note:1) The effect_radius (effective radius) of %s \n"% (s_model.name)
[8b677ec]291        description +="             is automatically calculated from size parameters (radius...).\n"
292        description += "         2) For non-spherical shape, this approximation is valid \n"
[1affe64]293        description += "            only for limited systems. Thus, use it at your own risk.\n"
294        description +="See %s description and %s description \n"%( p_model.name, s_model.name )
295        description += "        for details of individual models."
[8b677ec]296        self.description += description
[c9636f7]297   
Note: See TracBrowser for help on using the repository browser.