Index: sasmodels/details.py
===================================================================
--- sasmodels/details.py	(revision 8d620089dc0fbbdb285c976744abfa4439490599)
+++ sasmodels/details.py	(revision 0ff62d40c0cfee77d55844a2c4d37924c528ecac)
@@ -83,15 +83,4 @@
         print("theta par", self.buffer[-1])
 
-def build_details(kernel, pairs):
-    values, weights = zip(*pairs)
-    if max([len(w) for w in weights]) > 1:
-        call_details = poly_details(kernel.info, weights)
-    else:
-        call_details = mono_details(kernel.info)
-    weights, values = [np.hstack(v) for v in (weights, values)]
-    weights = weights.astype(dtype=kernel.dtype)
-    values = values.astype(dtype=kernel.dtype)
-    return call_details, weights, values
-
 def mono_details(model_info):
     call_details = CallDetails(model_info)
@@ -139,39 +128,2 @@
     raise NotImplementedError("Can't handle constraints yet")
 
-
-try:
-    np.meshgrid([])
-    meshgrid = np.meshgrid
-except ValueError:
-    # CRUFT: np.meshgrid requires multiple vectors
-    def meshgrid(*args):
-        if len(args) > 1:
-            return np.meshgrid(*args)
-        else:
-            return [np.asarray(v) for v in args]
-
-def dispersion_mesh(model_info, pars):
-    """
-    Create a mesh grid of dispersion parameters and weights.
-
-    Returns [p1,p2,...],w where pj is a vector of values for parameter j
-    and w is a vector containing the products for weights for each
-    parameter set in the vector.
-    """
-    value, weight = zip(*pars)
-    weight = [w if w else [1.] for w in weight]
-    weight = np.vstack([v.flatten() for v in meshgrid(*weight)])
-    weight = np.prod(weight, axis=0)
-    value = [v.flatten() for v in meshgrid(*value)]
-    lengths = [par.length for par in model_info.parameters.kernel_parameters
-               if par.type == 'volume']
-    if any(n > 1 for n in lengths):
-        pars = []
-        offset = 0
-        for n in lengths:
-            pars.append(np.vstack(value[offset:offset+n]) if n > 1 else value[offset])
-            offset += n
-        value = pars
-    return value, weight
-
-
Index: sasmodels/direct_model.py
===================================================================
--- sasmodels/direct_model.py	(revision a5b8477c53695e7df03ecec7346f0c1bd770d3f7)
+++ sasmodels/direct_model.py	(revision 0ff62d40c0cfee77d55844a2c4d37924c528ecac)
@@ -30,5 +30,5 @@
 from . import resolution
 from . import resolution2d
-from . import details
+from . import kernel
 
 try:
@@ -41,5 +41,5 @@
     from .modelinfo import Parameter, ParameterSet
 
-def call_kernel(kernel, pars, cutoff=0., mono=False):
+def call_kernel(calculator, pars, cutoff=0., mono=False):
     # type: (Kernel, ParameterSet, float, bool) -> np.ndarray
     """
@@ -55,10 +55,10 @@
     *mono* is True if polydispersity should be set to none on all parameters.
     """
-    parameters = kernel.info.parameters
+    parameters = calculator.info.parameters
     if mono:
         active = lambda name: False
-    elif kernel.dim == '1d':
+    elif calculator.dim == '1d':
         active = lambda name: name in parameters.pd_1d
-    elif kernel.dim == '2d':
+    elif calculator.dim == '2d':
         active = lambda name: name in parameters.pd_2d
     else:
@@ -69,6 +69,6 @@
                 for p in parameters.call_parameters]
 
-    call_details, weights, values = details.build_details(kernel, vw_pairs)
-    return kernel(call_details, weights, values, cutoff)
+    call_details, weights, values = kernel.build_details(calculator, vw_pairs)
+    return calculator(call_details, weights, values, cutoff)
 
 def get_weights(parameter, values):
Index: sasmodels/kernel.py
===================================================================
--- sasmodels/kernel.py	(revision a5b8477c53695e7df03ecec7346f0c1bd770d3f7)
+++ sasmodels/kernel.py	(revision 0ff62d40c0cfee77d55844a2c4d37924c528ecac)
@@ -10,11 +10,15 @@
 """
 
+import numpy as np
+from .details import mono_details, poly_details
+
 try:
     from typing import List
+except ImportError:
+    pass
+else:
     from .details import CallDetails
     from .modelinfo import ModelInfo
     import numpy as np  # type: ignore
-except ImportError:
-    pass
 
 class KernelModel(object):
@@ -42,2 +46,55 @@
         # type: () -> None
         pass
+
+try:
+    np.meshgrid([])
+    meshgrid = np.meshgrid
+except ValueError:
+    # CRUFT: np.meshgrid requires multiple vectors
+    def meshgrid(*args):
+        if len(args) > 1:
+            return np.meshgrid(*args)
+        else:
+            return [np.asarray(v) for v in args]
+
+def dispersion_mesh(model_info, pars):
+    """
+    Create a mesh grid of dispersion parameters and weights.
+
+    Returns [p1,p2,...],w where pj is a vector of values for parameter j
+    and w is a vector containing the products for weights for each
+    parameter set in the vector.
+    """
+    value, weight = zip(*pars)
+    weight = [w if w else [1.] for w in weight]
+    weight = np.vstack([v.flatten() for v in meshgrid(*weight)])
+    weight = np.prod(weight, axis=0)
+    value = [v.flatten() for v in meshgrid(*value)]
+    lengths = [par.length for par in model_info.parameters.kernel_parameters
+               if par.type == 'volume']
+    if any(n > 1 for n in lengths):
+        pars = []
+        offset = 0
+        for n in lengths:
+            pars.append(np.vstack(value[offset:offset+n]) if n > 1 else value[offset])
+            offset += n
+        value = pars
+    return value, weight
+
+
+
+def build_details(kernel, pairs):
+    # type: (Kernel, Tuple[List[np.ndarray], List[np.ndarray]]) -> Tuple[CallDetails, np.ndarray, np.ndarray]
+    """
+    Construct the kernel call details object for calling the particular kernel.
+    """
+    values, weights = zip(*pairs)
+    if max([len(w) for w in weights]) > 1:
+        call_details = poly_details(kernel.info, weights)
+    else:
+        call_details = mono_details(kernel.info)
+    weights, values = [np.hstack(v) for v in (weights, values)]
+    weights = weights.astype(dtype=kernel.dtype)
+    values = values.astype(dtype=kernel.dtype)
+    return call_details, weights, values
+
Index: sasmodels/model_test.py
===================================================================
--- sasmodels/model_test.py	(revision e78edc4f23715c806091d73f1ccdb92ca14b3832)
+++ sasmodels/model_test.py	(revision 0ff62d40c0cfee77d55844a2c4d37924c528ecac)
@@ -51,5 +51,5 @@
 
 from .core import list_models, load_model_info, build_model, HAVE_OPENCL
-from .details import dispersion_mesh
+from .kernel import dispersion_mesh
 from .direct_model import call_kernel, get_weights
 from .exception import annotate_exception
@@ -62,5 +62,5 @@
 else:
     from .modelinfo import ParameterTable, ParameterSet, TestCondition, ModelInfo
-    from .kernelpy import PyModel, PyInput, PyKernel, DType
+    from .kernel import KernelModel
 
 def call_ER(model_info, pars):
@@ -226,5 +226,5 @@
 
         def run_one(self, model, test):
-            # type: (PyModel, TestCondition) -> None
+            # type: (KernelModel, TestCondition) -> None
             user_pars, x, y = test
             pars = expand_pars(self.info.parameters, user_pars)
Index: sasmodels/product.py
===================================================================
--- sasmodels/product.py	(revision 7ae2b7f85895f1eddd6ef18f37ff501467a34961)
+++ sasmodels/product.py	(revision 0ff62d40c0cfee77d55844a2c4d37924c528ecac)
@@ -13,7 +13,6 @@
 import numpy as np  # type: ignore
 
-from .details import dispersion_mesh
 from .modelinfo import suffix_parameter, ParameterTable, ModelInfo
-from .kernel import KernelModel, Kernel
+from .kernel import KernelModel, Kernel, dispersion_mesh
 
 try:
Index: sasmodels/sasview_model.py
===================================================================
--- sasmodels/sasview_model.py	(revision 60f03de6b699388ad10f53569d70661864b486d6)
+++ sasmodels/sasview_model.py	(revision 0ff62d40c0cfee77d55844a2c4d37924c528ecac)
@@ -27,6 +27,6 @@
 from . import generate
 from . import weights
-from . import details
 from . import modelinfo
+from . import kernel
 
 try:
@@ -502,5 +502,5 @@
         pairs = [self._get_weights(p)
                  for p in self._model_info.parameters.call_parameters]
-        call_details, weight, value = details.build_details(kernel, pairs)
+        call_details, weight, value = kernel.build_details(kernel, pairs)
         result = kernel(call_details, weight, value, cutoff=self.cutoff)
         kernel.release()
@@ -577,5 +577,5 @@
                 for p in self._model_info.parameters.call_parameters
                 if p.type == 'volume']
-        return details.dispersion_mesh(self._model_info, pars)
+        return kernel.dispersion_mesh(self._model_info, pars)
 
     def _get_weights(self, par):
