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

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 a284455 was 35aface, checked in by Jae Cho <jhjcho@…>, 14 years ago

addede new models and attr. non_fittable

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