source: sasmodels/Models/code_cylinder_f.py @ 1726b21

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

cylinder now MUCH faster!

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