source: sasmodels/Models/code_cylinder_f.py @ a953943

core_shell_microgelscostrafo411magnetic_modelrelease_v0.94release_v0.95ticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since a953943 was ae7d639, checked in by HMP1 <helen.park@…>, 10 years ago

Ellipse now MUCH faster!

  • Property mode set to 100644
File size: 6.1 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4
5import ctypes
6from ctypes import c_int, c_double, c_void_p
7import numpy as np
8import pyopencl as cl
9from pyopencl import mem_flags as mf
10
11from weights import GaussianDispersion
12from sasmodel import card, set_precision, set_precision_1d, tic, toc
13
14
15class GpuCylinder(object):
16    PARS = {
17        'scale':1,'radius':1,'length':1,'sldCyl':1e-6,'sldSolv':0,'background':0,
18        'cyl_theta':0,'cyl_phi':0,
19    }
20    PD_PARS = ['radius', 'length', 'cyl_theta', 'cyl_phi']
21
22    def __init__(self, qx, qy, dtype='float32', cutoff=1e-5):
23
24        #create context, queue, and build program
25        ctx,_queue = card()
26        bessel = open('Kernel/NR_BessJ1.cpp').read()
27        kernel = open('Kernel/Kernel-Cylinder_f.cpp').read()
28        src, qx, qy = set_precision("\n".join((bessel,kernel)), qx, qy, dtype=dtype)
29        self.prg = cl.Program(ctx, src).build()
30        self.qx, self.qy = qx, qy
31        self.cutoff = cutoff
32
33        #buffers
34        self.qx_b = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=self.qx)
35        self.qy_b = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=self.qy)
36        self.res_b = cl.Buffer(ctx, cl.mem_flags.READ_WRITE, self.qx.nbytes)
37        self.res = np.empty_like(self.qx)
38
39    def eval(self, pars):
40        tic()
41
42        ctx,queue = card()
43        real = np.float32 if self.qx.dtype == np.dtype('float32') else np.float64
44        loops, loop_lengths = make_loops(pars, dtype=self.qx.dtype)
45        loops_b = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=loops)
46        loops_l = cl.LocalMemory(len(loops.data))
47
48        self.prg.CylinderKernel(queue, self.qx.shape, None,
49            self.qx_b, self.qy_b, self.res_b,
50            loops_b, loops_l, real(self.cutoff),
51            real(pars['scale']), real(pars['background']),
52            real(pars['sldCyl']-pars['sldSolv']),
53            *[np.uint32(pn) for pn in loop_lengths])
54
55        cl.enqueue_copy(queue, self.res, self.res_b)
56        print toc()*1000, self.qx.shape[0]
57
58        return self.res
59
60class CpuCylinder(GpuCylinder): #inherit parameters only
61    def __init__(self, qx, qy, dtype='float32', cutoff=1e-5):
62        self.qx, self.qy = [np.ascontiguousarray(v,'d') for v in qx,qy]
63        self.cutoff = cutoff
64        self.res = np.empty_like(self.qx)
65        self.dll = ctypes.CDLL('Kernel/cylinder.so')
66        self.fn = self.dll['CylinderKernel']
67        self.fn.argtypes = [
68            c_void_p, c_void_p, c_void_p, c_int,
69            c_void_p, c_double, c_double, c_double, c_double,
70            c_int, c_int, c_int, c_int
71        ]
72    def eval(self, pars):
73        loops, loop_lengths = make_loops(pars, dtype=self.qx.dtype)
74        self.fn(self.qx.ctypes.data, self.qy.ctypes.data, self.res.ctypes.data, len(self.qx),
75            loops.ctypes.data, self.cutoff, pars['scale'], pars['background'],
76            pars['sldCyl']-pars['sldSolv'], *loop_lengths)
77
78        return self.res
79
80def make_loops(pars, dtype='double'):
81    # 0.2 ms on sparkle to form the final loops
82    radius, length, theta, phi = \
83       [GaussianDispersion(int(pars[base+'_pd_n']), pars[base+'_pd'], pars[base+'_pd_nsigma'])
84         for base in GpuCylinder.PD_PARS]
85    parts = [
86        radius.get_weights(pars['radius'], 0, 10000, True),
87        length.get_weights(pars['length'], 0, 10000, True),
88        theta.get_weights(pars['cyl_theta'], -np.inf, np.inf, False),
89        phi.get_weights(pars['cyl_phi'], -np.inf, np.inf, False),
90        ]
91    # Make sure that weights are normalized to peaks at 1 so that
92    # the tolerance term can be used properly on truncated distributions
93    loops = np.hstack((v,w/w.max()) for v,w in parts)
94    #loops = np.hstack(parts)
95    loops = np.ascontiguousarray(loops.T, dtype).flatten()
96    return loops, [len(p[0]) for p in parts]
97
98class OneDGpuCylinder(object):
99    PARS = {
100        'scale':1,'radius':1,'length':1,'sldCyl':1e-6,'sldSolv':0,'background':0,
101        'bolim':0, 'uplim':90
102    }
103    PD_PARS = ['radius', 'length']
104
105    def __init__(self, q, dtype='float32'):
106
107        #create context, queue, and build program
108        ctx,_queue = card()
109        trala = open('Kernel/NR_BessJ1.cpp').read()+"\n"+open('Kernel/OneDCyl_Kfun.cpp').read()+"\n"+open('Kernel/Kernel-OneDCylinder.cpp').read()
110        src, self.q = set_precision_1d(trala, q, dtype=dtype)
111        self.prg = cl.Program(ctx, src).build()
112
113        #buffers
114        mf = cl.mem_flags
115        self.q_b = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=self.q)
116        self.res_b = cl.Buffer(ctx, mf.WRITE_ONLY, q.nbytes)
117        self.res = np.empty_like(self.q)
118
119    def eval(self, pars):
120
121        _ctx,queue = card()
122        radius, length = \
123            [GaussianDispersion(int(pars[base+'_pd_n']), pars[base+'_pd'], pars[base+'_pd_nsigma'])
124             for base in OneDGpuCylinder.PD_PARS]
125
126        #Get the weights for each
127        radius.value, radius.weight = radius.get_weights(pars['radius'], 0, 10000, True)
128        length.value, length.weight = length.get_weights(pars['length'], 0, 10000, True)
129
130        #Perform the computation, with all weight points
131        sum, norm, vol = 0.0, 0.0, 0.0,
132        sub = pars['sldCyl'] - pars['sldSolv']
133
134        real = np.float32 if self.q.dtype == np.dtype('float32') else np.float64
135        #Loop over radius, length, theta, phi weight points
136        for r in xrange(len(radius.weight)):
137            for l in xrange(len(length.weight)):
138                        self.prg.OneDCylKernel(queue, self.q.shape, None, self.q_b, self.res_b, real(sub),
139                                           real(length.value[l]), real(radius.value[r]), real(pars['scale']),
140                                           np.uint32(self.q.size), real(pars['uplim']), real(pars['bolim']))
141                        cl.enqueue_copy(queue, self.res, self.res_b)
142                        sum += radius.weight[r]*length.weight[l]*self.res*pow(radius.value[r],2)*length.value[l]
143                        vol += radius.weight[r]*length.weight[l] *pow(radius.value[r],2)*length.value[l]
144                        norm += radius.weight[r]*length.weight[l]
145
146        if vol != 0.0 and norm != 0.0:
147            sum *= norm/vol
148
149        return sum/norm + pars['background']
Note: See TracBrowser for help on using the repository browser.