source: sasview/src/sas/models/MultiplicationModel.py @ 3654779

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 3654779 was 79492222, checked in by krzywon, 10 years ago

Changed the file and folder names to remove all SANS references.

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