source: sasmodels/sasmodels/details.py @ 4bfd277

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

support ER/VR calls with vector parameters

  • Property mode set to 100644
File size: 5.8 KB
Line 
1import numpy as np
2
3class CallDetails(object):
4    def __init__(self, model_info):
5        parameters = model_info.parameters
6        max_pd = parameters.max_pd
7        npars = parameters.npars
8        par_offset = 4*max_pd
9        self._details = np.zeros(par_offset + 3*npars + 4, 'i4')
10
11        # generate views on different parts of the array
12        self._pd_par     = self._details[0*max_pd:1*max_pd]
13        self._pd_length  = self._details[1*max_pd:2*max_pd]
14        self._pd_offset  = self._details[2*max_pd:3*max_pd]
15        self._pd_stride  = self._details[3*max_pd:4*max_pd]
16        self._par_offset = self._details[par_offset+0*npars:par_offset+1*npars]
17        self._par_coord  = self._details[par_offset+1*npars:par_offset+2*npars]
18        self._pd_coord   = self._details[par_offset+2*npars:par_offset+3*npars]
19
20        # theta_par is fixed
21        self._details[-1] = parameters.theta_offset
22
23    @property
24    def ctypes(self): return self._details.ctypes
25
26    @property
27    def pd_par(self): return self._pd_par
28
29    @property
30    def pd_length(self): return self._pd_length
31
32    @property
33    def pd_offset(self): return self._pd_offset
34
35    @property
36    def pd_stride(self): return self._pd_stride
37
38    @property
39    def pd_coord(self): return self._pd_coord
40
41    @property
42    def par_coord(self): return self._par_coord
43
44    @property
45    def par_offset(self): return self._par_offset
46
47    @property
48    def num_active(self): return self._details[-4]
49    @num_active.setter
50    def num_active(self, v): self._details[-4] = v
51
52    @property
53    def total_pd(self): return self._details[-3]
54    @total_pd.setter
55    def total_pd(self, v): self._details[-3] = v
56
57    @property
58    def num_coord(self): return self._details[-2]
59    @num_coord.setter
60    def num_coord(self, v): self._details[-2] = v
61
62    @property
63    def theta_par(self): return self._details[-1]
64
65    def show(self):
66        print("total_pd", self.total_pd)
67        print("num_active", self.num_active)
68        print("pd_par", self.pd_par)
69        print("pd_length", self.pd_length)
70        print("pd_offset", self.pd_offset)
71        print("pd_stride", self.pd_stride)
72        print("par_offsets", self.par_offset)
73        print("num_coord", self.num_coord)
74        print("par_coord", self.par_coord)
75        print("pd_coord", self.pd_coord)
76        print("theta par", self._details[-1])
77
78def build_details(kernel, pairs):
79    values, weights = zip(*pairs)
80    if max([len(w) for w in weights]) > 1:
81        call_details = poly_details(kernel.info, weights)
82    else:
83        call_details = kernel.info.mono_details
84    weights, values = [np.hstack(v) for v in (weights, values)]
85    weights = weights.astype(dtype=kernel.dtype)
86    values = values.astype(dtype=kernel.dtype)
87    return call_details, weights, values
88
89def mono_details(model_info):
90    call_details = CallDetails(model_info)
91    # The zero defaults for monodisperse systems are mostly fine
92    call_details.par_offset[:] = np.arange(2, len(call_details.par_offset)+2)
93    return call_details
94
95def poly_details(model_info, weights):
96    #print("weights",weights)
97    weights = weights[2:] # Skip scale and background
98
99    # Decreasing list of polydispersity lengths
100    # Note: the reversing view, x[::-1], does not require a copy
101    pd_length = np.array([len(w) for w in weights])
102    num_active = np.sum(pd_length>1)
103    if num_active > model_info.parameters.max_pd:
104        raise ValueError("Too many polydisperse parameters")
105
106    pd_offset = np.cumsum(np.hstack((0, pd_length)))
107    idx = np.argsort(pd_length)[::-1][:num_active]
108    par_length = np.array([max(len(w),1) for w in weights])
109    pd_stride = np.cumprod(np.hstack((1, par_length[idx])))
110    par_offsets = np.cumsum(np.hstack((2, par_length)))
111
112    call_details = CallDetails(model_info)
113    call_details.pd_par[:num_active] = idx
114    call_details.pd_length[:num_active] = pd_length[idx]
115    call_details.pd_offset[:num_active] = pd_offset[idx]
116    call_details.pd_stride[:num_active] = pd_stride[:-1]
117    call_details.par_offset[:] = par_offsets[:-1]
118    call_details.total_pd = pd_stride[-1]
119    call_details.num_active = num_active
120    # Without constraints coordinated parameters are just the pd parameters
121    call_details.par_coord[:num_active] = idx
122    call_details.pd_coord[:num_active] = 2**np.arange(num_active)
123    call_details.num_coord = num_active
124    #call_details.show()
125    return call_details
126
127def constrained_poly_details(model_info, weights, constraints):
128    # Need to find the independently varying pars and sort them
129    # Need to build a coordination list for the dependent variables
130    # Need to generate a constraints function which takes values
131    # and weights, returning par blocks
132    raise NotImplementedError("Can't handle constraints yet")
133
134
135try:
136    np.meshgrid([])
137    meshgrid = np.meshgrid
138except ValueError:
139    # CRUFT: np.meshgrid requires multiple vectors
140    def meshgrid(*args):
141        if len(args) > 1:
142            return np.meshgrid(*args)
143        else:
144            return [np.asarray(v) for v in args]
145
146def dispersion_mesh(model_info, pars):
147    """
148    Create a mesh grid of dispersion parameters and weights.
149
150    Returns [p1,p2,...],w where pj is a vector of values for parameter j
151    and w is a vector containing the products for weights for each
152    parameter set in the vector.
153    """
154    value, weight = zip(*pars)
155    weight = [w if w else [1.] for w in weight]
156    weight = np.vstack([v.flatten() for v in meshgrid(*weight)])
157    weight = np.prod(weight, axis=0)
158    value = [v.flatten() for v in meshgrid(*value)]
159    lengths = [par.length for par in model_info.parameters.kernel_parameters
160               if par.type == 'volume']
161    if any(n > 1 for n in lengths):
162        pars = []
163        offset = 0
164        for n in lengths:
165            pars.append(np.vstack(value[offset:offset+n]) if n > 1 else value[offset])
166            offset += n
167        value = pars
168    return value, weight
169
170
Note: See TracBrowser for help on using the repository browser.