source: sasmodels/sasmodels/weights.py @ 5d4777d

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

reorganize, check and update models

  • Property mode set to 100644
File size: 5.3 KB
Line 
1"""
2SAS distributions for polydispersity.
3"""
4# TODO: include dispersion docs with the disperser models
5from math import sqrt
6import numpy as np
7from scipy.special import gammaln
8
9class Dispersion(object):
10    """
11    Base dispersion object.
12
13    Subclasses should define *_weights(center, sigma, lb, ub)*
14    which returns the x points and their corresponding weights.
15    """
16    type = "base disperser"
17    default = dict(npts=35, width=0, nsigmas=3)
18    def __init__(self, npts=None, width=None, nsigmas=None):
19        self.npts = self.default['npts'] if npts is None else npts
20        self.width = self.default['width'] if width is None else width
21        self.nsigmas = self.default['nsigmas'] if nsigmas is None else nsigmas
22
23    def get_pars(self):
24        pars = {'type': self.type}
25        pars.update(self.__dict__)
26        return pars
27
28    def set_weights(self, values, weights):
29        raise RuntimeError("set_weights is only available for ArrayDispersion")
30
31    def get_weights(self, center, lb, ub, relative):
32        """
33        Return the weights for the distribution.
34
35        *center* is the center of the distribution
36
37        *lb*,*ub* are the min and max allowed values
38
39        *relative* is True if the distribution width is proportional to the
40        center value instead of absolute.  For polydispersity use relative.
41        For orientation parameters use absolute.
42        """
43        sigma = self.width * center if relative else self.width
44        if sigma == 0 or self.npts < 2:
45            if lb <= center <= ub:
46                return np.array([center], 'd'), np.array([1.], 'd')
47            else:
48                return np.array([], 'd'), np.array([], 'd')
49        return self._weights(center, sigma, lb, ub)
50
51    def _weights(self, center, sigma, lb, ub):
52        """actual work of computing the weights"""
53        raise NotImplementedError
54
55    def _linspace(self, center, sigma, lb, ub):
56        """helper function to provide linear spaced weight points within range"""
57        npts, nsigmas = self.npts, self.nsigmas
58        x = center + np.linspace(-nsigmas*sigma, +nsigmas*sigma, npts)
59        x = x[(x >= lb) & (x <= ub)]
60        return x
61
62
63class GaussianDispersion(Dispersion):
64    type = "gaussian"
65    default = dict(npts=35, width=0, nsigmas=3)
66    def _weights(self, center, sigma, lb, ub):
67        x = self._linspace(center, sigma, lb, ub)
68        px = np.exp((x-center)**2 / (-2.0 * sigma * sigma))
69        return x, px
70
71
72class RectangleDispersion(Dispersion):
73    type = "rectangle"
74    default = dict(npts=35, width=0, nsigmas=1.70325)
75    def _weights(self, center, sigma, lb, ub):
76        x = self._linspace(center, sigma*sqrt(3.0), lb, ub)
77        px = np.ones_like(x)
78        return x, px
79
80
81class LogNormalDispersion(Dispersion):
82    type = "lognormal"
83    default = dict(npts=80, width=0, nsigmas=8)
84    def _weights(self, center, sigma, lb, ub):
85        x = self._linspace(center, sigma, max(lb,1e-8), max(ub,1e-8))
86        px = np.exp(-0.5*(np.log(x)-center)**2)/sigma**2/(x*sigma)
87        return x, px
88
89
90class SchulzDispersion(Dispersion):
91    type = "schulz"
92    default = dict(npts=80, width=0, nsigmas=8)
93    def _weights(self, center, sigma, lb, ub):
94        x = self._linspace(center, sigma, max(lb,1e-8), max(ub,1e-8))
95        R= x/center
96        z = (center/sigma)**2
97        arg = z*np.log(z) + (z-1)*np.log(R) - R*z - np.log(center) - gammaln(z)
98        px = np.exp(arg)
99        return x, px
100
101
102class ArrayDispersion(Dispersion):
103    type = "array"
104    default = dict(npts=35, width=0, nsigmas=1)
105    def __init__(self, npts=None, width=None, nsigmas=None):
106        Dispersion.__init__(self, npts, width, nsigmas)
107        self.values = np.array([0.], 'd')
108        self.weights = np.array([1.], 'd')
109
110    def set_weights(self, values, weights):
111        self.values = np.ascontiguousarray(values, 'd')
112        self.weights = np.ascontiguousarray(weights, 'd')
113        self.npts = len(values)
114
115    def _weights(self, center, sigma, lb, ub):
116        # TODO: interpolate into the array dispersion using npts
117        x = center + self.values*sigma
118        idx = (x>=lb)&(x<=ub)
119        x = x[idx]
120        px = self.weights[idx]
121        return x, px
122
123
124# dispersion name -> disperser lookup table.
125models = dict((d.type,d) for d in (
126    GaussianDispersion, RectangleDispersion,
127    ArrayDispersion, SchulzDispersion, LogNormalDispersion
128))
129
130
131def get_weights(disperser, n, width, nsigmas, value, limits, relative):
132    """
133    Return the set of values and weights for a polydisperse parameter.
134
135    *disperser* is the name of the disperser.
136
137    *n* is the number of points in the weight vector.
138
139    *width* is the width of the disperser distribution.
140
141    *nsigmas* is the number of sigmas to span for the dispersion convolution.
142
143    *value* is the value of the parameter in the model.
144
145    *limits* is [lb, ub], the lower and upper bound of the weight value.
146
147    *relative* is true if *width* is defined in proportion to the value
148    of the parameter, and false if it is an absolute width.
149
150    Returns *(value,weight)*, where *value* and *weight* are vectors.
151    """
152    cls = models[disperser]
153    obj = cls(n, width, nsigmas)
154    v,w = obj.get_weights(value, limits[0], limits[1], relative)
155    return v,w
156
157# Hack to allow sasview dispersion objects to interoperate with sasmodels
158dispersers = dict((v.__name__,k) for k,v in models.items())
159dispersers['DispersionModel'] = RectangleDispersion.type
160
Note: See TracBrowser for help on using the repository browser.