source: sasmodels/sasmodels/weights.py @ ff7119b

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

docu update

  • Property mode set to 100644
File size: 5.2 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:
45            return np.array([center], 'd'), np.array([1.], 'd')
46        return self._weights(center, sigma, lb, ub)
47
48    def _weights(self, center, sigma, lb, ub):
49        """actual work of computing the weights"""
50        raise NotImplementedError
51
52    def _linspace(self, center, sigma, lb, ub):
53        """helper function to provide linear spaced weight points within range"""
54        npts, nsigmas = self.npts, self.nsigmas
55        x = center + np.linspace(-nsigmas*sigma, +nsigmas*sigma, npts)
56        x = x[(x >= lb) & (x <= ub)]
57        return x
58
59
60class GaussianDispersion(Dispersion):
61    type = "gaussian"
62    default = dict(npts=35, width=0, nsigmas=3)
63    def _weights(self, center, sigma, lb, ub):
64        x = self._linspace(center, sigma, lb, ub)
65        px = np.exp((x-center)**2 / (-2.0 * sigma * sigma))
66        return x, px
67
68
69class RectangleDispersion(Dispersion):
70    type = "rectangle"
71    default = dict(npts=35, width=0, nsigmas=1.70325)
72    def _weights(self, center, sigma, lb, ub):
73        x = self._linspace(center, sigma*sqrt(3.0), lb, ub)
74        px = np.ones_like(x)
75        return x, px
76
77
78class LogNormalDispersion(Dispersion):
79    type = "lognormal"
80    default = dict(npts=80, width=0, nsigmas=8)
81    def _weights(self, center, sigma, lb, ub):
82        x = self._linspace(center, sigma, max(lb,1e-8), max(ub,1e-8))
83        px = np.exp(-0.5*(np.log(x)-center)**2)/sigma**2/(x*sigma)
84        return x, px
85
86
87class SchulzDispersion(Dispersion):
88    type = "schulz"
89    default = dict(npts=80, width=0, nsigmas=8)
90    def _weights(self, center, sigma, lb, ub):
91        x = self._linspace(center, sigma, max(lb,1e-8), max(ub,1e-8))
92        R= x/center
93        z = (center/sigma)**2
94        arg = z*np.log(z) + (z-1)*np.log(R) - R*z - np.log(center) - gammaln(z)
95        px = np.exp(arg)
96        return x, px
97
98
99class ArrayDispersion(Dispersion):
100    type = "array"
101    default = dict(npts=35, width=0, nsigmas=1)
102    def __init__(self, npts=None, width=None, nsigmas=None):
103        Dispersion.__init__(self, npts, width, nsigmas)
104        self.values = np.array([0.], 'd')
105        self.weights = np.array([1.], 'd')
106
107    def set_weights(self, values, weights):
108        self.values = np.ascontiguousarray(values, 'd')
109        self.weights = np.ascontiguousarray(weights, 'd')
110        self.npts = len(values)
111
112    def _weights(self, center, sigma, lb, ub):
113        # TODO: interpolate into the array dispersion using npts
114        x = center + self.values*sigma
115        idx = (x>=lb)&(x<=ub)
116        x = x[idx]
117        px = self.weights[idx]
118        return x, px
119
120
121# dispersion name -> disperser lookup table.
122models = dict((d.type,d) for d in (
123    GaussianDispersion, RectangleDispersion,
124    ArrayDispersion, SchulzDispersion, LogNormalDispersion
125))
126
127
128def get_weights(disperser, n, width, nsigmas, value, limits, relative):
129    """
130    Return the set of values and weights for a polydisperse parameter.
131
132    *disperser* is the name of the disperser.
133
134    *n* is the number of points in the weight vector.
135
136    *width* is the width of the disperser distribution.
137
138    *nsigmas* is the number of sigmas to span for the dispersion convolution.
139
140    *value* is the value of the parameter in the model.
141
142    *limits* is [lb, ub], the lower and upper bound of the weight value.
143
144    *relative* is true if *width* is defined in proportion to the value
145    of the parameter, and false if it is an absolute width.
146
147    Returns *(value,weight)*, where *value* and *weight* are vectors.
148    """
149    cls = models[disperser]
150    obj = cls(n, width, nsigmas)
151    v,w = obj.get_weights(value, limits[0], limits[1], relative)
152    return v,w
153
154# Hack to allow sasview dispersion objects to interoperate with sasmodels
155dispersers = dict((v.__name__,k) for k,v in models.items())
156dispersers['DispersionModel'] = RectangleDispersion.type
157
Note: See TracBrowser for help on using the repository browser.