Index: doc/developer/calculator.rst
===================================================================
--- doc/developer/calculator.rst	(revision 03cac087353e872f40154bf57ef8161eb5367fd4)
+++ doc/developer/calculator.rst	(revision 48fbd50a25db17a9c6bf7770aeb061514b3e8879)
@@ -133,8 +133,10 @@
 decreasing order of length for highest efficiency.
 
-We limit the number of polydisperse dimensions to MAX_PD (currently 4).
-This cuts the size of the structure in half compared to allowing a
-separate polydispersity for each parameter.  This will help a little
-bit for models with large numbers of parameters, such as the onion model.
+We limit the number of polydisperse dimensions to MAX_PD (currently 4),
+though some models may have fewer if they have fewer polydisperse
+parameters. This cuts the size of the structure in half compared to
+allowing a separate polydispersity for each parameter.  This will
+help a little bit for models with large numbers of parameters, such
+as the onion model.
 
 Parameters may be coordinated.  That is, we may have the value of one
Index: sasmodels/core.py
===================================================================
--- sasmodels/core.py	(revision 88aa3ee0492a9534afa8c766a659a4d4fa75ceee)
+++ sasmodels/core.py	(revision 48fbd50a25db17a9c6bf7770aeb061514b3e8879)
@@ -26,5 +26,5 @@
 __all__ = [
     "list_models", "load_model_info", "precompile_dll",
-    "build_model", "make_kernel", "call_kernel", "call_ER_VR",
+    "build_model", "call_kernel", "call_ER_VR",
 ]
 
@@ -167,10 +167,4 @@
 
 
-def make_kernel(model, q_vectors):
-    """
-    Return a computation kernel from the model definition and the q input.
-    """
-    return model(q_vectors)
-
 def get_weights(parameter, values):
     """
@@ -210,5 +204,5 @@
 def call_kernel(kernel, pars, cutoff=0, mono=False):
     """
-    Call *kernel* returned from :func:`make_kernel` with parameters *pars*.
+    Call *kernel* returned from *model.make_kernel* with parameters *pars*.
 
     *cutoff* is the limiting value for the product of dispersion weights used
Index: sasmodels/direct_model.py
===================================================================
--- sasmodels/direct_model.py	(revision 303d8d66300d33bd04a1db60bb002b734d6f6985)
+++ sasmodels/direct_model.py	(revision 48fbd50a25db17a9c6bf7770aeb061514b3e8879)
@@ -25,5 +25,4 @@
 import numpy as np
 
-from .core import make_kernel
 from .core import call_kernel, call_ER_VR
 from . import sesans
@@ -153,9 +152,13 @@
     def _calc_theory(self, pars, cutoff=0.0):
         if self._kernel is None:
-            self._kernel = make_kernel(self._model, self._kernel_inputs)  # pylint: disable=attribute-dedata_type
-            self._kernel_mono = make_kernel(self._model, self._kernel_mono_inputs) if self._kernel_mono_inputs else None
+            self._kernel = self._model.make_kernel(self._kernel_inputs)
+            self._kernel_mono = (
+                self._model.make_kernel(self._kernel_mono_inputs)
+                if self._kernel_mono_inputs else None
+            )
 
         Iq_calc = call_kernel(self._kernel, pars, cutoff=cutoff)
-        Iq_mono = call_kernel(self._kernel_mono, pars, mono=True) if self._kernel_mono_inputs else None
+        Iq_mono = (call_kernel(self._kernel_mono, pars, mono=True)
+                   if self._kernel_mono_inputs else None)
         if self.data_type == 'sesans':
             result = sesans.transform(self._data,
Index: sasmodels/generate.py
===================================================================
--- sasmodels/generate.py	(revision abc03d8adad3069f7285b81575c54de1fad97dac)
+++ sasmodels/generate.py	(revision abc03d8adad3069f7285b81575c54de1fad97dac)
@@ -684,4 +684,6 @@
 
 def poly_details(model_info, weights):
+    print("entering poly",weights)
+    print([p.name for p in model_info['parameters']])
     pars = model_info['parameters'][2:]  # skip scale and background
     weights = weights[2:]
Index: sasmodels/kernelcl.py
===================================================================
--- sasmodels/kernelcl.py	(revision 445d1c0e3f3fab8c5e5ded3d5fd240e0f7b1367e)
+++ sasmodels/kernelcl.py	(revision 48fbd50a25db17a9c6bf7770aeb061514b3e8879)
@@ -336,5 +336,5 @@
         self.program = None
 
-    def make_calculator(self, q_vectors, details):
+    def make_kernel(self, q_vectors):
         if self.program is None:
             compiler = environment().compile_program
@@ -344,5 +344,5 @@
         kernel_name = generate.kernel_name(self.info, is_2d)
         kernel = getattr(self.program, kernel_name)
-        return GpuKernel(kernel, self.info, q_vectors, details, self.dtype)
+        return GpuKernel(kernel, self.info, q_vectors, self.dtype)
 
     def release(self):
@@ -403,5 +403,4 @@
         context = env.get_context(self.dtype)
         #print("creating inputs of size", self.global_size)
-        # COPY_HOST_PTR initiates transfer as necessary?
         self.q_b = cl.Buffer(context, mf.READ_ONLY | mf.COPY_HOST_PTR,
                              hostbuf=self.q)
@@ -438,8 +437,5 @@
     Call :meth:`release` when done with the kernel instance.
     """
-    def __init__(self, kernel, model_info, q_vectors, details, dtype):
-        if details.dtype != np.int32:
-            raise TypeError("numeric type does not match the kernel type")
-
+    def __init__(self, kernel, model_info, q_vectors, dtype):
         max_pd = self.info['max_pd']
         npars = len(model_info['parameters'])-2
@@ -449,5 +445,4 @@
         self.kernel = kernel
         self.info = model_info
-        self.details = details
         self.pd_stop_index = 4*max_pd-1
         # plus three for the normalization values
@@ -459,47 +454,37 @@
         self.queue = env.get_queue(dtype)
 
-        # details is int32 data, padded to a 32 integer boundary
-        size = 4*((self.info['mono'].size+7)//8)*8 # padded to 32 byte boundary
-        self.details_b = cl.Buffer(self.queue.context,
-                                   mf.READ_ONLY | mf.COPY_HOST_PTR,
-                                   hostbuf=details)
-        size = np.sum(details[max_pd:2*max_pd])
-        self.weights_b = cl.Buffer(self.queue.context, mf.READ_ONLY, size)
-        size = np.sum(details[max_pd:2*max_pd])+npars
-        self.values_b = cl.Buffer(self.queue.context, mf.READ_ONLY, size)
+        # details is int32 data, padded to an 8 integer boundary
+        size = ((max_pd*5 + npars*3 + 2 + 7)//8)*8
         self.result_b = cl.Buffer(self.queue.context, mf.READ_WRITE,
                                q_input.global_size[0] * q_input.dtype.itemsize)
         self.q_input = q_input # allocated by GpuInput above
 
-        self._need_release = [
-            self.details_b, self.weights_b, self.values_b, self.result_b,
-            self.q_input,
-        ]
-
-    def __call__(self, weights, values, cutoff):
+        self._need_release = [ self.result_b, self.q_input ]
+
+    def __call__(self, details, weights, values, cutoff):
         real = (np.float32 if self.q_input.dtype == generate.F32
                 else np.float64 if self.q_input.dtype == generate.F64
                 else np.float16 if self.q_input.dtype == generate.F16
                 else np.float32)  # will never get here, so use np.float32
-
-        if weights.dtype != real or values.dtype != real:
-            raise TypeError("numeric type does not match the kernel type")
-
-        cl.enqueue_copy(self.queue, self.weights_b, weights)
-        cl.enqueue_copy(self.queue, self.values_b, values)
-
+        assert details.dtype == np.int32
+        assert weights.dtype == real and values.dtype == real
+
+        context = self.queue.context
+        details_b = cl.Buffer(context, mf.READ_ONLY | mf.COPY_HOST_PTR,
+                              hostbuf=details)
+        weights_b = cl.Buffer(context, mf.READ_ONLY | mf.COPY_HOST_PTR,
+                              hostbuf=weights)
+        values_b = cl.Buffer(context, mf.READ_ONLY | mf.COPY_HOST_PTR,
+                             hostbuf=values)
+
+        start, stop = 0, self.details[self.pd_stop_index]
         args = [
-            np.uint32(self.q_input.nq),
-            np.uint32(0),
-            np.uint32(self.details[self.pd_stop_index]),
-            self.details_b,
-            self.weights_b,
-            self.values_b,
-            self.q_input.q_b,
-            self.result_b,
-            real(cutoff),
+            np.uint32(self.q_input.nq), np.uint32(start), np.uint32(stop),
+            self.details_b, self.weights_b, self.values_b,
+            self.q_input.q_b, self.result_b, real(cutoff),
         ]
         self.kernel(self.queue, self.q_input.global_size, None, *args)
         cl.enqueue_copy(self.queue, self.result, self.result_b)
+        [v.release() for v in details_b, weights_b, values_b]
 
         return self.result[:self.nq]
Index: sasmodels/kerneldll.py
===================================================================
--- sasmodels/kerneldll.py	(revision abc03d8adad3069f7285b81575c54de1fad97dac)
+++ sasmodels/kerneldll.py	(revision abc03d8adad3069f7285b81575c54de1fad97dac)
@@ -218,5 +218,5 @@
         self.dll = None
 
-    def __call__(self, q_vectors):
+    def make_kernel(self, q_vectors):
         q_input = PyInput(q_vectors, self.dtype)
         if self.dll is None: self._load_dll()
@@ -251,13 +251,10 @@
     """
     def __init__(self, kernel, model_info, q_input):
+        self.kernel = kernel
         self.info = model_info
         self.q_input = q_input
         self.dtype = q_input.dtype
-        self.kernel = kernel
-        self.res = np.empty(q_input.nq+3, q_input.dtype)
         self.dim = '2d' if q_input.is_2d else '1d'
-
-        # In dll kernel, but not in opencl kernel
-        self.p_res = self.res.ctypes.data
+        self.result = np.empty(q_input.nq+3, q_input.dtype)
 
     def __call__(self, details, weights, values, cutoff):
@@ -265,26 +262,22 @@
                 else np.float64 if self.q_input.dtype == generate.F64
                 else np.float128)
-        if details.dtype != np.int32 or weights.dtype != real or values.dtype != real:
-            raise TypeError("numeric type does not match the kernel type")
-        #details = np.asarray(details, dtype='int32')
-        #weights = np.asarray(weights, dtype=real)
-        #values = np.asarray(values, dtype=real)
-        #TODO: How can I access max_pd and is this the way to do it?
-        #max_pd = model_info['max_pd']
-        max_pd = 1
+        assert details.dtype == np.int32
+        assert weights.dtype == real and values.dtype == real
+
+        max_pd = self.info['max_pd']
+        start, stop = 0, details[4*max_pd-1]
         args = [
             self.q_input.nq, # nq
-            #TODO: pd_start will need to be changed
-            0, # pd_start
-            details[4*max_pd-1], # pd_stop pd_stride[MAX_PD]
+            start, # pd_start
+            stop, # pd_stop pd_stride[MAX_PD]
             details.ctypes.data, # problem
             weights.ctypes.data,  # weights
             values.ctypes.data,  #pars
-            self.q_input.q_pointers[0], #q
-            self.p_res,   # results
+            self.q_input.q.ctypes.data, #q
+            self.result.ctypes.data,   # results
             real(cutoff), # cutoff
             ]
         self.kernel(*args)
-        return self.res[:-3]
+        return self.result[:-3]
 
     def release(self):
Index: sasmodels/kernelpy.py
===================================================================
--- sasmodels/kernelpy.py	(revision d5ac45fbf8e0b8649deccb5e4e72b31f7017e35b)
+++ sasmodels/kernelpy.py	(revision 48fbd50a25db17a9c6bf7770aeb061514b3e8879)
@@ -53,6 +53,11 @@
         self.dtype = dtype
         self.is_2d = (len(q_vectors) == 2)
-        self.q_vectors = [np.ascontiguousarray(q, self.dtype) for q in q_vectors]
-        self.q_pointers = [q.ctypes.data for q in self.q_vectors]
+        if self.is_2d:
+            self.q = np.empty((self.nq, 2), dtype=dtype)
+            self.q[:, 0] = q_vectors[0]
+            self.q[:, 1] = q_vectors[1]
+        else:
+            self.q = np.empty(self.nq, dtype=dtype)
+            self.q[:self.nq] = q_vectors[0]
 
     def release(self):
@@ -60,5 +65,5 @@
         Free resources associated with the model inputs.
         """
-        self.q_vectors = []
+        self.q = None
 
 class PyKernel(object):
Index: sasmodels/model_test.py
===================================================================
--- sasmodels/model_test.py	(revision a84a0cac83cecece77ff644df4f2552ef1452b62)
+++ sasmodels/model_test.py	(revision 48fbd50a25db17a9c6bf7770aeb061514b3e8879)
@@ -51,5 +51,5 @@
 
 from .core import list_models, load_model_info, build_model, HAVE_OPENCL
-from .core import make_kernel, call_kernel, call_ER, call_VR
+from .core import call_kernel, call_ER, call_VR
 from .exception import annotate_exception
 
@@ -187,9 +187,9 @@
                 Qx, Qy = zip(*x)
                 q_vectors = [np.array(Qx), np.array(Qy)]
-                kernel = make_kernel(model, q_vectors)
+                kernel = model.make_kernel(q_vectors)
                 actual = call_kernel(kernel, pars)
             else:
                 q_vectors = [np.array(x)]
-                kernel = make_kernel(model, q_vectors)
+                kernel = model.make_kernel(q_vectors)
                 actual = call_kernel(kernel, pars)
 
Index: sasmodels/resolution.py
===================================================================
--- sasmodels/resolution.py	(revision 303d8d66300d33bd04a1db60bb002b734d6f6985)
+++ sasmodels/resolution.py	(revision 48fbd50a25db17a9c6bf7770aeb061514b3e8879)
@@ -479,5 +479,5 @@
     """
     from sasmodels import core
-    kernel = core.make_kernel(form, [q])
+    kernel = form.make_kernel([q])
     theory = core.call_kernel(kernel, pars)
     kernel.release()
@@ -562,5 +562,6 @@
         extra = set(pars.keys()) - par_set
         raise ValueError("bad parameters: [%s] not in [%s]"%
-                         (", ".join(sorted(extra)), ", ".join(sorted(keys))))
+                         (", ".join(sorted(extra)),
+                          ", ".join(sorted(pars.keys()))))
 
     _fn = lambda q, q0, dq: eval_form(q, form, pars)*gaussian(q, q0, dq)
@@ -696,5 +697,5 @@
     def _eval_sphere(self, pars, resolution):
         from sasmodels import core
-        kernel = core.make_kernel(self.model, [resolution.q_calc])
+        kernel = self.model.make_kernel([resolution.q_calc])
         theory = core.call_kernel(kernel, pars)
         result = resolution.apply(theory)
@@ -1065,5 +1066,5 @@
     model = core.build_model(model_info)
 
-    kernel = core.make_kernel(model, [resolution.q_calc])
+    kernel = model.make_kernel([resolution.q_calc])
     theory = core.call_kernel(kernel, pars)
     Iq = resolution.apply(theory)
