source: sasmodels/sasmodels/dll.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.1 KB
Line 
1"""
2C types wrapper for sasview models.
3"""
4
5import ctypes as ct
6from ctypes import c_void_p, c_int, c_double
7
8import numpy as np
9
10from . import gen
11
12from .gen import F32, F64
13
14IQ_ARGS = [c_void_p, c_void_p, c_int, c_void_p, c_double]
15IQXY_ARGS = [c_void_p, c_void_p, c_void_p, c_int, c_void_p, c_double]
16
17class DllModel(object):
18    """
19    ctypes wrapper for a single model.
20
21    *source* and *info* are the model source and interface as returned
22    from :func:`gen.make`.
23
24    *dtype* is the desired model precision.  Any numpy dtype for single
25    or double precision floats will do, such as 'f', 'float32' or 'single'
26    for single and 'd', 'float64' or 'double' for double.  Double precision
27    is an optional extension which may not be available on all devices.
28
29    Call :meth:`release` when done with the kernel.
30    """
31    def __init__(self, dllpath, info):
32        self.info = info
33        self.dllpath = dllpath
34        self.dll = None
35
36    def _load_dll(self):
37        Nfixed1d = len(self.info['partype']['fixed-1d'])
38        Nfixed2d = len(self.info['partype']['fixed-2d'])
39        Npd1d = len(self.info['partype']['pd-1d'])
40        Npd2d = len(self.info['partype']['pd-2d'])
41
42        self.dll = ct.CDLL(self.dllpath)
43
44        self.Iq = self.dll[gen.kernel_name(self.info, False)]
45        self.Iq.argtypes = IQ_ARGS + [c_double]*Nfixed1d + [c_int]*Npd1d
46
47        self.Iqxy = self.dll[gen.kernel_name(self.info, True)]
48        self.Iqxy.argtypes = IQXY_ARGS + [c_double]*Nfixed2d + [c_int]*Npd2d
49
50    def __getstate__(self):
51        return {'info': self.info, 'dllpath': self.dllpath, 'dll': None}
52
53    def __setstate__(self, state):
54        self.__dict__ = state
55
56    def __call__(self, input):
57        if self.dll is None: self._load_dll()
58
59        kernel = self.Iqxy if input.is_2D else self.Iq
60        return DllKernel(kernel, self.info, input)
61
62    def make_input(self, q_vectors):
63        """
64        Make q input vectors available to the model.
65
66        This only needs to be done once for all models that operate on the
67        same input.  So for example, if you are adding two different models
68        together to compare to a data set, then only one model needs to
69        needs to call make_input, so long as the models have the same dtype.
70        """
71        return DllInput(q_vectors)
72
73    def release(self):
74        pass # TODO: should release the dll
75
76
77class DllInput(object):
78    """
79    Make q data available to the gpu.
80
81    *q_vectors* is a list of q vectors, which will be *[q]* for 1-D data,
82    and *[qx, qy]* for 2-D data.  Internally, the vectors will be reallocated
83    to get the best performance on OpenCL, which may involve shifting and
84    stretching the array to better match the memory architecture.  Additional
85    points will be evaluated with *q=1e-3*.
86
87    *dtype* is the data type for the q vectors. The data type should be
88    set to match that of the kernel, which is an attribute of
89    :class:`GpuProgram`.  Note that not all kernels support double
90    precision, so even if the program was created for double precision,
91    the *GpuProgram.dtype* may be single precision.
92
93    Call :meth:`release` when complete.  Even if not called directly, the
94    buffer will be released when the data object is freed.
95    """
96    def __init__(self, q_vectors):
97        self.nq = q_vectors[0].size
98        self.dtype = np.dtype('double')
99        self.is_2D = (len(q_vectors) == 2)
100        self.q_vectors = [np.ascontiguousarray(q,self.dtype) for q in q_vectors]
101        self.q_pointers = [q.ctypes.data for q in q_vectors]
102
103    def release(self):
104        self.q_vectors = []
105
106class DllKernel(object):
107    """
108    Callable SAS kernel.
109
110    *kernel* is the DllKernel object to call.
111
112    *info* is the module information
113
114    *input* is the DllInput q vectors at which the kernel should be
115    evaluated.
116
117    The resulting call method takes the *pars*, a list of values for
118    the fixed parameters to the kernel, and *pd_pars*, a list of (value,weight)
119    vectors for the polydisperse parameters.  *cutoff* determines the
120    integration limits: any points with combined weight less than *cutoff*
121    will not be calculated.
122
123    Call :meth:`release` when done with the kernel instance.
124    """
125    def __init__(self, kernel, info, input):
126        self.input = input
127        self.kernel = kernel
128        self.res = np.empty(input.nq, input.dtype)
129        dim = '2d' if input.is_2D else '1d'
130        self.fixed_pars = info['partype']['fixed-'+dim]
131        self.pd_pars = info['partype']['pd-'+dim]
132
133        # In dll kernel, but not in opencl kernel
134        self.p_res = self.res.ctypes.data
135
136    def __call__(self, pars, pd_pars, cutoff):
137        real = np.float32 if self.input.dtype == F32 else np.float64
138        fixed = [real(p) for p in pars]
139        cutoff = real(cutoff)
140        loops = np.hstack(pd_pars)
141        loops = np.ascontiguousarray(loops.T, self.input.dtype).flatten()
142        loops_N = [np.uint32(len(p[0])) for p in pd_pars]
143
144        nq = c_int(self.input.nq)
145        p_loops = loops.ctypes.data
146        args = self.input.q_pointers + [self.p_res, nq, p_loops, cutoff] + fixed + loops_N
147        #print pars
148        self.kernel(*args)
149
150        return self.res
151
152    def release(self):
153        pass
Note: See TracBrowser for help on using the repository browser.