source: sasview/src/sas/sascalc/fit/MultiplicationModel.py @ 9c391946

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since 9c391946 was cb4ef58, checked in by Paul Kienzle <pkienzle@…>, 9 years ago

remove references to internal _model_info attribute from sasview

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