source: sasmodels/sasmodels/sasview_model.py @ a7684e5

core_shell_microgelscostrafo411magnetic_modelrelease_v0.94release_v0.95ticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since a7684e5 was a7684e5, checked in by Paul Kienzle <pkienzle@…>, 10 years ago

docu updates

  • Property mode set to 100644
File size: 11.0 KB
Line 
1import math
2from copy import deepcopy
3
4import numpy as np
5
6def make_class(kernel_module, dtype='single'):
7    from .core import opencl_model
8    model =  opencl_model(kernel_module, dtype=dtype)
9    def __init__(self, multfactor=1):
10        SasviewModel.__init__(self, model)
11    attrs = dict(__init__=__init__)
12    ConstructedModel = type(model.info['name'],  (SasviewModel,), attrs)
13    return ConstructedModel
14
15class SasviewModel(object):
16    """
17    Sasview wrapper for opencl/ctypes model.
18    """
19    def __init__(self, model):
20        """Initialization"""
21        self._model = model
22
23        self.name = model.info['name']
24        self.description = model.info['description']
25        self.category = None
26        self.multiplicity_info = None
27        self.is_multifunc = False
28
29        ## interpret the parameters
30        ## TODO: reorganize parameter handling
31        self.details = dict()
32        self.params = dict()
33        self.dispersion = dict()
34        partype = model.info['partype']
35        for name,units,default,limits,ptype,description in model.info['parameters']:
36            self.params[name] = default
37            self.details[name] = [units]+limits
38
39        for name in partype['pd-2d']:
40            self.dispersion[name] = {
41                'width': 0,
42                'npts': 35,
43                'nsigmas': 3,
44                'type': 'gaussian',
45            }
46
47        self.orientation_params = (
48            partype['orientation']
49            + [n+'.width' for n in partype['orientation']]
50            + partype['magnetic'])
51        self.magnetic_params = partype['magnetic']
52        self.fixed = [n+'.width' for n in partype['pd-2d']]
53        self.non_fittable = []
54
55        ## independent parameter name and unit [string]
56        self.input_name = model.info.get("input_name","Q")
57        self.input_unit = model.info.get("input_unit","A^{-1}")
58        self.output_name = model.info.get("output_name","Intensity")
59        self.output_unit = model.info.get("output_unit","cm^{-1}")
60
61        ## _persistency_dict is used by sans.perspectives.fitting.basepage
62        ## to store dispersity reference.
63        ## TODO: _persistency_dict to persistency_dict throughout sasview
64        self._persistency_dict = {}
65
66        ## New fields introduced for opencl rewrite
67        self.cutoff = 1e-5
68
69    def __str__(self):
70        """
71        :return: string representation
72        """
73        return self.name
74
75    def is_fittable(self, par_name):
76        """
77        Check if a given parameter is fittable or not
78
79        :param par_name: the parameter name to check
80        """
81        return par_name.lower() in self.fixed
82        #For the future
83        #return self.params[str(par_name)].is_fittable()
84
85
86    def getProfile(self):
87        """
88        Get SLD profile
89
90        : return: (z, beta) where z is a list of depth of the transition points
91                beta is a list of the corresponding SLD values
92        """
93        return None, None
94
95    def setParam(self, name, value):
96        """
97        Set the value of a model parameter
98
99        :param name: name of the parameter
100        :param value: value of the parameter
101
102        """
103        # Look for dispersion parameters
104        toks = name.split('.')
105        if len(toks)==2:
106            for item in self.dispersion.keys():
107                if item.lower()==toks[0].lower():
108                    for par in self.dispersion[item]:
109                        if par.lower() == toks[1].lower():
110                            self.dispersion[item][par] = value
111                            return
112        else:
113            # Look for standard parameter
114            for item in self.params.keys():
115                if item.lower()==name.lower():
116                    self.params[item] = value
117                    return
118
119        raise ValueError, "Model does not contain parameter %s" % name
120
121    def getParam(self, name):
122        """
123        Set the value of a model parameter
124
125        :param name: name of the parameter
126
127        """
128        # Look for dispersion parameters
129        toks = name.split('.')
130        if len(toks)==2:
131            for item in self.dispersion.keys():
132                if item.lower()==toks[0].lower():
133                    for par in self.dispersion[item]:
134                        if par.lower() == toks[1].lower():
135                            return self.dispersion[item][par]
136        else:
137            # Look for standard parameter
138            for item in self.params.keys():
139                if item.lower()==name.lower():
140                    return self.params[item]
141
142        raise ValueError, "Model does not contain parameter %s" % name
143
144    def getParamList(self):
145        """
146        Return a list of all available parameters for the model
147        """
148        list = self.params.keys()
149        # WARNING: Extending the list with the dispersion parameters
150        list.extend(self.getDispParamList())
151        return list
152
153    def getDispParamList(self):
154        """
155        Return a list of all available parameters for the model
156        """
157        # TODO: fix test so that parameter order doesn't matter
158        ret = ['%s.%s'%(d.lower(), p)
159               for d in self._model.info['partype']['pd-2d']
160               for p in ('npts', 'nsigmas', 'width')]
161        #print ret
162        return ret
163
164    def clone(self):
165        """ Return a identical copy of self """
166        return deepcopy(self)
167
168    def run(self, x=0.0):
169        """
170        Evaluate the model
171
172        :param x: input q, or [q,phi]
173
174        :return: scattering function P(q)
175
176        **DEPRECATED**: use calculate_Iq instead
177        """
178        if isinstance(x, (list,tuple)):
179            q, phi = x
180            return self.calculate_Iq([q * math.cos(phi)],
181                                     [q * math.sin(phi)])[0]
182        else:
183            return self.calculate_Iq([float(x)])[0]
184
185
186    def runXY(self, x=0.0):
187        """
188        Evaluate the model in cartesian coordinates
189
190        :param x: input q, or [qx, qy]
191
192        :return: scattering function P(q)
193
194        **DEPRECATED**: use calculate_Iq instead
195        """
196        if isinstance(x, (list,tuple)):
197            return self.calculate_Iq([float(x[0])],[float(x[1])])[0]
198        else:
199            return self.calculate_Iq([float(x)])[0]
200
201    def evalDistribution(self, qdist):
202        """
203        Evaluate a distribution of q-values.
204
205        * For 1D, a numpy array is expected as input: ::
206
207            evalDistribution(q)
208
209          where q is a numpy array.
210
211        * For 2D, a list of numpy arrays are expected: [qx,qy],
212          with 1D arrays::
213
214              qx = [ qx[0], qx[1], qx[2], ....]
215
216          and::
217
218              qy = [ qy[0], qy[1], qy[2], ....]
219
220        Then get ::
221
222            q = numpy.sqrt(qx^2+qy^2)
223
224        that is a qr in 1D array::
225
226            q = [q[0], q[1], q[2], ....]
227
228
229        :param qdist: ndarray of scalar q-values or list [qx,qy] where qx,qy are 1D ndarrays
230        """
231        if isinstance(qdist, (list,tuple)):
232            # Check whether we have a list of ndarrays [qx,qy]
233            qx, qy = qdist
234            return self.calculate_Iq(qx, qy)
235
236        elif isinstance(qdist, np.ndarray):
237            # We have a simple 1D distribution of q-values
238            return self.calculate_Iq(qdist)
239
240        else:
241            raise TypeError("evalDistribution expects q or [qx, qy], not %r"%type(qdist))
242
243    def calculate_Iq(self, *args):
244        q_vectors = [np.asarray(q) for q in args]
245        fn = self._model(self._model.make_input(q_vectors))
246        pars = [self.params[v] for v in fn.fixed_pars]
247        pd_pars = [self._get_weights(p) for p in fn.pd_pars]
248        result = fn(pars, pd_pars, self.cutoff)
249        fn.input.release()
250        fn.release()
251        return result
252
253    def calculate_ER(self):
254        """
255        Calculate the effective radius for P(q)*S(q)
256
257        :return: the value of the effective radius
258        """
259        ER = self._model.info.get('ER', None)
260        if ER is None:
261            return 1.0
262        else:
263            vol_pars = self._model.info['partype']['volume']
264            values, weights = self._dispersion_mesh(vol_pars)
265            fv = ER(*values)
266            #print values[0].shape, weights.shape, fv.shape
267            return np.sum(weights*fv) / np.sum(weights)
268
269    def calculate_VR(self):
270        """
271        Calculate the volf ratio for P(q)*S(q)
272
273        :return: the value of the volf ratio
274        """
275        VR = self._model.info.get('VR', None)
276        if VR is None:
277            return 1.0
278        else:
279            vol_pars = self._model.info['partype']['volume']
280            values, weights = self._dispersion_mesh(vol_pars)
281            whole,part = VR(*values)
282            return np.sum(weights*part)/np.sum(weights*whole)
283
284    def set_dispersion(self, parameter, dispersion):
285        """
286        Set the dispersion object for a model parameter
287
288        :param parameter: name of the parameter [string]
289        :param dispersion: dispersion object of type Dispersion
290        """
291        if parameter.lower() in (s.lower() for s in self.params.keys()):
292            # TODO: Store the disperser object directly in the model.
293            # The current method of creating one on the fly whenever it is
294            # needed is kind of funky.
295            # Note: can't seem to get disperser parameters from sasview
296            # (1) Could create a sasview model that has not yet # been
297            # converted, assign the disperser to one of its polydisperse
298            # parameters, then retrieve the disperser parameters from the
299            # sasview model.  (2) Could write a disperser parameter retriever
300            # in sasview.  (3) Could modify sasview to use sasmodels.weights
301            # dispersers.
302            # For now, rely on the fact that the sasview only ever uses
303            # new dispersers in the set_dispersion call and create a new
304            # one instead of trying to assign parameters.
305            from . import weights
306            disperser = weights.dispersers[dispersion.__class__.__name__]
307            dispersion = weights.models[disperser]()
308            self.dispersion[parameter] = dispersion.get_pars()
309        else:
310            raise ValueError("%r is not a dispersity or orientation parameter")
311
312    def _dispersion_mesh(self, pars):
313        """
314        Create a mesh grid of dispersion parameters and weights.
315
316        Returns [p1,p2,...],w where pj is a vector of values for parameter j
317        and w is a vector containing the products for weights for each
318        parameter set in the vector.
319        """
320        values, weights = zip(*[self._get_weights(p) for p in pars])
321        values = [v.flatten() for v in np.meshgrid(*values)]
322        weights = np.vstack([v.flatten() for v in np.meshgrid(*weights)])
323        weights = np.prod(weights, axis=0)
324        return values, weights
325
326    def _get_weights(self, par):
327        from . import weights
328
329        relative = self._model.info['partype']['pd-rel']
330        limits = self._model.info['limits']
331        dis = self.dispersion[par]
332        v,w = weights.get_weights(
333            dis['type'], dis['npts'], dis['width'], dis['nsigmas'],
334            self.params[par], limits[par], par in relative)
335        return v,w/w.max()
336
Note: See TracBrowser for help on using the repository browser.