source: sasmodels/sasmodels/dll.py @ 14de349

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

add autogenerated polydispersity loops

  • Property mode set to 100644
File size: 4.4 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 *meta* 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    def __init__(self, dllpath, meta):
30        self.meta = meta
31        self.dll = ct.CDLL(dllpath)
32        self.Iq = self.dll[gen.kernel_name(self.meta, False)]
33        self.Iqxy = self.dll[gen.kernel_name(self.meta, True)]
34
35
36        self.PARS = dict((p[0],p[2]) for p in meta['parameters'])
37        self.PD_PARS = [p[0] for p in meta['parameters'] if p[4] != ""]
38
39        # Determine the set of fixed and polydisperse parameters
40        Nfixed = len([p[0] for p in meta['parameters'] if p[4] == ""])
41        N1D = len([p for p in meta['parameters'] if p[4]=="volume"])
42        N2D = len([p for p in meta['parameters'] if p[4]!=""])
43        self.Iq.argtypes = IQ_ARGS + [c_double]*Nfixed + [c_int]*N1D
44        self.Iqxy.argtypes = IQXY_ARGS + [c_double]*Nfixed + [c_int]*N2D
45
46    def __call__(self, input, cutoff=1e-5):
47        kernel = self.Iqxy if input.is_2D else self.Iq
48        return DllKernel(kernel, self.meta, input, cutoff)
49
50    def make_input(self, q_vectors):
51        """
52        Make q input vectors available to the model.
53
54        This only needs to be done once for all models that operate on the
55        same input.  So for example, if you are adding two different models
56        together to compare to a data set, then only one model needs to
57        needs to call make_input, so long as the models have the same dtype.
58        """
59        return DllInput(q_vectors)
60
61
62class DllInput(object):
63    """
64    Make q data available to the gpu.
65
66    *q_vectors* is a list of q vectors, which will be *[q]* for 1-D data,
67    and *[qx, qy]* for 2-D data.  Internally, the vectors will be reallocated
68    to get the best performance on OpenCL, which may involve shifting and
69    stretching the array to better match the memory architecture.  Additional
70    points will be evaluated with *q=1e-3*.
71
72    *dtype* is the data type for the q vectors. The data type should be
73    set to match that of the kernel, which is an attribute of
74    :class:`GpuProgram`.  Note that not all kernels support double
75    precision, so even if the program was created for double precision,
76    the *GpuProgram.dtype* may be single precision.
77
78    Call :meth:`release` when complete.  Even if not called directly, the
79    buffer will be released when the data object is freed.
80    """
81    def __init__(self, q_vectors):
82        self.nq = q_vectors[0].size
83        self.dtype = np.dtype('double')
84        self.is_2D = (len(q_vectors) == 2)
85        self.q_vectors = [np.ascontiguousarray(q,self.dtype) for q in q_vectors]
86        self.q_pointers = [q.ctypes.data for q in q_vectors]
87
88    def release(self):
89        self.q_vectors = []
90
91class DllKernel(object):
92    def __init__(self, kernel, meta, input, cutoff):
93        self.cutoff = cutoff
94        self.input = input
95        self.kernel = kernel
96        self.meta = meta
97
98        self.res = np.empty(input.nq, input.dtype)
99        self.p_res = self.res.ctypes.data
100
101        # Determine the set of fixed and polydisperse parameters
102        self.fixed_pars = [p[0] for p in meta['parameters'] if p[4] == ""]
103        self.pd_pars = [p for p in meta['parameters']
104               if p[4]=="volume" or (p[4]=="orientation" and input.is_2D)]
105
106    def eval(self, pars):
107        fixed, loops, loop_n = \
108            gen.kernel_pars(pars, self.meta, self.input.is_2D, dtype=self.input.dtype)
109        real = np.float32 if self.input.dtype == F32 else np.float64
110        nq = c_int(self.input.nq)
111        cutoff = real(self.cutoff)
112
113        p_loops = loops.ctypes.data
114        pars = self.input.q_pointers + [self.p_res, nq, p_loops, cutoff] + fixed + loop_n
115        #print pars
116        self.kernel(*pars)
117
118        return self.res
119
120    def release(self):
121        pass
122
123    def __del__(self):
124        self.release()
Note: See TracBrowser for help on using the repository browser.