source: sasmodels/sasmodels/bumps_model.py @ ec7e360

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

refactor option processing for compare.py, allowing more flexible selection of calculation engines

  • Property mode set to 100644
File size: 5.1 KB
Line 
1"""
2Wrap sasmodels for direct use by bumps.
3
4:class:`Model` is a wrapper for the sasmodels kernel which defines a
5bumps *Parameter* box for each kernel parameter.  *Model* accepts keyword
6arguments to set the initial value for each parameter.
7
8:class:`Experiment` combines the *Model* function with a data file loaded by the
9sasview data loader.  *Experiment* takes a *cutoff* parameter controlling
10how far the polydispersity integral extends.
11
12"""
13
14import warnings
15
16import numpy as np
17
18from .data import plot_theory
19from .direct_model import DataMixin
20
21# CRUFT: old style bumps wrapper which doesn't separate data and model
22def BumpsModel(data, model, cutoff=1e-5, **kw):
23    warnings.warn("Use of BumpsModel is deprecated.  Use bumps_model.Experiment instead.")
24    model = Model(model, **kw)
25    experiment = Experiment(data=data, model=model, cutoff=cutoff)
26    for k in model._parameter_names:
27        setattr(experiment, k, getattr(model, k))
28    return experiment
29
30def create_parameters(model_info, **kwargs):
31    # lazy import; this allows the doc builder and nosetests to run even
32    # when bumps is not on the path.
33    from bumps.names import Parameter
34
35    partype = model_info['partype']
36
37    pars = {}
38    for p in model_info['parameters']:
39        name, default, limits = p[0], p[2], p[3]
40        value = kwargs.pop(name, default)
41        pars[name] = Parameter.default(value, name=name, limits=limits)
42    for name in partype['pd-2d']:
43        for xpart, xdefault, xlimits in [
44            ('_pd', 0., limits),
45            ('_pd_n', 35., (0, 1000)),
46            ('_pd_nsigma', 3., (0, 10)),
47        ]:
48            xname = name + xpart
49            xvalue = kwargs.pop(xname, xdefault)
50            pars[xname] = Parameter.default(xvalue, name=xname, limits=xlimits)
51
52    pd_types = {}
53    for name in partype['pd-2d']:
54        xname = name + '_pd_type'
55        xvalue = kwargs.pop(xname, 'gaussian')
56        pd_types[xname] = xvalue
57
58    if kwargs:  # args not corresponding to parameters
59        raise TypeError("unexpected parameters: %s"
60                        % (", ".join(sorted(kwargs.keys()))))
61
62    return pars, pd_types
63
64class Model(object):
65    def __init__(self, model, **kwargs):
66        self._sasmodel = model
67        pars, pd_types = create_parameters(model.info, **kwargs)
68        for k,v in pars.items():
69            setattr(self, k, v)
70        for k,v in pd_types.items():
71            setattr(self, k, v)
72        self._parameter_names = list(pars.keys())
73        self._pd_type_names = list(pd_types.keys())
74
75    def parameters(self):
76        """
77        Return a dictionary of parameters
78        """
79        return dict((k, getattr(self, k)) for k in self._parameter_names)
80
81    def state(self):
82        pars = dict((k, getattr(self, k).value) for k in self._parameter_names)
83        pars.update((k, getattr(self, k)) for k in self._pd_type_names)
84        return pars
85
86class Experiment(DataMixin):
87    """
88    Return a bumps wrapper for a SAS model.
89
90    *data* is the data to be fitted.
91
92    *model* is the SAS model from :func:`core.load_model`.
93
94    *cutoff* is the integration cutoff, which avoids computing the
95    the SAS model where the polydispersity weight is low.
96
97    Model parameters can be initialized with additional keyword
98    arguments, or by assigning to model.parameter_name.value.
99
100    The resulting bumps model can be used directly in a FitProblem call.
101    """
102    def __init__(self, data, model, cutoff=1e-5):
103
104        # remember inputs so we can inspect from outside
105        self.model = model
106        self.cutoff = cutoff
107        self._interpret_data(data, model._sasmodel)
108        self.update()
109
110    def update(self):
111        self._cache = {}
112
113    def numpoints(self):
114        """
115            Return the number of points
116        """
117        return len(self.Iq)
118
119    def parameters(self):
120        """
121        Return a dictionary of parameters
122        """
123        return self.model.parameters()
124
125    def theory(self):
126        if 'theory' not in self._cache:
127            pars = self.model.state()
128            self._cache['theory'] = self._calc_theory(pars, cutoff=self.cutoff)
129        return self._cache['theory']
130
131    def residuals(self):
132        #if np.any(self.err ==0): print("zeros in err")
133        return (self.theory() - self.Iq) / self.dIq
134
135    def nllf(self):
136        delta = self.residuals()
137        #if np.any(np.isnan(R)): print("NaN in residuals")
138        return 0.5 * np.sum(delta ** 2)
139
140    #def __call__(self):
141    #    return 2 * self.nllf() / self.dof
142
143    def plot(self, view='log'):
144        """
145        Plot the data and residuals.
146        """
147        data, theory, resid = self._data, self.theory(), self.residuals()
148        plot_theory(data, theory, resid, view)
149
150    def simulate_data(self, noise=None):
151        Iq = self.theory()
152        self._set_data(Iq, noise)
153
154    def save(self, basename):
155        pass
156
157    def __getstate__(self):
158        # Can't pickle gpu functions, so instead make them lazy
159        state = self.__dict__.copy()
160        state['_kernel'] = None
161        return state
162
163    def __setstate__(self, state):
164        # pylint: disable=attribute-defined-outside-init
165        self.__dict__ = state
Note: See TracBrowser for help on using the repository browser.