Index: sasmodels/core.py
===================================================================
--- sasmodels/core.py	(revision b7172bb32b97fed9a6de170d43d15c6e780f5466)
+++ sasmodels/core.py	(revision b7172bb32b97fed9a6de170d43d15c6e780f5466)
@@ -6,5 +6,4 @@
     "build_model", "make_kernel", "call_kernel", "call_ER_VR",
     ]
-
 
 from os.path import basename, dirname, join as joinpath, splitext
@@ -30,4 +29,19 @@
 
 try:
+    # Python 3.5 and up
+    from importlib.util import spec_from_file_location, module_from_spec
+    def load_module(fullname, path):
+        spec = spec_from_file_location(fullname, path)
+        module = module_from_spec(spec)
+        spec.loader.exec_module(module)
+        return module
+except ImportError:
+    # CRUFT: python 2
+    import imp
+    def load_module(fullname, path):
+        module = imp.load_source(fullname, path)
+        return module
+
+try:
     np.meshgrid([])
     meshgrid = np.meshgrid
@@ -40,5 +54,4 @@
             return [np.asarray(v) for v in args]
 
-
 def list_models():
     """
@@ -63,21 +76,4 @@
     """
     return build_model(load_model_info(model_name), **kw)
-
-def load_model_info_from_path(path):
-    # Pull off the last .ext if it exists; there may be others
-    name = basename(splitext(path)[0])
-
-    # Not cleaning name since don't need to be able to reload this
-    # model later
-    # Should probably turf the model from sys.modules after we are done...
-
-    # Placing the model in the 'sasmodels.custom' name space, even
-    # though it doesn't actually exist.  imp.load_source doesn't seem
-    # to care.
-    kernel_module = imp.load_source('sasmodels.custom.'+name, path)
-
-    # Now that we have the module, we can load it as usual
-    model_info = generate.make_model_info(kernel_module)
-    return model_info
 
 def load_model_info(model_name):
@@ -102,8 +98,24 @@
         return product.make_product_info(P_info, Q_info)
 
+    kernel_module = load_kernel_module(model_name)
+    return generate.make_model_info(kernel_module)
+
+
+def load_kernel_module(model_name):
+    if model_name.endswith('.py'):
+        path = model_name
+        # Pull off the last .ext if it exists; there may be others
+        name = basename(splitext(path)[0])
+        # Placing the model in the 'sasmodels.custom' name space.
+        from sasmodels import custom
+        kernel_module = load_module('sasmodels.custom.'+name, path)
+    else:
+        from sasmodels import models
+        __import__('sasmodels.models.'+model_name)
+        kernel_module = getattr(models, model_name, None)
     #import sys; print "\n".join(sys.path)
     __import__('sasmodels.models.'+model_name)
     kernel_module = getattr(models, model_name, None)
-    return generate.make_model_info(kernel_module)
+    return kernel_module
 
 
@@ -179,11 +191,5 @@
 
 
-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(model_info, pars, name):
+def get_weights(parameter, values):
     """
     Generate the distribution for parameter *name* given the parameter values
@@ -193,11 +199,13 @@
     from the *pars* dictionary for parameter value and parameter dispersion.
     """
-    relative = name in model_info['partype']['pd-rel']
-    limits = model_info['limits'][name]
-    disperser = pars.get(name+'_pd_type', 'gaussian')
-    value = pars.get(name, model_info['defaults'][name])
-    npts = pars.get(name+'_pd_n', 0)
-    width = pars.get(name+'_pd', 0.0)
-    nsigma = pars.get(name+'_pd_nsigma', 3.0)
+    value = values.get(parameter.name, parameter.default)
+    relative = parameter.relative_pd
+    limits = parameter.limits
+    disperser = values.get(parameter.name+'_pd_type', 'gaussian')
+    npts = values.get(parameter.name+'_pd_n', 0)
+    width = values.get(parameter.name+'_pd', 0.0)
+    nsigma = values.get(parameter.name+'_pd_nsigma', 3.0)
+    if npts == 0 or width == 0:
+        return [value], []
     value, weight = weights.get_weights(
         disperser, npts, width, nsigma, value, limits, relative)
@@ -220,5 +228,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
@@ -228,13 +236,35 @@
     with an error of about 1%, which is usually less than the measurement
     uncertainty.
-    """
-    fixed_pars = [pars.get(name, kernel.info['defaults'][name])
-                  for name in kernel.fixed_pars]
+
+    *mono* is True if polydispersity should be set to none on all parameters.
+    """
+    parameters = kernel.info['parameters']
     if mono:
-        pd_pars = [( np.array([pars[name]]), np.array([1.0]) )
-                   for name in kernel.pd_pars]
-    else:
-        pd_pars = [get_weights(kernel.info, pars, name) for name in kernel.pd_pars]
-    return kernel(fixed_pars, pd_pars, cutoff=cutoff)
+        active = lambda name: False
+    elif kernel.dim == '1d':
+        active = lambda name: name in parameters.pd_1d
+    elif kernel.dim == '2d':
+        active = lambda name: name in parameters.pd_2d
+    else:
+        active = lambda name: True
+
+    vw_pairs = [(get_weights(p, pars) if active(p.name)
+                 else ([pars.get(p.name, p.default)], []))
+                for p in parameters.call_parameters]
+
+    details, weights, values = build_details(kernel, vw_pairs)
+    return kernel(details, weights, values, cutoff)
+
+def build_details(kernel, pairs):
+    values, weights = zip(*pairs)
+    if max([len(w) for w in weights]) > 1:
+        details = generate.poly_details(kernel.info, weights)
+    else:
+        details = kernel.info['mono_details']
+    weights, values = [np.hstack(v) for v in (weights, values)]
+    weights = weights.astype(dtype=kernel.dtype)
+    values = values.astype(dtype=kernel.dtype)
+    return details, weights, values
+
 
 def call_ER_VR(model_info, vol_pars):
@@ -259,16 +289,17 @@
 
 
-def call_ER(info, pars):
-    """
-    Call the model ER function using *pars*.
-    *info* is either *model.info* if you have a loaded model, or *kernel.info*
-    if you have a model kernel prepared for evaluation.
-    """
-    ER = info.get('ER', None)
+def call_ER(model_info, values):
+    """
+    Call the model ER function using *values*. *model_info* is either
+    *model.info* if you have a loaded model, or *kernel.info* if you
+    have a model kernel prepared for evaluation.
+    """
+    ER = model_info.get('ER', None)
     if ER is None:
         return 1.0
     else:
-        vol_pars = [get_weights(info, pars, name)
-                    for name in info['partype']['volume']]
+        vol_pars = [get_weights(parameter, values)
+                    for parameter in model_info['parameters']
+                    if parameter.type == 'volume']
         value, weight = dispersion_mesh(vol_pars)
         individual_radii = ER(*value)
@@ -276,5 +307,5 @@
         return np.sum(weight*individual_radii) / np.sum(weight)
 
-def call_VR(info, pars):
+def call_VR(model_info, values):
     """
     Call the model VR function using *pars*.
@@ -282,10 +313,11 @@
     if you have a model kernel prepared for evaluation.
     """
-    VR = info.get('VR', None)
+    VR = model_info.get('VR', None)
     if VR is None:
         return 1.0
     else:
-        vol_pars = [get_weights(info, pars, name)
-                    for name in info['partype']['volume']]
+        vol_pars = [get_weights(parameter, values)
+                    for parameter in model_info['parameters']
+                    if parameter.type == 'volume']
         value, weight = dispersion_mesh(vol_pars)
         whole, part = VR(*value)
