source: sasmodels/sasmodels/details.py @ 8d62008

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

remove circular dependency between details/modelinfo; fix compare Calculator type hint

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