Index: extra/pylint.rc
===================================================================
--- extra/pylint.rc	(revision 6e68289f10e05b04aa6bcbba67042e16cb307883)
+++ extra/pylint.rc	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -67,4 +67,14 @@
     locally-enabled,
     locally-disabled,
+    #no-else-return,
+    #invalid-name,
+    #fixme,
+    #too-many-lines,
+    #too-many-locals,
+    #too-many-branches,
+    #too-many-statements,
+    #too-many-instance-attributes,
+    #too-many-return-statements,
+    #no-self-use,
 
 [REPORTS]
@@ -109,5 +119,5 @@
 good-names=_,
     input, id,
-    h, i, j, k, n, p, v, w, x, y, z,
+    h, i, j, k, n, p, u, v, w, x, y, z,
     q, qx, qy, qz, Q, Qx, Qy, Qz,
     dt, dx, dy, dz, dq,
Index: sasmodels/bumps_model.py
===================================================================
--- sasmodels/bumps_model.py	(revision 2c4a190a64dff2df7c3a9d3ea13d6d566b439ecd)
+++ sasmodels/bumps_model.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -26,4 +26,5 @@
     from .kernel import KernelModel
     from .modelinfo import ModelInfo
+    from .resolution import Resolution
     Data = Union[Data1D, Data2D]
 except ImportError:
@@ -180,8 +181,16 @@
     @property
     def resolution(self):
+        # type: () -> Union[None, Resolution]
+        """
+        :class:`sasmodels.Resolution` applied to the data, if any.
+        """
         return self._resolution
 
     @resolution.setter
     def resolution(self, value):
+        # type: (Resolution) -> None
+        """
+        :class:`sasmodels.Resolution` applied to the data, if any.
+        """
         self._resolution = value
 
@@ -270,5 +279,5 @@
         Save the model parameters and data into a file.
 
-        Not Implemented.
+        Not Implemented except for sesans fits.
         """
         if self.data_type == "sesans":
Index: sasmodels/compare.py
===================================================================
--- sasmodels/compare.py	(revision 4de145843b2267c4f1e8be16517c48de601151b6)
+++ sasmodels/compare.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -725,5 +725,5 @@
         set_integration_size(model_info, ngauss)
 
-    if (dtype != "default" and not dtype.endswith('!') 
+    if (dtype != "default" and not dtype.endswith('!')
             and not (kernelcl.use_opencl() or kernelcuda.use_cuda())):
         raise RuntimeError("OpenCL not available " + kernelcl.OPENCL_ERROR)
@@ -772,5 +772,5 @@
         opts['pars'] = parse_pars(opts, maxdim=maxdim)
         if opts['pars'] is None:
-            return
+            return limits
         result = run_models(opts, verbose=True)
         if opts['plot']:
@@ -822,6 +822,6 @@
     args = dict((k, v) for k, v in args.items()
                 if "_pd" not in k
-                   and ":" not in k
-                   and k not in ("background", "scale", "theta", "phi", "psi"))
+                and ":" not in k
+                and k not in ("background", "scale", "theta", "phi", "psi"))
     args = args.copy()
 
@@ -896,5 +896,5 @@
     # work with trimmed data, not the full set
     sorted_err = np.sort(abs(err.compressed()))
-    if len(sorted_err) == 0:
+    if sorted_err.size == 0:
         print(label + "  no valid values")
         return
Index: sasmodels/convert.py
===================================================================
--- sasmodels/convert.py	(revision 21c93c37dac1eb1749656b388dd13e5fa29e63f3)
+++ sasmodels/convert.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -384,4 +384,5 @@
 
 def revert_name(model_info):
+    """Translate model name back to the name used in SasView 3.x"""
     oldname, _ = CONVERSION_TABLE.get(model_info.id, [None, {}])
     return oldname
@@ -653,4 +654,7 @@
 
 def test_backward_forward():
+    """
+    Test conversion of model parameters from 4.x to 3.x and back.
+    """
     from .core import list_models
     L = lambda name: _check_one(name, seed=1)
Index: sasmodels/core.py
===================================================================
--- sasmodels/core.py	(revision d92182f88244a4ce39a4ef855ac8295e073ff65f)
+++ sasmodels/core.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -323,4 +323,7 @@
 
 def test_composite_order():
+    """
+    Check that mixture models produce the same result independent of ordder.
+    """
     def test_models(fst, snd):
         """Confirm that two models produce the same parameters"""
@@ -337,4 +340,5 @@
 
     def build_test(first, second):
+        """Construct pair model test"""
         test = lambda description: test_models(first, second)
         description = first + " vs. " + second
@@ -382,5 +386,5 @@
 
 def list_models_main():
-    # type: () -> None
+    # type: () -> int
     """
     Run list_models as a main program.  See :func:`list_models` for the
@@ -391,9 +395,9 @@
     try:
         models = list_models(kind)
-    except Exception as exc:
+        print("\n".join(models))
+    except Exception:
         print(list_models.__doc__)
         return 1
-
-    print("\n".join(list_models(kind)))
+    return 0
 
 if __name__ == "__main__":
Index: sasmodels/data.py
===================================================================
--- sasmodels/data.py	(revision bd7630de7da8bd67aa24b8fc036b2dcc1bc72a16)
+++ sasmodels/data.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -500,5 +500,5 @@
             mdata = masked_array(data.y, data.mask.copy())
             mdata[~np.isfinite(mdata)] = masked
-            if view is 'log':
+            if view == 'log':
                 mdata[mdata <= 0] = masked
             plt.errorbar(data.x, scale*mdata, yerr=data.dy, fmt='.')
@@ -514,5 +514,5 @@
             mtheory = masked_array(theory)
             mtheory[~np.isfinite(mtheory)] = masked
-            if view is 'log':
+            if view == 'log':
                 mtheory[mtheory <= 0] = masked
             plt.plot(theory_x, scale*mtheory, '-')
Index: sasmodels/details.py
===================================================================
--- sasmodels/details.py	(revision 885753a925cb7e3423bdd0db6db395557a6fb1c6)
+++ sasmodels/details.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -236,9 +236,9 @@
     npars = kernel.info.parameters.npars
     nvalues = kernel.info.parameters.nvalues
-    scalars = [value for value, _dispersity, _weight in mesh]
+    scalars = [value for value, dispersity, weight in mesh]
     # skipping scale and background when building values and weights
-    _values, dispersity, weights = zip(*mesh[2:npars+2]) if npars else ((), (), ())
-    #weights = correct_theta_weights(kernel.info.parameters, dispersity, weights)
-    length = np.array([len(w) for w in weights])
+    value, dispersity, weight = zip(*mesh[2:npars+2]) if npars else ((), (), ())
+    #weight = correct_theta_weights(kernel.info.parameters, dispersity, weight)
+    length = np.array([len(w) for w in weight])
     offset = np.cumsum(np.hstack((0, length)))
     call_details = make_details(kernel.info, length, offset[:-1], offset[-1])
@@ -246,5 +246,5 @@
     data_len = nvalues + 2*sum(len(v) for v in dispersity)
     extra = (32 - data_len%32)%32
-    data = np.hstack((scalars,) + dispersity + weights + ZEROS[:extra])
+    data = np.hstack((scalars,) + dispersity + weight + ZEROS[:extra])
     data = data.astype(kernel.dtype)
     is_magnetic = convert_magnetism(kernel.info.parameters, data)
Index: sasmodels/direct_model.py
===================================================================
--- sasmodels/direct_model.py	(revision 2c4a190a64dff2df7c3a9d3ea13d6d566b439ecd)
+++ sasmodels/direct_model.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -31,5 +31,4 @@
 from . import resolution2d
 from .details import make_kernel_args, dispersion_mesh
-from .modelinfo import DEFAULT_BACKGROUND
 from .product import RADIUS_MODE_ID
 
Index: sasmodels/generate.py
===================================================================
--- sasmodels/generate.py	(revision cd289474f05b7a39cccd795f21f84befdf2a8788)
+++ sasmodels/generate.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -190,4 +190,12 @@
 
 def get_data_path(external_dir, target_file):
+    """
+    Search for the target file relative in the installed application.
+
+    Search first in the location of the generate module in case we are
+    running directly from the distribution.  Search next to the python
+    executable for windows installs.  Search in the ../Resources directory
+    next to the executable for Mac OS/X installs.
+    """
     path = abspath(dirname(__file__))
     if exists(joinpath(path, target_file)):
@@ -225,6 +233,6 @@
 #
 # NOTE: there is an RST_PROLOG at the end of this file which is NOT
-# used for the bundled documentation. Still as long as we are defining the macros
-# in two places any new addition should define the macro in both places.
+# used for the bundled documentation. Still as long as we are defining the
+# macros in two places any new addition should define the macro in both places.
 RST_UNITS = {
     "Ang": "|Ang|",
@@ -530,5 +538,5 @@
 
 def test_tag_float():
-    """check that floating point constants are properly identified and tagged with 'f'"""
+    """Check that floating point constants are identified and tagged with 'f'"""
 
     cases = """
@@ -707,5 +715,6 @@
     return False
 
-_SHELL_VOLUME_PATTERN = re.compile(r"(^|\s)double\s+shell_volume[(]", flags=re.MULTILINE)
+_SHELL_VOLUME_PATTERN = re.compile(r"(^|\s)double\s+shell_volume[(]",
+                                   flags=re.MULTILINE)
 def contains_shell_volume(source):
     # type: (List[str]) -> bool
@@ -801,5 +810,5 @@
             logger.warn("oriented shapes should define Iqac or Iqabc")
         else:
-            raise ValueError("Expected function Iqac or Iqabc for oriented shape")
+            raise ValueError("Expected Iqac or Iqabc for oriented shape")
 
     # Define the parameter table
@@ -815,14 +824,24 @@
         refs = _call_pars("_v.", partable.form_volume_parameters)
         if is_hollow:
-            call_volume = "#define CALL_VOLUME(_form, _shell, _v) do { _form = form_volume(%s); _shell = shell_volume(%s); } while (0)"%((",".join(refs),)*2)
+            call_volume = (
+                "#define CALL_VOLUME(_form, _shell, _v) "
+                "do { _form = form_volume(%s); _shell = shell_volume(%s); } "
+                "while (0)") % ((",".join(refs),)*2)
         else:
-            call_volume = "#define CALL_VOLUME(_form, _shell, _v) do { _form = _shell = form_volume(%s); } while (0)"%(",".join(refs))
+            call_volume = (
+                "#define CALL_VOLUME(_form, _shell, _v) "
+                "do { _form = _shell = form_volume(%s); } "
+                "while (0)") % (",".join(refs))
         if model_info.effective_radius_type:
-            call_effective_radius = "#define CALL_EFFECTIVE_RADIUS(_mode, _v) effective_radius(_mode, %s)"%(",".join(refs))
+            call_effective_radius = (
+                "#define CALL_EFFECTIVE_RADIUS(_mode, _v) "
+                "effective_radius(_mode, %s)") % (",".join(refs))
     else:
         # Model doesn't have volume.  We could make the kernel run a little
         # faster by not using/transferring the volume normalizations, but
         # the ifdef's reduce readability more than is worthwhile.
-        call_volume = "#define CALL_VOLUME(_form, _shell, _v) do { _form = _shell = 1.0; } while (0)"
+        call_volume = (
+            "#define CALL_VOLUME(_form, _shell, _v) "
+            "do { _form = _shell = 1.0; } while (0)")
     source.append(call_volume)
     source.append(call_effective_radius)
@@ -879,12 +898,11 @@
     source.append("#define MAGNETIC_PARS %s"%",".join(str(k) for k in magpars))
     source.append("#define PROJECTION %d"%PROJECTION)
-    # TODO: allow mixed python/opencl kernels?
-    ocl = _kernels(kernel_code, call_iq, clear_iq, call_iqxy, clear_iqxy, model_info.name)
-    dll = _kernels(kernel_code, call_iq, clear_iq, call_iqxy, clear_iqxy, model_info.name)
-
-    result = {
-        'dll': '\n'.join(source+dll[0]+dll[1]+dll[2]),
-        'opencl': '\n'.join(source+ocl[0]+ocl[1]+ocl[2]),
-    }
+    wrappers = _kernels(kernel_code, call_iq, clear_iq,
+                        call_iqxy, clear_iqxy, model_info.name)
+    code = '\n'.join(source + wrappers[0] + wrappers[1] + wrappers[2])
+
+    # Note: Identical code for dll and opencl.  This may not always be the case
+    # so leave them as separate entries in the returned value.
+    result = {'dll': code, 'opencl': code}
     return result
 
Index: sasmodels/gengauss.py
===================================================================
--- sasmodels/gengauss.py	(revision ff31782cc25cfbfea355acdd6d0204c959b41dcc)
+++ sasmodels/gengauss.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
+"""
+Generate the Gauss-Legendre integration points and save them as a C file.
+"""
 from __future__ import division, print_function
-
-import sys
 
 import numpy as np
@@ -8,4 +9,7 @@
 
 def gengauss(n, path):
+    """
+    Save the Gauss-Legendre integration points for length *n* into file *path*.
+    """
     z, w = leggauss(n)
 
Index: sasmodels/guyou.py
===================================================================
--- sasmodels/guyou.py	(revision b3703f51bafe67afb8acefe986ad7466720f755b)
+++ sasmodels/guyou.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -68,4 +68,5 @@
 
 def ellipticJi(u, v, m):
+    """Returns [sn, cn, dn](u + iv|m)."""
     scalar = np.isscalar(u) and np.isscalar(v) and np.isscalar(m)
     u, v, m = np.broadcast_arrays(u, v, m)
@@ -100,7 +101,6 @@
     ]
 
-# calculate F(phi+ipsi|m).
-# see Abramowitz and Stegun, 17.4.11.
 def ellipticFi(phi, psi, m):
+    """Returns F(phi+ipsi|m). See Abramowitz and Stegun, 17.4.11."""
     if np.any(phi == 0):
         scalar = np.isscalar(phi) and np.isscalar(psi) and np.isscalar(m)
@@ -207,6 +207,6 @@
         plt.plot(x, y, 'g')
 
-    for long in range(-limit, limit+1, step):
-        x, y = guyou_invert(scale*long, scale*long_line)
+    for longitude in range(-limit, limit+1, step):
+        x, y = guyou_invert(scale*longitude, scale*long_line)
         plt.plot(x, y, 'b')
     plt.xlabel('longitude')
Index: sasmodels/jitter.py
===================================================================
--- sasmodels/jitter.py	(revision cff293936894836b818da65a243155cfab9fc5c4)
+++ sasmodels/jitter.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -51,5 +51,9 @@
 
 import numpy as np
-from numpy import pi, cos, sin, sqrt, exp, degrees, radians
+from numpy import pi, cos, sin, sqrt, exp, log, degrees, radians, arccos, arctan2
+
+# Too many complaints about variable names from pylint:
+#    a, b, c, u, v, x, y, z, dx, dy, dz, px, py, pz, R, Rx, Ry, Rz, ...
+# pylint: disable=invalid-name
 
 def draw_beam(axes, view=(0, 0), alpha=0.5, steps=25):
@@ -60,10 +64,10 @@
     #axes.scatter([0]*100,[0]*100,np.linspace(1, -1, 100), alpha=alpha)
 
-    u = np.linspace(0, 2 * np.pi, steps)
+    u = np.linspace(0, 2 * pi, steps)
     v = np.linspace(-1, 1, 2)
 
     r = 0.02
-    x = r*np.outer(np.cos(u), np.ones_like(v))
-    y = r*np.outer(np.sin(u), np.ones_like(v))
+    x = r*np.outer(cos(u), np.ones_like(v))
+    y = r*np.outer(sin(u), np.ones_like(v))
     z = 1.3*np.outer(np.ones_like(u), v)
 
@@ -90,9 +94,9 @@
     """Draw an ellipsoid."""
     a, b, c = size
-    u = np.linspace(0, 2 * np.pi, steps)
-    v = np.linspace(0, np.pi, steps)
-    x = a*np.outer(np.cos(u), np.sin(v))
-    y = b*np.outer(np.sin(u), np.sin(v))
-    z = c*np.outer(np.ones_like(u), np.cos(v))
+    u = np.linspace(0, 2 * pi, steps)
+    v = np.linspace(0, pi, steps)
+    x = a*np.outer(cos(u), sin(v))
+    y = b*np.outer(sin(u), sin(v))
+    z = c*np.outer(np.ones_like(u), cos(v))
     x, y, z = transform_xyz(view, jitter, x, y, z)
 
@@ -167,4 +171,5 @@
 
 def draw_box(axes, size, view):
+    """Draw a wireframe box at a particular view."""
     a, b, c = size
     x = a*np.array([+1, -1, +1, -1, +1, -1, +1, -1])
@@ -172,16 +177,16 @@
     z = c*np.array([+1, +1, +1, +1, -1, -1, -1, -1])
     x, y, z = transform_xyz(view, None, x, y, z)
-    def draw(i, j):
-        axes.plot([x[i],x[j]], [y[i], y[j]], [z[i], z[j]], color='black')
-    draw(0, 1)
-    draw(0, 2)
-    draw(0, 3)
-    draw(7, 4)
-    draw(7, 5)
-    draw(7, 6)
+    def _draw(i, j):
+        axes.plot([x[i], x[j]], [y[i], y[j]], [z[i], z[j]], color='black')
+    _draw(0, 1)
+    _draw(0, 2)
+    _draw(0, 3)
+    _draw(7, 4)
+    _draw(7, 5)
+    _draw(7, 6)
 
 def draw_parallelepiped(axes, size, view, jitter, steps=None,
                         color=(0.6, 1.0, 0.6), alpha=1):
-    """Draw a parallelepiped."""
+    """Draw a parallelepiped surface, with view and jitter."""
     a, b, c = size
     x = a*np.array([+1, -1, +1, -1, +1, -1, +1, -1])
@@ -224,16 +229,18 @@
     ])
 
-def draw_sphere(axes, radius=0.5, steps=25, center=(0,0,0), color='w', alpha=1.):
+def draw_sphere(axes, radius=0.5, steps=25,
+                center=(0, 0, 0), color='w', alpha=1.):
     """Draw a sphere"""
-    u = np.linspace(0, 2 * np.pi, steps)
-    v = np.linspace(0, np.pi, steps)
-
-    x = radius * np.outer(np.cos(u), np.sin(v)) + center[0]
-    y = radius * np.outer(np.sin(u), np.sin(v)) + center[1]
-    z = radius * np.outer(np.ones(np.size(u)), np.cos(v)) + center[2]
+    u = np.linspace(0, 2 * pi, steps)
+    v = np.linspace(0, pi, steps)
+
+    x = radius * np.outer(cos(u), sin(v)) + center[0]
+    y = radius * np.outer(sin(u), sin(v)) + center[1]
+    z = radius * np.outer(np.ones(np.size(u)), cos(v)) + center[2]
     axes.plot_surface(x, y, z, color=color, alpha=alpha)
     #axes.plot_wireframe(x, y, z)
 
 def draw_axes(axes, origin=(-1, -1, -1), length=(2, 2, 2)):
+    """Draw wireframe axes lines, with given origin and length"""
     x, y, z = origin
     dx, dy, dz = length
@@ -243,4 +250,9 @@
 
 def draw_person_on_sphere(axes, view, height=0.5, radius=0.5):
+    """
+    Draw a person on the surface of a sphere.
+
+    *view* indicates (latitude, longitude, orientation)
+    """
     limb_offset = height * 0.05
     head_radius = height * 0.10
@@ -259,7 +271,7 @@
 
     # circle for head
-    u = np.linspace(0, 2 * np.pi, 40)
-    x = head_radius * np.cos(u)
-    z = head_radius * np.sin(u) + head_height
+    u = np.linspace(0, 2 * pi, 40)
+    x = head_radius * cos(u)
+    z = head_radius * sin(u) + head_height
     _draw_part(x, z)
 
@@ -304,5 +316,5 @@
     dtheta, dphi, dpsi = jitter
     base = {'gaussian':3, 'rectangle':sqrt(3), 'uniform':1}[dist]
-    def steps(delta):
+    def _steps(delta):
         if views is None:
             n = max(3, min(25, 2*int(base*delta/5)))
@@ -310,7 +322,7 @@
             n = views
         return base*delta*np.linspace(-1, 1, n) if delta > 0 else [0.]
-    for theta in steps(dtheta):
-        for phi in steps(dphi):
-            for psi in steps(dpsi):
+    for theta in _steps(dtheta):
+        for phi in _steps(dphi):
+            for psi in _steps(dpsi):
                 w = project_weight(theta, phi, 1.0, 1.0)
                 if w > 0:
@@ -319,5 +331,5 @@
     for v in 'xyz':
         a, b, c = size
-        lim = np.sqrt(a**2 + b**2 + c**2)
+        lim = sqrt(a**2 + b**2 + c**2)
         getattr(axes, 'set_'+v+'lim')([-lim, lim])
         #getattr(axes, v+'axis').label.set_text(v)
@@ -390,5 +402,5 @@
         def _project(theta_i, phi_j, psi):
             latitude, longitude = theta_i, phi_j
-            return latitude, longitude, psi
+            return latitude, longitude, psi, 'xyz'
             #return Rx(phi_j)*Ry(theta_i)
         def _weight(theta_i, phi_j, w_i, w_j):
@@ -400,5 +412,5 @@
             longitude = phi_j/scale if abs(phi_j) < abs(scale)*180 else 0
             #print("(%+7.2f, %+7.2f) => (%+7.2f, %+7.2f)"%(theta_i, phi_j, latitude, longitude))
-            return latitude, longitude, psi
+            return latitude, longitude, psi, 'xyz'
             #return Rx(longitude)*Ry(latitude)
         def _project(theta_i, phi_j, w_i, w_j):
@@ -412,5 +424,5 @@
             #latitude, longitude = guyou_invert([theta_i], [phi_j])
             longitude, latitude = guyou_invert([phi_j], [theta_i])
-            return latitude, longitude, psi
+            return latitude, longitude, psi, 'xyz'
             #return Rx(longitude[0])*Ry(latitude[0])
         def _weight(theta_i, phi_j, w_i, w_j):
@@ -420,5 +432,5 @@
         def _project(theta_i, phi_j, psi):
             latitude = sqrt(theta_i**2 + phi_j**2)
-            longitude = degrees(np.arctan2(phi_j, theta_i))
+            longitude = degrees(arctan2(phi_j, theta_i))
             #print("(%+7.2f, %+7.2f) => (%+7.2f, %+7.2f)"%(theta_i, phi_j, latitude, longitude))
             return latitude, longitude, psi-longitude, 'zyz'
@@ -462,8 +474,8 @@
         def _project(theta_i, phi_j, psi):
             radius = min(1, sqrt(theta_i**2 + phi_j**2)/180)
-            latitude = 180-degrees(2*np.arccos(radius))
-            longitude = degrees(np.arctan2(phi_j, theta_i))
+            latitude = 180-degrees(2*arccos(radius))
+            longitude = degrees(arctan2(phi_j, theta_i))
             #print("(%+7.2f, %+7.2f) => (%+7.2f, %+7.2f)"%(theta_i, phi_j, latitude, longitude))
-            return latitude, longitude, psi, "zyz"
+            return latitude, longitude, psi, 'zyz'
             #R = Rz(longitude)*Ry(latitude)*Rz(psi)
             #return R_to_xyz(R)
@@ -487,8 +499,8 @@
     Based on: Shoemakeâs "Euler Angle Conversion", Graphics Gems IV, pp.  222-229
     """
-    phi = np.arctan2(R[1, 2], R[2, 2])
-    theta = np.arctan2(-R[0, 2], np.sqrt(R[0, 0]**2 + R[0, 1]**2))
-    psi = np.arctan2(R[0, 1], R[0, 0])
-    return np.degrees(phi), np.degrees(theta), np.degrees(psi)
+    phi = arctan2(R[1, 2], R[2, 2])
+    theta = arctan2(-R[0, 2], sqrt(R[0, 0]**2 + R[0, 1]**2))
+    psi = arctan2(R[0, 1], R[0, 0])
+    return degrees(phi), degrees(theta), degrees(psi)
 
 def draw_mesh(axes, view, jitter, radius=1.2, n=11, dist='gaussian',
@@ -502,7 +514,7 @@
     def _rotate(theta, phi, z):
         dview = _project(theta, phi, 0.)
-        if len(dview) == 4: # hack for zyz coords
+        if dview[3] == 'zyz':
             return Rz(dview[1])*Ry(dview[0])*z
-        else:
+        else:  # dview[3] == 'xyz':
             return Rx(dview[1])*Ry(dview[0])*z
 
@@ -657,42 +669,77 @@
 #orient_relative_to_beam = orient_relative_to_beam_quaternion
 
+# === Quaterion class definition === BEGIN
 # Simple stand-alone quaternion class
-import numpy as np
-from copy import copy
+
+# Note: this code works but isn't unused since quaternions didn't solve the
+# representation problem.  Leave it here in case we want to revisit this later.
+
+#import numpy as np
 class Quaternion(object):
+    r"""
+    Quaternion(w, r) = w + ir[0] + jr[1] + kr[2]
+
+    Quaternion.from_angle_axis(theta, r) for a rotation of angle theta about
+    an axis oriented toward the direction r.  This defines a unit quaternion,
+    normalizing $r$ to the unit vector $\hat r$, and setting quaternion
+    $Q = \cos \theta + \sin \theta \hat r$
+
+    Quaternion objects can be multiplied, which applies a rotation about the
+    given axis, allowing composition of rotations without risk of gimbal lock.
+    The resulting quaternion is applied to a set of points using *Q.rot(v)*.
+    """
     def __init__(self, w, r):
-         self.w = w
-         self.r = np.asarray(r, dtype='d')
+        self.w = w
+        self.r = np.asarray(r, dtype='d')
+
     @staticmethod
     def from_angle_axis(theta, r):
-         theta = np.radians(theta)/2
-         r = np.asarray(r)
-         w = np.cos(theta)
-         r = np.sin(theta)*r/np.dot(r,r)
-         return Quaternion(w, r)
+        """Build quaternion as rotation theta about axis r"""
+        theta = np.radians(theta)/2
+        r = np.asarray(r)
+        w = np.cos(theta)
+        r = np.sin(theta)*r/np.dot(r, r)
+        return Quaternion(w, r)
+
     def __mul__(self, other):
+        """Multiply quaterions"""
         if isinstance(other, Quaternion):
             w = self.w*other.w - np.dot(self.r, other.r)
             r = self.w*other.r + other.w*self.r + np.cross(self.r, other.r)
             return Quaternion(w, r)
+        raise NotImplementedError("Quaternion * non-quaternion not implemented")
+
     def rot(self, v):
+        """Transform point *v* by quaternion"""
         v = np.asarray(v).T
         use_transpose = (v.shape[-1] != 3)
-        if use_transpose: v = v.T
+        if use_transpose:
+            v = v.T
         v = v + np.cross(2*self.r, np.cross(self.r, v) + self.w*v)
         #v = v + 2*self.w*np.cross(self.r, v) + np.cross(2*self.r, np.cross(self.r, v))
-        if use_transpose: v = v.T
+        if use_transpose:
+            v = v.T
         return v.T
+
     def conj(self):
+        """Conjugate quaternion"""
         return Quaternion(self.w, -self.r)
+
     def inv(self):
+        """Inverse quaternion"""
         return self.conj()/self.norm()**2
+
     def norm(self):
+        """Quaternion length"""
         return np.sqrt(self.w**2 + np.sum(self.r**2))
+
     def __str__(self):
         return "%g%+gi%+gj%+gk"%(self.w, self.r[0], self.r[1], self.r[2])
+
 def test_qrot():
-    # Define rotation of 60 degrees around an axis in y-z that is 60 degrees from y.
-    # The rotation axis is determined by rotating the point [0, 1, 0] about x.
+    """Quaternion checks"""
+    # Define rotation of 60 degrees around an axis in y-z that is 60 degrees
+    # from y.  The rotation axis is determined by rotating the point [0, 1, 0]
+    # about x.
     ax = Quaternion.from_angle_axis(60, [1, 0, 0]).rot([0, 1, 0])
     q = Quaternion.from_angle_axis(60, ax)
@@ -704,4 +751,5 @@
 #test_qrot()
 #import sys; sys.exit()
+# === Quaterion class definition === END
 
 # translate between number of dimension of dispersity and the number of
@@ -725,14 +773,15 @@
     or the top of the range, depending on whether *mode* is 'central' or 'top'.
     """
-    if portion == 1.0:
-        return data.min(), data.max()
-    elif mode == 'central':
-        data = np.sort(data.flatten())
-        offset = int(portion*len(data)/2 + 0.5)
-        return data[offset], data[-offset]
-    elif mode == 'top':
-        data = np.sort(data.flatten())
-        offset = int(portion*len(data) + 0.5)
-        return data[offset], data[-1]
+    if portion < 1.0:
+        if mode == 'central':
+            data = np.sort(data.flatten())
+            offset = int(portion*len(data)/2 + 0.5)
+            return data[offset], data[-offset]
+        if mode == 'top':
+            data = np.sort(data.flatten())
+            offset = int(portion*len(data) + 0.5)
+            return data[offset], data[-1]
+    # Default: full range
+    return data.min(), data.max()
 
 def draw_scattering(calculator, axes, view, jitter, dist='gaussian'):
@@ -765,9 +814,9 @@
 
     # compute the pattern
-    qx, qy = calculator._data.x_bins, calculator._data.y_bins
+    qx, qy = calculator.qxqy
     Iqxy = calculator(**pars).reshape(len(qx), len(qy))
 
     # scale it and draw it
-    Iqxy = np.log(Iqxy)
+    Iqxy = log(Iqxy)
     if calculator.limits:
         # use limits from orientation (0,0,0)
@@ -781,12 +830,13 @@
     #qx, qy = np.meshgrid(qx, qy)
     if 0:
-        from matplotlib import cm
         level = np.asarray(255*(Iqxy - vmin)/(vmax - vmin), 'i')
         level[level < 0] = 0
+        from matplotlib import pylab as plt
         colors = plt.get_cmap()(level)
+        #from matplotlib import cm
         #colors = cm.coolwarm(level)
         #colors = cm.gist_yarg(level)
         #colors = cm.Wistia(level)
-        colors[level<=0, 3] = 0.  # set floor to transparent
+        colors[level <= 0, 3] = 0.  # set floor to transparent
         x, y = np.meshgrid(qx/qx.max(), qy/qy.max())
         axes.plot_surface(x, y, -1.1*np.ones_like(x), facecolors=colors)
@@ -822,4 +872,7 @@
     calculator = DirectModel(data, model)
 
+    # Remember the data axes so we can plot the results
+    calculator.qxqy = (q, q)
+
     # stuff the values for non-orientation parameters into the calculator
     calculator.pars = pars.copy()
@@ -829,5 +882,5 @@
     # under rotation or angular dispersion
     Iqxy = calculator(theta=0, phi=0, psi=0, **calculator.pars)
-    Iqxy = np.log(Iqxy)
+    Iqxy = log(Iqxy)
     vmin, vmax = clipped_range(Iqxy, 0.95, mode='top')
     calculator.limits = vmin, vmax+1
@@ -963,5 +1016,5 @@
     PLOT_ENGINE(calculator, draw_shape, size, view, jitter, dist, mesh, projection)
 
-def mpl_plot(calculator, draw_shape, size, view, jitter, dist, mesh, projection):
+def _mpl_plot(calculator, draw_shape, size, view, jitter, dist, mesh, projection):
     # Note: travis-ci does not support mpl_toolkits.mplot3d, but this shouldn't be
     # an issue since we are lazy-loading the package on a path that isn't tested.
@@ -1020,5 +1073,5 @@
 
     ## callback to draw the new view
-    def update(val, axis=None):
+    def _update(val, axis=None):
         view = stheta.val, sphi.val, spsi.val
         jitter = sdtheta.val, sdphi.val, sdpsi.val
@@ -1047,13 +1100,13 @@
 
     ## bind control widgets to view updater
-    stheta.on_changed(lambda v: update(v, 'theta'))
-    sphi.on_changed(lambda v: update(v, 'phi'))
-    spsi.on_changed(lambda v: update(v, 'psi'))
-    sdtheta.on_changed(lambda v: update(v, 'dtheta'))
-    sdphi.on_changed(lambda v: update(v, 'dphi'))
-    sdpsi.on_changed(lambda v: update(v, 'dpsi'))
+    stheta.on_changed(lambda v: _update(v, 'theta'))
+    sphi.on_changed(lambda v: _update(v, 'phi'))
+    spsi.on_changed(lambda v: _update(v, 'psi'))
+    sdtheta.on_changed(lambda v: _update(v, 'dtheta'))
+    sdphi.on_changed(lambda v: _update(v, 'dphi'))
+    sdpsi.on_changed(lambda v: _update(v, 'dpsi'))
 
     ## initialize view
-    update(None, 'phi')
+    _update(None, 'phi')
 
     ## go interactive
@@ -1062,4 +1115,11 @@
 
 def map_colors(z, kw):
+    """
+    Process matplotlib-style colour arguments.
+
+    Pulls 'cmap', 'alpha', 'vmin', and 'vmax' from th *kw* dictionary, setting
+    the *kw['color']* to an RGB array.  These are ignored if 'c' or 'color' are
+    set inside *kw*.
+    """
     from matplotlib import cm
 
@@ -1083,8 +1143,10 @@
 
 def make_vec(*args):
+    """Turn all elements of *args* into numpy arrays"""
     #return [np.asarray(v, 'd').flatten() for v in args]
     return [np.asarray(v, 'd') for v in args]
 
 def make_image(z, kw):
+    """Convert numpy array *z* into a *PIL* RGB image."""
     import PIL.Image
     from matplotlib import cm
@@ -1113,5 +1175,5 @@
     'b': 'blue',
 }
-def ipv_fix_color(kw):
+def _ipv_fix_color(kw):
     alpha = kw.pop('alpha', None)
     color = kw.get('color', None)
@@ -1132,5 +1194,5 @@
             kw['color'] = color
 
-def ipv_set_transparency(kw, obj):
+def _ipv_set_transparency(kw, obj):
     color = kw.get('color', None)
     if (isinstance(color, np.ndarray)
@@ -1141,7 +1203,13 @@
 
 def ipv_axes():
+    """
+    Build a matplotlib style Axes interface for ipyvolume
+    """
     import ipyvolume as ipv
 
-    class Plotter:
+    class Axes:
+        """
+        Matplotlib Axes3D style interface to ipyvolume renderer.
+        """
         # transparency can be achieved by setting the following:
         #    mesh.color = [r, g, b, alpha]
@@ -1156,27 +1224,31 @@
         # maybe need to synchronize update of x/y/z to avoid shimmy when moving
         def plot(self, x, y, z, **kw):
-            ipv_fix_color(kw)
+            """mpl style plot interface for ipyvolume"""
+            _ipv_fix_color(kw)
             x, y, z = make_vec(x, y, z)
             ipv.plot(x, y, z, **kw)
         def plot_surface(self, x, y, z, **kw):
+            """mpl style plot_surface interface for ipyvolume"""
             facecolors = kw.pop('facecolors', None)
             if facecolors is not None:
                 kw['color'] = facecolors
-            ipv_fix_color(kw)
+            _ipv_fix_color(kw)
             x, y, z = make_vec(x, y, z)
             h = ipv.plot_surface(x, y, z, **kw)
-            ipv_set_transparency(kw, h)
+            _ipv_set_transparency(kw, h)
             #h.material.side = "DoubleSide"
             return h
         def plot_trisurf(self, x, y, triangles=None, Z=None, **kw):
+            """mpl style plot_trisurf interface for ipyvolume"""
             kw.pop('linewidth', None)
-            ipv_fix_color(kw)
+            _ipv_fix_color(kw)
             x, y, z = make_vec(x, y, Z)
             if triangles is not None:
                 triangles = np.asarray(triangles)
             h = ipv.plot_trisurf(x, y, z, triangles=triangles, **kw)
-            ipv_set_transparency(kw, h)
+            _ipv_set_transparency(kw, h)
             return h
         def scatter(self, x, y, z, **kw):
+            """mpl style scatter interface for ipyvolume"""
             x, y, z = make_vec(x, y, z)
             map_colors(z, kw)
@@ -1184,10 +1256,12 @@
             kw['marker'] = _IPV_MARKERS.get(marker, marker)
             h = ipv.scatter(x, y, z, **kw)
-            ipv_set_transparency(kw, h)
+            _ipv_set_transparency(kw, h)
             return h
         def contourf(self, x, y, v, zdir='z', offset=0, levels=None, **kw):
+            """mpl style contourf interface for ipyvolume"""
             # Don't use contour for now (although we might want to later)
             self.pcolor(x, y, v, zdir='z', offset=offset, **kw)
         def pcolor(self, x, y, v, zdir='z', offset=0, **kw):
+            """mpl style pcolor interface for ipyvolume"""
             x, y, v = make_vec(x, y, v)
             image = make_image(v, kw)
@@ -1200,22 +1274,29 @@
             v = np.array([[0., 0], [1, 1]])
             h = ipv.plot_mesh(x, y, z, u=u, v=v, texture=image, wireframe=False)
-            ipv_set_transparency(kw, h)
+            _ipv_set_transparency(kw, h)
             h.material.side = "DoubleSide"
             return h
         def text(self, *args, **kw):
+            """mpl style text interface for ipyvolume"""
             pass
         def set_xlim(self, limits):
+            """mpl style set_xlim interface for ipyvolume"""
             ipv.xlim(*limits)
         def set_ylim(self, limits):
+            """mpl style set_ylim interface for ipyvolume"""
             ipv.ylim(*limits)
         def set_zlim(self, limits):
+            """mpl style set_zlim interface for ipyvolume"""
             ipv.zlim(*limits)
         def set_axes_on(self):
+            """mpl style set_axes_on interface for ipyvolume"""
             ipv.style.axis_on()
         def set_axis_off(self):
+            """mpl style set_axes_off interface for ipyvolume"""
             ipv.style.axes_off()
-    return Plotter()
-
-def ipv_plot(calculator, draw_shape, size, view, jitter, dist, mesh, projection):
+    return Axes()
+
+def _ipv_plot(calculator, draw_shape, size, view, jitter, dist, mesh, projection):
+    from IPython.display import display
     import ipywidgets as widgets
     import ipyvolume as ipv
@@ -1223,5 +1304,5 @@
     axes = ipv_axes()
 
-    def draw(view, jitter):
+    def _draw(view, jitter):
         camera = ipv.gcf().camera
         #print(ipv.gcf().__dict__.keys())
@@ -1271,8 +1352,8 @@
     #widgets.interact(update, Îž=trange, Ï=prange, Ï=prange, ÎÎž=dtrange, ÎÏ=dprange, ÎÏ=dprange)
 
-    def update(theta, phi, psi, dtheta, dphi, dpsi):
-        draw(view=(theta, phi, psi), jitter=(dtheta, dphi, dpsi))
-
-    def slider(name, slice, init=0.):
+    def _update(theta, phi, psi, dtheta, dphi, dpsi):
+        _draw(view=(theta, phi, psi), jitter=(dtheta, dphi, dpsi))
+
+    def _slider(name, slice, init=0.):
         return widgets.FloatSlider(
             value=init,
@@ -1288,10 +1369,10 @@
             readout_format='.1f',
             )
-    theta = slider(u'Îž', trange, view[0])
-    phi = slider(u'Ï', prange, view[1])
-    psi = slider(u'Ï', prange, view[2])
-    dtheta = slider(u'ÎÎž', dtrange, jitter[0])
-    dphi = slider(u'ÎÏ', dprange, jitter[1])
-    dpsi = slider(u'ÎÏ', dprange, jitter[2])
+    theta = _slider(u'Îž', trange, view[0])
+    phi = _slider(u'Ï', prange, view[1])
+    psi = _slider(u'Ï', prange, view[2])
+    dtheta = _slider(u'ÎÎž', dtrange, jitter[0])
+    dphi = _slider(u'ÎÏ', dprange, jitter[1])
+    dpsi = _slider(u'ÎÏ', dprange, jitter[2])
     fields = {
         'theta': theta, 'phi': phi, 'psi': psi,
@@ -1303,21 +1384,27 @@
     ])
 
-    out = widgets.interactive_output(update, fields)
+    out = widgets.interactive_output(_update, fields)
     display(ui, out)
 
 
 _ENGINES = {
-    "matplotlib": mpl_plot,
-    "mpl": mpl_plot,
-    #"plotly": plotly_plot,
-    "ipvolume": ipv_plot,
-    "ipv": ipv_plot,
+    "matplotlib": _mpl_plot,
+    "mpl": _mpl_plot,
+    #"plotly": _plotly_plot,
+    "ipvolume": _ipv_plot,
+    "ipv": _ipv_plot,
 }
 PLOT_ENGINE = _ENGINES["matplotlib"]
 def set_plotter(name):
+    """
+    Setting the plotting engine to matplotlib/ipyvolume or equivalently mpl/ipv.
+    """
     global PLOT_ENGINE
     PLOT_ENGINE = _ENGINES[name]
 
 def main():
+    """
+    Command line interface to the jitter viewer.
+    """
     parser = argparse.ArgumentParser(
         description="Display jitter",
Index: sasmodels/kernel.py
===================================================================
--- sasmodels/kernel.py	(revision 3199b177cb91c6bd74017b7f942ddbbbc9bb46c0)
+++ sasmodels/kernel.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -25,21 +25,41 @@
 
 class KernelModel(object):
+    """
+    Model definition for the compute engine.
+    """
     info = None  # type: ModelInfo
     dtype = None # type: np.dtype
     def make_kernel(self, q_vectors):
         # type: (List[np.ndarray]) -> "Kernel"
+        """
+        Instantiate a kernel for evaluating the model at *q_vectors*.
+        """
         raise NotImplementedError("need to implement make_kernel")
 
     def release(self):
         # type: () -> None
+        """
+        Free resources associated with the kernel.
+        """
         pass
 
 
 class Kernel(object):
-    #: kernel dimension, either "1d" or "2d"
+    """
+    Instantiated model for the compute engine, applied to a particular *q*.
+
+    Subclasses should define :meth:`_call_kernel` to evaluate the kernel over
+    its inputs.
+    """
+    #: Kernel dimension, either "1d" or "2d".
     dim = None  # type: str
+    #: Model info.
     info = None  # type: ModelInfo
-    results = None # type: List[np.ndarray]
+    #: Numerical precision for the computation.
     dtype = None  # type: np.dtype
+    #: q values at which the kernel is to be evaluated
+    q_input = None  # type: Any
+    #: Place to hold result of :meth:`_call_kernel` for subclass.
+    result = None # type: np.ndarray
 
     def Iq(self, call_details, values, cutoff, magnetic):
@@ -57,6 +77,6 @@
         built into the model, and do not need an additional scale.
         """
-        _, F2, _, shell_volume, _ = self.Fq(call_details, values, cutoff, magnetic,
-                              effective_radius_type=0)
+        _, F2, _, shell_volume, _ = self.Fq(call_details, values, cutoff,
+                                            magnetic, effective_radius_type=0)
         combined_scale = values[0]/shell_volume
         background = values[1]
@@ -64,5 +84,6 @@
     __call__ = Iq
 
-    def Fq(self, call_details, values, cutoff, magnetic, effective_radius_type=0):
+    def Fq(self, call_details, values, cutoff, magnetic,
+           effective_radius_type=0):
         # type: (CallDetails, np.ndarray, np.ndarray, float, bool, int) -> np.ndarray
         r"""
@@ -81,5 +102,5 @@
         .. math::
 
-            I(q) = \text{scale} * P (1 + <F>^2/<F^2> (S - 1)) + \text{background}
+            I(q) = \text{scale} P (1 + <F>^2/<F^2> (S - 1)) + \text{background}
                  = \text{scale}/<V> (<F^2> + <F>^2 (S - 1)) + \text{background}
 
@@ -90,5 +111,5 @@
         .. math::
 
-            P(q) = \frac{\sum w_k F^2(q, x_k) / \sum w_k}{\sum w_k V_k / \sum w_k}
+            P(q)=\frac{\sum w_k F^2(q, x_k) / \sum w_k}{\sum w_k V_k / \sum w_k}
 
         The form factor itself is scaled by volume and contrast to compute the
@@ -131,5 +152,6 @@
         hollow and solid shapes.
         """
-        self._call_kernel(call_details, values, cutoff, magnetic, effective_radius_type)
+        self._call_kernel(call_details, values, cutoff, magnetic,
+                          effective_radius_type)
         #print("returned",self.q_input.q, self.result)
         nout = 2 if self.info.have_Fq and self.dim == '1d' else 1
@@ -146,5 +168,6 @@
         if shell_volume == 0.:
             shell_volume = 1.
-        F1 = self.result[1:nout*self.q_input.nq:nout]/total_weight if nout == 2 else None
+        F1 = (self.result[1:nout*self.q_input.nq:nout]/total_weight
+              if nout == 2 else None)
         F2 = self.result[0:nout*self.q_input.nq:nout]/total_weight
         return F1, F2, effective_radius, shell_volume, form_volume/shell_volume
@@ -152,7 +175,11 @@
     def release(self):
         # type: () -> None
+        """
+        Free resources associated with the kernel instance.
+        """
         pass
 
-    def _call_kernel(self, call_details, values, cutoff, magnetic, effective_radius_type):
+    def _call_kernel(self, call_details, values, cutoff, magnetic,
+                     effective_radius_type):
         # type: (CallDetails, np.ndarray, np.ndarray, float, bool, int) -> np.ndarray
         """
Index: sasmodels/kernelcl.py
===================================================================
--- sasmodels/kernelcl.py	(revision 3199b177cb91c6bd74017b7f942ddbbbc9bb46c0)
+++ sasmodels/kernelcl.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -53,4 +53,5 @@
 from __future__ import print_function
 
+import sys
 import os
 import warnings
@@ -61,5 +62,4 @@
     from time import perf_counter as clock
 except ImportError: # CRUFT: python < 3.3
-    import sys
     if sys.platform.count("darwin") > 0:
         from time import time as clock
@@ -99,4 +99,5 @@
 # CRUFT: pyopencl < 2017.1 (as of June 2016 needs quotes around include path).
 def quote_path(v):
+    # type: (str) -> str
     """
     Quote the path if it is not already quoted.
@@ -110,11 +111,12 @@
 
 def fix_pyopencl_include():
+    # type: (None) -> None
     """
     Monkey patch pyopencl to allow spaces in include file path.
     """
-    import pyopencl as cl
-    if hasattr(cl, '_DEFAULT_INCLUDE_OPTIONS'):
-        cl._DEFAULT_INCLUDE_OPTIONS = [
-            quote_path(v) for v in cl._DEFAULT_INCLUDE_OPTIONS
+    import pyopencl
+    if hasattr(pyopencl, '_DEFAULT_INCLUDE_OPTIONS'):
+        pyopencl._DEFAULT_INCLUDE_OPTIONS = [
+            quote_path(v) for v in pyopencl._DEFAULT_INCLUDE_OPTIONS
             ]
 
@@ -147,4 +149,6 @@
 
 def use_opencl():
+    # type: () -> bool
+    """Return True if OpenCL is the default computational engine"""
     sas_opencl = os.environ.get("SAS_OPENCL", "OpenCL").lower()
     return HAVE_OPENCL and sas_opencl != "none" and not sas_opencl.startswith("cuda")
@@ -153,4 +157,5 @@
 ENV = None
 def reset_environment():
+    # type: () -> None
     """
     Call to create a new OpenCL context, such as after a change to SAS_OPENCL.
@@ -323,8 +328,10 @@
             return [cl.create_some_context(interactive=False)]
         except Exception as exc:
+            # TODO: Should warnings instead by put into logging.warn?
             warnings.warn(str(exc))
-            warnings.warn("pyopencl.create_some_context() failed.  The "
-                "environment variable 'SAS_OPENCL' or 'PYOPENCL_CTX' might "
-                "not be set correctly")
+            warnings.warn(
+                "pyopencl.create_some_context() failed.  The environment "
+                "variable 'SAS_OPENCL' or 'PYOPENCL_CTX' might not be set "
+                "correctly")
 
     return _get_default_context()
Index: sasmodels/kernelcuda.py
===================================================================
--- sasmodels/kernelcuda.py	(revision fa26e7834aa4332ef6ffc8eaf2a64c9ea1b51a64)
+++ sasmodels/kernelcuda.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -59,5 +59,4 @@
 
 import os
-import warnings
 import logging
 import time
@@ -110,4 +109,6 @@
 
 def use_cuda():
+    # type: None -> bool
+    """Returns True if CUDA is the default compute engine."""
     sas_opencl = os.environ.get("SAS_OPENCL", "CUDA").lower()
     return HAVE_CUDA and sas_opencl.startswith("cuda")
@@ -136,5 +137,5 @@
         if not HAVE_CUDA:
             raise RuntimeError("CUDA startup failed with ***"
-                            + CUDA_ERROR + "***; using C compiler instead")
+                               + CUDA_ERROR + "***; using C compiler instead")
         reset_environment()
         if ENV is None:
@@ -198,5 +199,6 @@
     """
     for match in FUNCTION_PATTERN.finditer(source):
-        print(match.group('qualifiers').replace('\n',r'\n'), match.group('function'), '(')
+        print(match.group('qualifiers').replace('\n', r'\n'),
+              match.group('function'), '(')
     return source
 
@@ -260,4 +262,5 @@
 
     def release(self):
+        """Free the CUDA device associated with this context."""
         if self.context is not None:
             self.context.pop()
Index: sasmodels/kerneldll.py
===================================================================
--- sasmodels/kerneldll.py	(revision 3199b177cb91c6bd74017b7f942ddbbbc9bb46c0)
+++ sasmodels/kerneldll.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -174,5 +174,5 @@
 
 
-def compile(source, output):
+def compile_model(source, output):
     # type: (str, str) -> None
     """
@@ -265,5 +265,5 @@
         with os.fdopen(system_fd, "w") as file_handle:
             file_handle.write(source)
-        compile(source=filename, output=dll)
+        compile_model(source=filename, output=dll)
         # Comment the following to keep the generated C file.
         # Note: If there is a syntax error then compile raises an error
Index: sasmodels/kernelpy.py
===================================================================
--- sasmodels/kernelpy.py	(revision 3199b177cb91c6bd74017b7f942ddbbbc9bb46c0)
+++ sasmodels/kernelpy.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -16,5 +16,7 @@
     from numpy import cbrt
 except ImportError:
-    def cbrt(x): return x ** (1.0/3.0)
+    def cbrt(x):
+        """Return cubed root of x."""
+        return x ** (1.0/3.0)
 
 from .generate import F64
@@ -43,7 +45,8 @@
         self.info = model_info
         self.dtype = np.dtype('d')
-        logger.info("make python model " + self.info.name)
+        logger.info("make python model %s", self.info.name)
 
     def make_kernel(self, q_vectors):
+        """Instantiate the python kernel with input *q_vectors*"""
         q_input = PyInput(q_vectors, dtype=F64)
         return PyKernel(self.info, q_input)
Index: sasmodels/list_pars.py
===================================================================
--- sasmodels/list_pars.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/list_pars.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -16,5 +16,5 @@
 from .compare import columnize
 
-def find_pars(type=None):
+def find_pars(kind=None):
     """
     Find all parameters in all models.
@@ -26,10 +26,10 @@
         model_info = load_model_info(name)
         for p in model_info.parameters.kernel_parameters:
-            if type is None or p.type == type:
+            if kind is None or p.type == kind:
                 partable.setdefault(p.name, [])
                 partable[p.name].append(name)
     return partable
 
-def list_pars(names_only=True, type=None):
+def list_pars(names_only=True, kind=None):
     """
     Print all parameters in all models.
@@ -38,5 +38,5 @@
     occurs in.
     """
-    partable = find_pars(type)
+    partable = find_pars(kind)
     if names_only:
         print(columnize(list(sorted(partable.keys()))))
@@ -57,12 +57,12 @@
         help="list models which use this argument")
     parser.add_argument(
-        'type', default="any", nargs='?',
+        'kind', default="any", nargs='?',
         metavar="volume|orientation|sld|none|any",
         choices=['volume', 'orientation', 'sld', None, 'any'],
-        type=lambda v: None if v == 'any' else '' if v == 'none' else v,
-        help="only list arguments of the given type")
+        type=lambda v: None if v == 'any' else None if v == 'none' else v,
+        help="only list arguments of the given kind")
     args = parser.parse_args()
 
-    list_pars(names_only=not args.verbose, type=args.type)
+    list_pars(names_only=not args.verbose, kind=args.kind)
 
 if __name__ == "__main__":
Index: sasmodels/mixture.py
===================================================================
--- sasmodels/mixture.py	(revision b171acdab344b2827f9bdca29d66411c2484991f)
+++ sasmodels/mixture.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -120,4 +120,5 @@
 
     def random():
+        """Random set of model parameters for mixture model"""
         combined_pars = {}
         for k, part in enumerate(parts):
@@ -149,4 +150,7 @@
 
 class MixtureModel(KernelModel):
+    """
+    Model definition for mixture of models.
+    """
     def __init__(self, model_info, parts):
         # type: (ModelInfo, List[KernelModel]) -> None
@@ -165,15 +169,18 @@
         kernels = [part.make_kernel(q_vectors) for part in self.parts]
         return MixtureKernel(self.info, kernels)
+    make_kernel.__doc__ = KernelModel.make_kernel.__doc__
 
     def release(self):
         # type: () -> None
-        """
-        Free resources associated with the model.
-        """
+        """Free resources associated with the model."""
         for part in self.parts:
             part.release()
+    release.__doc__ = KernelModel.release.__doc__
 
 
 class MixtureKernel(Kernel):
+    """
+    Instantiated kernel for mixture of models.
+    """
     def __init__(self, model_info, kernels):
         # type: (ModelInfo, List[Kernel]) -> None
@@ -185,5 +192,5 @@
         self.results = []  # type: List[np.ndarray]
 
-    def __call__(self, call_details, values, cutoff, magnetic):
+    def Iq(self, call_details, values, cutoff, magnetic):
         # type: (CallDetails, np.ndarray, np.ndarry, float, bool) -> np.ndarray
         scale, background = values[0:2]
@@ -191,5 +198,5 @@
         # remember the parts for plotting later
         self.results = []  # type: List[np.ndarray]
-        parts = MixtureParts(self.info, self.kernels, call_details, values)
+        parts = _MixtureParts(self.info, self.kernels, call_details, values)
         for kernel, kernel_details, kernel_values in parts:
             #print("calling kernel", kernel.info.name)
@@ -208,11 +215,21 @@
         return scale*total + background
 
+    Iq.__doc__ = Kernel.Iq.__doc__
+    __call__ = Iq
+
     def release(self):
         # type: () -> None
+        """Free resources associated with the kernel."""
         for k in self.kernels:
             k.release()
 
 
-class MixtureParts(object):
+# Note: _MixtureParts doesn't implement iteration correctly, and only allows
+# a single iterator to be active at once.  It doesn't matter in this case
+# since _MixtureParts is only used in one place, but it is not clean style.
+class _MixtureParts(object):
+    """
+    Mixture component iterator.
+    """
     def __init__(self, model_info, kernels, call_details, values):
         # type: (ModelInfo, List[Kernel], CallDetails, np.ndarray) -> None
@@ -223,4 +240,9 @@
         self.values = values
         self.spin_index = model_info.parameters.npars + 2
+        # The following are redefined by __iter__, but set them here so that
+        # lint complains a little less.
+        self.part_num = -1
+        self.par_index = -1
+        self.mag_index = -1
         #call_details.show(values)
 
Index: sasmodels/model_test.py
===================================================================
--- sasmodels/model_test.py	(revision d92182f88244a4ce39a4ef855ac8295e073ff65f)
+++ sasmodels/model_test.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -61,5 +61,4 @@
 import numpy as np  # type: ignore
 
-from . import core
 from .core import list_models, load_model_info, build_model
 from .direct_model import call_kernel, call_Fq
@@ -137,8 +136,8 @@
         test_method_name = "test_%s_python" % model_info.id
         test = ModelTestCase(test_name, model_info,
-                                test_method_name,
-                                platform="dll",  # so that
-                                dtype="double",
-                                stash=stash)
+                             test_method_name,
+                             platform="dll",  # so that
+                             dtype="double",
+                             stash=stash)
         suite.addTest(test)
     else:   # kernel implemented in C
@@ -149,8 +148,8 @@
             test_method_name = "test_%s_dll" % model_info.id
             test = ModelTestCase(test_name, model_info,
-                                    test_method_name,
-                                    platform="dll",
-                                    dtype="double",
-                                    stash=stash)
+                                 test_method_name,
+                                 platform="dll",
+                                 dtype="double",
+                                 stash=stash)
             suite.addTest(test)
 
@@ -164,7 +163,7 @@
             # presence of *single=False* in the model file.
             test = ModelTestCase(test_name, model_info,
-                                    test_method_name,
-                                    platform="ocl", dtype=None,
-                                    stash=stash)
+                                 test_method_name,
+                                 platform="ocl", dtype=None,
+                                 stash=stash)
             #print("defining", test_name)
             suite.addTest(test)
@@ -179,7 +178,7 @@
             # presence of *single=False* in the model file.
             test = ModelTestCase(test_name, model_info,
-                                    test_method_name,
-                                    platform="cuda", dtype=None,
-                                    stash=stash)
+                                 test_method_name,
+                                 platform="cuda", dtype=None,
+                                 stash=stash)
             #print("defining", test_name)
             suite.addTest(test)
@@ -413,4 +412,12 @@
 # CRUFT: old interface; should be deprecated and removed
 def run_one(model_name):
+    # type: (str) -> str
+    """
+    [Deprecated] Run the tests associated with *model_name*.
+
+    Use the following instead::
+
+        succss, output = check_model(load_model_info(model_name))
+    """
     # msg = "use check_model(model_info) rather than run_one(model_name)"
     # warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
@@ -421,9 +428,9 @@
         return output
 
-    success, output = check_model(model_info)
+    _, output = check_model(model_info)
     return output
 
 def check_model(model_info):
-    # type: (ModelInfo) -> str
+    # type: (ModelInfo) -> Tuple[bool, str]
     """
     Run the tests for a single model, capturing the output.
@@ -492,5 +499,5 @@
         loaders.append('cuda')
     tests = make_suite(loaders, ['all'])
-    def build_test(test):
+    def _build_test(test):
         # In order for nosetest to show the test name, wrap the test.run_all
         # instance in function that takes the test name as a parameter which
@@ -510,5 +517,5 @@
 
     for test in tests:
-        yield build_test(test)
+        yield _build_test(test)
 
 
@@ -539,19 +546,19 @@
                         "models will be tested.  See core.list_models() for "
                         "names of other groups, such as 'py' or 'single'.")
-    args, models = parser.parse_known_args()
-
-    if args.engine == "opencl":
+    opts = parser.parse_args()
+
+    if opts.engine == "opencl":
         if not use_opencl():
             print("opencl is not available")
             return 1
         loaders = ['opencl']
-    elif args.engine == "dll":
+    elif opts.engine == "dll":
         loaders = ["dll"]
-    elif args.engine == "cuda":
+    elif opts.engine == "cuda":
         if not use_cuda():
             print("cuda is not available")
             return 1
         loaders = ['cuda']
-    elif args.engine == "all":
+    elif opts.engine == "all":
         loaders = ['dll']
         if use_opencl():
@@ -560,9 +567,9 @@
             loaders.append('cuda')
     else:
-        print("unknown engine " + args.engine)
+        print("unknown engine " + opts.engine)
         return 1
 
-    runner = TestRunner(verbosity=args.verbose, **test_args)
-    result = runner.run(make_suite(loaders, args.models))
+    runner = TestRunner(verbosity=opts.verbose, **test_args)
+    result = runner.run(make_suite(loaders, opts.models))
     return 1 if result.failures or result.errors else 0
 
Index: sasmodels/modelinfo.py
===================================================================
--- sasmodels/modelinfo.py	(revision d8eaa3df1322c45c7c27c76e3029929844afb9b9)
+++ sasmodels/modelinfo.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -1004,11 +1004,13 @@
     #: *have_Fq* is True, then Iq should return an interleaved array of
     #: $[\sum F(q_1), \sum F^2(q_1), \ldots, \sum F(q_n), \sum F^2(q_n)]$.
-    Iq = None               # type: Union[None, str, Callable[[np.ndarray], np.ndarray]]
+    Iq = None               # type: Union[None, str, Callable[[...], np.ndarray]]
+    #: Returns *I(qx, qy, a, b, ...)*.  The interface follows :attr:`Iq`.
+    Iqxy = None             # type: Union[None, str, Callable[[...], np.ndarray]]
     #: Returns *I(qab, qc, a, b, ...)*.  The interface follows :attr:`Iq`.
-    Iqac = None             # type: Union[None, str, Callable[[np.ndarray], np.ndarray]]
+    Iqac = None             # type: Union[None, str, Callable[[...], np.ndarray]]
     #: Returns *I(qa, qb, qc, a, b, ...)*.  The interface follows :attr:`Iq`.
-    Iqabc = None            # type: Union[None, str, Callable[[np.ndarray], np.ndarray]]
+    Iqabc = None            # type: Union[None, str, Callable[[...], np.ndarray]]
     #: Returns *I(qx, qy, a, b, ...)*.  The interface follows :attr:`Iq`.
-    Imagnetic = None        # type: Union[None, str, Callable[[np.ndarray], np.ndarray]]
+    Imagnetic = None        # type: Union[None, str, Callable[[...], np.ndarray]]
     #: Returns a model profile curve *x, y*.  If *profile* is defined, this
     #: curve will appear in response to the *Show* button in SasView.  Use
Index: sasmodels/models/_spherepy.py
===================================================================
--- sasmodels/models/_spherepy.py	(revision 304c775207a9dbbadb23ad414848454d66b556d1)
+++ sasmodels/models/_spherepy.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -69,10 +69,13 @@
 
 def form_volume(radius):
+    """Calculate volume for sphere"""
     return 1.333333333333333 * pi * radius ** 3
 
 def effective_radius(mode, radius):
+    """Calculate R_eff for sphere"""
     return radius
 
 def Iq(q, sld, sld_solvent, radius):
+    """Calculate I(q) for sphere"""
     #print "q",q
     #print "sld,r",sld,sld_solvent,radius
Index: sasmodels/models/adsorbed_layer.py
===================================================================
--- sasmodels/models/adsorbed_layer.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/adsorbed_layer.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -87,4 +87,5 @@
 def Iq(q, second_moment, adsorbed_amount, density_shell, radius,
        volfraction, sld_shell, sld_solvent):
+    """Return I(q) for adsorbed layer model."""
     with errstate(divide='ignore'):
         aa = ((sld_shell - sld_solvent)/density_shell * adsorbed_amount) / q
@@ -96,4 +97,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     # only care about the value of second_moment:
     #    curve = scale * e**(-second_moment^2 q^2)/q^2
Index: sasmodels/models/barbell.py
===================================================================
--- sasmodels/models/barbell.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/barbell.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -79,5 +79,5 @@
 .. [#] H Kaya and N R deSouza, *J. Appl. Cryst.*, 37 (2004) 508-509 (addenda
    and errata)
-L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949). 
+L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949).
 
 Authorship and Verification
@@ -117,10 +117,12 @@
 
 source = ["lib/polevl.c", "lib/sas_J1.c", "lib/gauss76.c", "barbell.c"]
-have_Fq = True 
+have_Fq = True
 effective_radius_type = [
-    "equivalent cylinder excluded volume","equivalent volume sphere", "radius", "half length", "half total length",
+    "equivalent cylinder excluded volume", "equivalent volume sphere",
+    "radius", "half length", "half total length",
     ]
 
 def random():
+    """Return a random parameter set for the model."""
     # TODO: increase volume range once problem with bell radius is fixed
     # The issue is that bell radii of more than about 200 fail at high q
Index: sasmodels/models/bcc_paracrystal.py
===================================================================
--- sasmodels/models/bcc_paracrystal.py	(revision da7b26b2f3ecdd4d6eea063846bb148e3def2477)
+++ sasmodels/models/bcc_paracrystal.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -1,6 +1,6 @@
 r"""
-.. warning:: This model and this model description are under review following 
-             concerns raised by SasView users. If you need to use this model, 
-             please email help@sasview.org for the latest situation. *The 
+.. warning:: This model and this model description are under review following
+             concerns raised by SasView users. If you need to use this model,
+             please email help@sasview.org for the latest situation. *The
              SasView Developers. September 2018.*
 
@@ -141,4 +141,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     # Define lattice spacing as a multiple of the particle radius
     # using the formulat a = 4 r/sqrt(3).  Systems which are ordered
Index: sasmodels/models/be_polyelectrolyte.py
===================================================================
--- sasmodels/models/be_polyelectrolyte.py	(revision ca77fc11c7ce03a565ec1aaf8755f560748546d0)
+++ sasmodels/models/be_polyelectrolyte.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -36,5 +36,5 @@
   constituting the polymer monomer and the solvent molecules, respectively.
 
-- $v_p$ and $v_s$ are the partial molar volume of the polymer and the 
+- $v_p$ and $v_s$ are the partial molar volume of the polymer and the
   solvent, respectively.
 
@@ -50,5 +50,5 @@
 - $C_s$ is the concentration of monovalent salt(1/|Ang^3| - internally converted from mol/L).
 
-- $\alpha$ is the degree of ionization (the ratio of charged monomers to the total 
+- $\alpha$ is the degree of ionization (the ratio of charged monomers to the total
   number of monomers)
 
@@ -74,10 +74,10 @@
 dimensionally useful units of 1/|Ang^3|, only the converted version of the
 polymer concentration was actually being used in the calculation while the
-unconverted salt concentration (still in apparent units of mol/L) was being 
-used.  This was carried through to Sasmodels as used for SasView 4.1 (though 
-the line of code converting the salt concentration to the new units was removed 
-somewhere along the line). Simple dimensional analysis of the calculation shows 
-that the converted salt concentration should be used, which the original code 
-suggests was the intention, so this has now been corrected (for SasView 4.2). 
+unconverted salt concentration (still in apparent units of mol/L) was being
+used.  This was carried through to Sasmodels as used for SasView 4.1 (though
+the line of code converting the salt concentration to the new units was removed
+somewhere along the line). Simple dimensional analysis of the calculation shows
+that the converted salt concentration should be used, which the original code
+suggests was the intention, so this has now been corrected (for SasView 4.2).
 Once better validation has been performed this note will be removed.
 
@@ -141,5 +141,5 @@
     :params: see parameter table
     :return: 1-D form factor for polyelectrolytes in low salt
-    
+
     parameter names, units, default values, and behavior (volume, sld etc) are
     defined in the parameter table.  The concentrations are converted from
@@ -167,4 +167,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     # TODO: review random be_polyelectrolyte model generation
     pars = dict(
Index: sasmodels/models/binary_hard_sphere.py
===================================================================
--- sasmodels/models/binary_hard_sphere.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/binary_hard_sphere.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -112,4 +112,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     # TODO: binary_hard_sphere fails at low qr
     radius_lg = 10**np.random.uniform(2, 4.7)
Index: sasmodels/models/broad_peak.py
===================================================================
--- sasmodels/models/broad_peak.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/broad_peak.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -96,4 +96,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     pars = dict(
         scale=1,
Index: sasmodels/models/capped_cylinder.py
===================================================================
--- sasmodels/models/capped_cylinder.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/capped_cylinder.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -82,5 +82,5 @@
 .. [#] H Kaya and N-R deSouza, *J. Appl. Cryst.*, 37 (2004) 508-509 (addenda
    and errata)
-L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949). 
+L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949).
 
 Authorship and Verification
@@ -139,8 +139,10 @@
 have_Fq = True
 effective_radius_type = [
-    "equivalent cylinder excluded volume", "equivalent volume sphere", "radius", "half length", "half total length",
+    "equivalent cylinder excluded volume", "equivalent volume sphere",
+    "radius", "half length", "half total length",
     ]
 
 def random():
+    """Return a random parameter set for the model."""
     # TODO: increase volume range once problem with bell radius is fixed
     # The issue is that bell radii of more than about 200 fail at high q
Index: sasmodels/models/core_multi_shell.py
===================================================================
--- sasmodels/models/core_multi_shell.py	(revision ee60aa768b29c58e20b19e94578ec3a94a0889d7)
+++ sasmodels/models/core_multi_shell.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -103,4 +103,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     num_shells = np.minimum(np.random.poisson(3)+1, 10)
     total_radius = 10**np.random.uniform(1.7, 4)
Index: sasmodels/models/core_shell_bicelle.py
===================================================================
--- sasmodels/models/core_shell_bicelle.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/core_shell_bicelle.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -89,6 +89,6 @@
    from Proquest <http://search.proquest.com/docview/304915826?accountid
    =26379>`_
-   
-   L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949). 
+
+   L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949).
 
 Authorship and Verification
@@ -158,9 +158,10 @@
 have_Fq = True
 effective_radius_type = [
-    "excluded volume","equivalent volume sphere", "outer rim radius",
+    "excluded volume", "equivalent volume sphere", "outer rim radius",
     "half outer thickness", "half diagonal",
     ]
 
 def random():
+    """Return a random parameter set for the model."""
     pars = dict(
         radius=10**np.random.uniform(1.3, 3),
Index: sasmodels/models/core_shell_bicelle_elliptical.py
===================================================================
--- sasmodels/models/core_shell_bicelle_elliptical.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/core_shell_bicelle_elliptical.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -100,5 +100,5 @@
 
 .. [#]
-L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949). 
+L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949).
 
 Authorship and Verification
@@ -149,9 +149,11 @@
 have_Fq = True
 effective_radius_type = [
-    "equivalent cylinder excluded volume", "equivalent volume sphere", "outer rim average radius", "outer rim min radius",
+    "equivalent cylinder excluded volume", "equivalent volume sphere",
+    "outer rim average radius", "outer rim min radius",
     "outer max radius", "half outer thickness", "half diagonal",
     ]
 
 def random():
+    """Return a random parameter set for the model."""
     outer_major = 10**np.random.uniform(1, 4.7)
     outer_minor = 10**np.random.uniform(1, 4.7)
Index: sasmodels/models/core_shell_bicelle_elliptical_belt_rough.py
===================================================================
--- sasmodels/models/core_shell_bicelle_elliptical_belt_rough.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/core_shell_bicelle_elliptical_belt_rough.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -112,5 +112,5 @@
 
 .. [#]
-L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949). 
+L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949).
 
 Authorship and Verification
@@ -162,7 +162,10 @@
 have_Fq = True
 effective_radius_type = [
-    "equivalent cylinder excluded volume", "equivalent volume sphere", "outer rim average radius", "outer rim min radius",
+    "equivalent cylinder excluded volume", "equivalent volume sphere",
+    "outer rim average radius", "outer rim min radius",
     "outer max radius", "half outer thickness", "half diagonal",
     ]
+
+# TODO: No random() for core-shell bicelle elliptical belt rough
 
 demo = dict(scale=1, background=0,
@@ -190,6 +193,7 @@
     #[{'radius': 30.0, 'x_core': 3.0, 'thick_rim':8.0, 'thick_face':14.0, 'length':50.0}, 'VR', 1],
 
-    [{'radius': 30.0, 'x_core': 3.0, 'thick_rim':8.0, 'thick_face':14.0, 'length':50.0,
-      'sld_core':4.0, 'sld_face':7.0, 'sld_rim':1.0, 'sld_solvent':6.0, 'background':0.0},
+    [{'radius': 30.0, 'x_core': 3.0, 'thick_rim': 8.0, 'thick_face': 14.0,
+      'length': 50.0, 'sld_core': 4.0, 'sld_face': 7.0, 'sld_rim': 1.0,
+      'sld_solvent': 6.0, 'background': 0.0},
      0.015, 189.328],
     #[{'theta':80., 'phi':10.}, (qx, qy), 7.88866563001 ],
Index: sasmodels/models/core_shell_cylinder.py
===================================================================
--- sasmodels/models/core_shell_cylinder.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/core_shell_cylinder.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -70,5 +70,5 @@
    1445-1452
 .. [#kline] S R Kline, *J Appl. Cryst.*, 39 (2006) 895
-L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949). 
+L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949).
 
 Authorship and Verification
@@ -139,4 +139,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     outer_radius = 10**np.random.uniform(1, 4.7)
     # Use a distribution with a preference for thin shell or thin core
Index: sasmodels/models/core_shell_ellipsoid.py
===================================================================
--- sasmodels/models/core_shell_ellipsoid.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/core_shell_ellipsoid.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -147,9 +147,10 @@
 have_Fq = True
 effective_radius_type = [
-    "average outer curvature", "equivalent volume sphere",     
+    "average outer curvature", "equivalent volume sphere",
     "min outer radius", "max outer radius",
     ]
 
 def random():
+    """Return a random parameter set for the model."""
     volume = 10**np.random.uniform(5, 12)
     outer_polar = 10**np.random.uniform(1.3, 4)
Index: sasmodels/models/core_shell_parallelepiped.py
===================================================================
--- sasmodels/models/core_shell_parallelepiped.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/core_shell_parallelepiped.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -173,5 +173,5 @@
    from Proquest <http://search.proquest.com/docview/304915826?accountid
    =26379>`_
-L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949). 
+L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949).
 
 Authorship and Verification
@@ -186,5 +186,5 @@
 
 import numpy as np
-from numpy import pi, inf, sqrt, cos, sin
+from numpy import inf
 
 name = "core_shell_parallelepiped"
@@ -229,5 +229,5 @@
 have_Fq = True
 effective_radius_type = [
-    "equivalent cylinder excluded volume", 
+    "equivalent cylinder excluded volume",
     "equivalent volume sphere",
     "half outer length_a", "half outer length_b", "half outer length_c",
@@ -237,4 +237,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     outer = 10**np.random.uniform(1, 4.7, size=3)
     thick = np.random.beta(0.5, 0.5, size=3)*(outer-2) + 1
@@ -269,5 +270,5 @@
 # 2d values not tested against other codes or models
 if 0:  # pak: model rewrite; need to update tests
-    qx, qy = 0.2 * cos(pi/6.), 0.2 * sin(pi/6.)
+    qx, qy = 0.2 * np.cos(np.pi/6.), 0.2 * np.sin(np.pi/6.)
     tests = [[{}, 0.2, 0.533149288477],
              [{}, [0.2], [0.533149288477]],
Index: sasmodels/models/core_shell_sphere.py
===================================================================
--- sasmodels/models/core_shell_sphere.py	(revision 304c775207a9dbbadb23ad414848454d66b556d1)
+++ sasmodels/models/core_shell_sphere.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -84,4 +84,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     outer_radius = 10**np.random.uniform(1.3, 4.3)
     # Use a distribution with a preference for thin shell or thin core
Index: sasmodels/models/cylinder.py
===================================================================
--- sasmodels/models/cylinder.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/cylinder.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -98,5 +98,5 @@
 J. S. Pedersen, Adv. Colloid Interface Sci. 70, 171-210 (1997).
 G. Fournet, Bull. Soc. Fr. Mineral. Cristallogr. 74, 39-113 (1951).
-L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949). 
+L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949).
 """
 
@@ -146,4 +146,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     volume = 10**np.random.uniform(5, 12)
     length = 10**np.random.uniform(-2, 2)*volume**0.333
@@ -184,16 +185,33 @@
 
 # Default radius and length
-radius, length = parameters[2][2], parameters[3][2]
+def calc_volume(radius, length):
+    """Return form volume for cylinder."""
+    return pi*radius**2*length
+def calc_r_effs(radius, length):
+    """Return effective radii for modes 0-7 of cylinder."""
+    return [
+        0.,
+        0.5*(0.75*radius*(2.0*radius*length
+                          + (radius + length)*(pi*radius + length)))**(1./3.),
+        (0.75*radius**2*length)**(1./3.),
+        radius,
+        length/2.,
+        min(radius, length/2.),
+        max(radius, length/2.),
+        np.sqrt(4*radius**2 + length**2)/2.,
+    ]
+r_effs = calc_r_effs(parameters[2][2], parameters[3][2])
+cyl_vol = calc_volume(parameters[2][2], parameters[3][2])
 tests.extend([
-    ({'radius_effective_mode': 0}, 0.1, None, None, 0., pi*radius**2*length, 1.0),   
-    ({'radius_effective_mode': 1}, 0.1, None, None, 0.5*(0.75*radius*(2.0*radius*length + (radius + length)*(pi*radius + length)))**(1./3.), None, None),    
-    ({'radius_effective_mode': 2}, 0.1, None, None, (0.75*radius**2*length)**(1./3.), None, None),
-    ({'radius_effective_mode': 3}, 0.1, None, None, radius, None, None),
-    ({'radius_effective_mode': 4}, 0.1, None, None, length/2., None, None),
-    ({'radius_effective_mode': 5}, 0.1, None, None, min(radius, length/2.), None, None),
-    ({'radius_effective_mode': 6}, 0.1, None, None, max(radius, length/2.), None, None),
-    ({'radius_effective_mode': 7}, 0.1, None, None, np.sqrt(4*radius**2 + length**2)/2., None, None),
+    ({'radius_effective_mode': 0}, 0.1, None, None, r_effs[0], cyl_vol, 1.0),
+    ({'radius_effective_mode': 1}, 0.1, None, None, r_effs[1], None, None),
+    ({'radius_effective_mode': 2}, 0.1, None, None, r_effs[2], None, None),
+    ({'radius_effective_mode': 3}, 0.1, None, None, r_effs[3], None, None),
+    ({'radius_effective_mode': 4}, 0.1, None, None, r_effs[4], None, None),
+    ({'radius_effective_mode': 5}, 0.1, None, None, r_effs[5], None, None),
+    ({'radius_effective_mode': 6}, 0.1, None, None, r_effs[6], None, None),
+    ({'radius_effective_mode': 7}, 0.1, None, None, r_effs[7], None, None),
 ])
-del radius, length
+del r_effs, cyl_vol
 # pylint: enable=bad-whitespace, line-too-long
 
Index: sasmodels/models/dab.py
===================================================================
--- sasmodels/models/dab.py	(revision 304c775207a9dbbadb23ad414848454d66b556d1)
+++ sasmodels/models/dab.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -66,4 +66,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     pars = dict(
         scale=10**np.random.uniform(1, 4),
Index: sasmodels/models/ellipsoid.py
===================================================================
--- sasmodels/models/ellipsoid.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/ellipsoid.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -126,9 +126,4 @@
 from numpy import inf, sin, cos, pi
 
-try:
-    from numpy import cbrt
-except ImportError:
-    def cbrt(x): return x ** (1.0/3.0)
-
 name = "ellipsoid"
 title = "Ellipsoid of revolution with uniform scattering length density."
@@ -174,4 +169,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     volume = 10**np.random.uniform(5, 12)
     radius_polar = 10**np.random.uniform(1.3, 4)
Index: sasmodels/models/elliptical_cylinder.py
===================================================================
--- sasmodels/models/elliptical_cylinder.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/elliptical_cylinder.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -88,5 +88,5 @@
 L A Feigin and D I Svergun, *Structure Analysis by Small-Angle X-Ray and
 Neutron Scattering*, Plenum, New York, (1987) [see table 3.4]
-L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949). 
+L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949).
 
 Authorship and Verification
@@ -125,5 +125,6 @@
 have_Fq = True
 effective_radius_type = [
-    "equivalent cylinder excluded volume", "equivalent volume sphere", "average radius", "min radius", "max radius",
+    "equivalent cylinder excluded volume",
+    "equivalent volume sphere", "average radius", "min radius", "max radius",
     "equivalent circular cross-section",
     "half length", "half min dimension", "half max dimension", "half diagonal",
@@ -135,9 +136,10 @@
 
 def random():
+    """Return a random parameter set for the model."""
     # V = pi * radius_major * radius_minor * length;
     volume = 10**np.random.uniform(3, 9)
     length = 10**np.random.uniform(1, 3)
     axis_ratio = 10**np.random.uniform(0, 2)
-    radius_minor = np.sqrt(volume/length/axis_ratio)
+    radius_minor = sqrt(volume/length/axis_ratio)
     volfrac = 10**np.random.uniform(-4, -2)
     pars = dict(
@@ -156,6 +158,6 @@
 
 tests = [
-#    [{'radius_minor': 20.0, 'axis_ratio': 1.5, 'length':400.0}, 'ER', 79.89245454155024],
-#    [{'radius_minor': 20.0, 'axis_ratio': 1.2, 'length':300.0}, 'VR', 1],
+    #[{'radius_minor': 20.0, 'axis_ratio': 1.5, 'length':400.0}, 'ER', 79.89245454155024],
+    #[{'radius_minor': 20.0, 'axis_ratio': 1.2, 'length':300.0}, 'VR', 1],
 
     # The SasView test result was 0.00169, with a background of 0.001
Index: sasmodels/models/fcc_paracrystal.py
===================================================================
--- sasmodels/models/fcc_paracrystal.py	(revision da7b26b2f3ecdd4d6eea063846bb148e3def2477)
+++ sasmodels/models/fcc_paracrystal.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -3,7 +3,7 @@
 #note - calculation requires double precision
 r"""
-.. warning:: This model and this model description are under review following 
-             concerns raised by SasView users. If you need to use this model, 
-             please email help@sasview.org for the latest situation. *The 
+.. warning:: This model and this model description are under review following
+             concerns raised by SasView users. If you need to use this model,
+             please email help@sasview.org for the latest situation. *The
              SasView Developers. September 2018.*
 
@@ -137,4 +137,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     # copied from bcc_paracrystal
     radius = 10**np.random.uniform(1.3, 4)
Index: sasmodels/models/flexible_cylinder.py
===================================================================
--- sasmodels/models/flexible_cylinder.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/flexible_cylinder.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -89,4 +89,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     length = 10**np.random.uniform(2, 6)
     radius = 10**np.random.uniform(1, 3)
Index: sasmodels/models/flexible_cylinder_elliptical.py
===================================================================
--- sasmodels/models/flexible_cylinder_elliptical.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/flexible_cylinder_elliptical.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -115,4 +115,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     length = 10**np.random.uniform(2, 6)
     radius = 10**np.random.uniform(1, 3)
Index: sasmodels/models/fractal.py
===================================================================
--- sasmodels/models/fractal.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/fractal.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -100,4 +100,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     radius = 10**np.random.uniform(0.7, 4)
     #radius = 5
Index: sasmodels/models/fractal_core_shell.py
===================================================================
--- sasmodels/models/fractal_core_shell.py	(revision 2cc8aa2a798267dbefb6309f9f93a254fa180b82)
+++ sasmodels/models/fractal_core_shell.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -60,5 +60,5 @@
 
 import numpy as np
-from numpy import pi, inf
+from numpy import inf
 
 name = "fractal_core_shell"
@@ -98,4 +98,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     outer_radius = 10**np.random.uniform(0.7, 4)
     # Use a distribution with a preference for thin shell or thin core
@@ -126,4 +127,5 @@
             cor_length=100.0)
 
+# TODO: why is there an ER function here?
 def ER(radius, thickness):
     """
@@ -136,19 +138,21 @@
 #tests = [[{'radius': 20.0, 'thickness': 10.0}, 'ER', 30.0],
 tests = [
-#         # At some point the SasView 3.x test result was deemed incorrect. The
-          #following tests were verified against NIST IGOR macros ver 7.850.
-          #NOTE: NIST macros do only provide for a polydispers core (no option
-          #for a poly shell or for a monodisperse core.  The results seemed
-          #extremely sensitive to the core PD, varying non monotonically all
-          #the way to a PD of 1e-6. From 1e-6 to 1e-9 no changes in the
-          #results were observed and the values below were taken using PD=1e-9.
-          #Non-monotonically = I(0.001)=188 to 140 to 177 back to 160 etc.
-         [{'radius': 20.0,
-           'thickness': 5.0,
-           'sld_core': 3.5,
-           'sld_shell': 1.0,
-           'sld_solvent': 6.35,
-           'volfraction': 0.05,
-           'background': 0.0},
-           [0.001,0.00291,0.0107944,0.029923,0.100726,0.476304],
-           [177.146,165.151,84.1596,20.1466,1.40906,0.00622666]]]
+    # At some point the SasView 3.x test result was deemed incorrect.  The
+    # following tests were verified against NIST IGOR macros ver 7.850.
+    # NOTE: NIST macros do only provide for a polydispers core (no option
+    # for a poly shell or for a monodisperse core.  The results seemed
+    # extremely sensitive to the core PD, varying non monotonically all
+    # the way to a PD of 1e-6. From 1e-6 to 1e-9 no changes in the
+    # results were observed and the values below were taken using PD=1e-9.
+    # Non-monotonically = I(0.001)=188 to 140 to 177 back to 160 etc.
+    [{'radius': 20.0,
+      'thickness': 5.0,
+      'sld_core': 3.5,
+      'sld_shell': 1.0,
+      'sld_solvent': 6.35,
+      'volfraction': 0.05,
+      'background': 0.0},
+     [0.001, 0.00291, 0.0107944, 0.029923, 0.100726, 0.476304],
+     [177.146, 165.151, 84.1596, 20.1466, 1.40906, 0.00622666]
+    ]
+]
Index: sasmodels/models/fuzzy_sphere.py
===================================================================
--- sasmodels/models/fuzzy_sphere.py	(revision ee60aa768b29c58e20b19e94578ec3a94a0889d7)
+++ sasmodels/models/fuzzy_sphere.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -82,9 +82,10 @@
 # pylint: enable=bad-whitespace,line-too-long
 
-source = ["lib/sas_3j1x_x.c","fuzzy_sphere.c"]
+source = ["lib/sas_3j1x_x.c", "fuzzy_sphere.c"]
 have_Fq = True
 effective_radius_type = ["radius", "radius + fuzziness"]
 
 def random():
+    """Return a random parameter set for the model."""
     radius = 10**np.random.uniform(1, 4.7)
     fuzziness = 10**np.random.uniform(-2, -0.5)*radius  # 1% to 31% fuzziness
Index: sasmodels/models/gauss_lorentz_gel.py
===================================================================
--- sasmodels/models/gauss_lorentz_gel.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/gauss_lorentz_gel.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -89,4 +89,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     gauss_scale = 10**np.random.uniform(1, 3)
     lorentz_scale = 10**np.random.uniform(1, 3)
Index: sasmodels/models/gaussian_peak.py
===================================================================
--- sasmodels/models/gaussian_peak.py	(revision 304c775207a9dbbadb23ad414848454d66b556d1)
+++ sasmodels/models/gaussian_peak.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -51,4 +51,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     peak_pos = 10**np.random.uniform(-3, -1)
     sigma = 10**np.random.uniform(-1.3, -0.3)*peak_pos
Index: sasmodels/models/gel_fit.py
===================================================================
--- sasmodels/models/gel_fit.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/gel_fit.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -72,4 +72,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     guinier_scale = 10**np.random.uniform(1, 3)
     lorentz_scale = 10**np.random.uniform(1, 3)
Index: sasmodels/models/guinier.py
===================================================================
--- sasmodels/models/guinier.py	(revision c9fc87335cd86f10c8c9e3654927fdeeba6a70af)
+++ sasmodels/models/guinier.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -38,7 +38,7 @@
 
 To specify a negative value of $R_g^2$ in SasView, simply give $R_g$ a negative
-value ($R_g^2$ will be evaluated as $R_g |R_g|$). Note that the physical radius 
-of gyration, of the exterior of the particle, will still be large and positive. 
-It is only the apparent size from the small $Q$ data that will give a small or 
+value ($R_g^2$ will be evaluated as $R_g |R_g|$). Note that the physical radius
+of gyration, of the exterior of the particle, will still be large and positive.
+It is only the apparent size from the small $Q$ data that will give a small or
 negative value of $R_g^2$.
 
@@ -74,4 +74,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     scale = 10**np.random.uniform(1, 4)
     # Note: compare.py has Rg cutoff of 1e-30 at q=1 for guinier, so use that
@@ -89,5 +90,5 @@
 
 # parameters for demo
-demo = dict(scale=1.0,  background=0.001, rg=60.0 )
+demo = dict(scale=1.0, background=0.001, rg=60.0)
 
 # parameters for unit tests
Index: sasmodels/models/guinier_porod.py
===================================================================
--- sasmodels/models/guinier_porod.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/guinier_porod.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -115,4 +115,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     rg = 10**np.random.uniform(1, 5)
     s = np.random.uniform(0, 3)
Index: sasmodels/models/hardsphere.py
===================================================================
--- sasmodels/models/hardsphere.py	(revision 304c775207a9dbbadb23ad414848454d66b556d1)
+++ sasmodels/models/hardsphere.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -155,4 +155,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     pars = dict(
         scale=1, background=0,
Index: sasmodels/models/hayter_msa.py
===================================================================
--- sasmodels/models/hayter_msa.py	(revision 304c775207a9dbbadb23ad414848454d66b556d1)
+++ sasmodels/models/hayter_msa.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -91,4 +91,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     # TODO: too many failures for random hayter_msa parameters
     pars = dict(
Index: sasmodels/models/hollow_cylinder.py
===================================================================
--- sasmodels/models/hollow_cylinder.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/hollow_cylinder.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -60,5 +60,5 @@
 .. [#] L A Feigin and D I Svergun, *Structure Analysis by Small-Angle X-Ray and
    Neutron Scattering*, Plenum Press, New York, (1987)
-L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949). 
+L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949).
 
 Authorship and Verification
@@ -103,5 +103,6 @@
 have_Fq = True
 effective_radius_type = [
-    "excluded volume", "equivalent outer volume sphere", "outer radius", "half length",
+    "excluded volume", "equivalent outer volume sphere",
+    "outer radius", "half length",
     "half outer min dimension", "half outer max dimension",
     "half outer diagonal",
@@ -109,4 +110,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     length = 10**np.random.uniform(1, 4.7)
     outer_radius = 10**np.random.uniform(1, 4.7)
@@ -130,21 +132,37 @@
             theta_pd=10, theta_pd_n=5,
            )
+
+def r_eff(radius, thickness, length):
+    """R_eff from excluded volume"""
+    radius += thickness
+    return (0.5*(0.75*radius*(2.0*radius*length
+                              + (radius + length)*(pi*radius + length))
+                )**(1./3.))
+
+def shell_volume(radius, thickness, length):
+    """shell volume for parameter set"""
+    return pi*((radius+thickness)**2-radius**2)*length
+
+def form_shell_ratio(radius, thickness, length):
+    """form:shell ratio"""
+    return (radius+thickness)**2/((radius+thickness)**2 - radius**2)
+
 q = 0.1
 # april 6 2017, rkh added a 2d unit test, assume correct!
 qx = q*cos(pi/6.0)
 qy = q*sin(pi/6.0)
-radius = parameters[0][2]
-thickness = parameters[1][2]
-length = parameters[2][2]
+test_pars = [
+    parameters[0][2], # radius
+    parameters[1][2], # thickness
+    parameters[2][2], # length
+]
 # Parameters for unit tests
 tests = [
     [{}, 0.00005, 1764.926],
     [{}, 0.1, None, None,
-     0.5*(0.75*(radius+thickness)*(2.0*(radius+thickness)*length + ((radius+thickness) + length)*(pi*(radius+thickness) + length)))**(1./3.),  # R_eff from excluded volume
-     pi*((radius+thickness)**2-radius**2)*length,  # shell volume
-     (radius+thickness)**2/((radius+thickness)**2 - radius**2), # form:shell ratio
+     r_eff(*test_pars), shell_volume(*test_pars), form_shell_ratio(*test_pars),
     ],
     [{}, 0.001, 1756.76],
     [{}, (qx, qy), 2.36885476192],
 ]
-del qx, qy  # not necessary to delete, but cleaner
+del qx, qy, test_pars  # not necessary to delete, but cleaner
Index: sasmodels/models/hollow_rectangular_prism.py
===================================================================
--- sasmodels/models/hollow_rectangular_prism.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/hollow_rectangular_prism.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -98,5 +98,5 @@
 
 .. [#Nayuk2012] R Nayuk and K Huber, *Z. Phys. Chem.*, 226 (2012) 837-854
-L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949). 
+L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949).
 
 
@@ -110,5 +110,5 @@
 
 import numpy as np
-from numpy import pi, inf, sqrt
+from numpy import inf
 
 name = "hollow_rectangular_prism"
@@ -151,5 +151,5 @@
 have_Fq = True
 effective_radius_type = [
-    "equivalent cylinder excluded volume", "equivalent outer volume sphere", 
+    "equivalent cylinder excluded volume", "equivalent outer volume sphere",
     "half length_a", "half length_b", "half length_c",
     "equivalent outer circular cross-section",
@@ -158,4 +158,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     a, b, c = 10**np.random.uniform(1, 4.7, size=3)
     # Thickness is limited to 1/2 the smallest dimension
Index: sasmodels/models/hollow_rectangular_prism_thin_walls.py
===================================================================
--- sasmodels/models/hollow_rectangular_prism_thin_walls.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/hollow_rectangular_prism_thin_walls.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -72,5 +72,5 @@
 
 .. [#Nayuk2012] R Nayuk and K Huber, *Z. Phys. Chem.*, 226 (2012) 837-854
-L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949). 
+L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949).
 
 
@@ -84,5 +84,5 @@
 
 import numpy as np
-from numpy import pi, inf, sqrt
+from numpy import inf
 
 name = "hollow_rectangular_prism_thin_walls"
@@ -111,5 +111,5 @@
 have_Fq = True
 effective_radius_type = [
-    "equivalent cylinder excluded volume", "equivalent outer volume sphere", 
+    "equivalent cylinder excluded volume", "equivalent outer volume sphere",
     "half length_a", "half length_b", "half length_c",
     "equivalent outer circular cross-section",
@@ -119,4 +119,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     a, b, c = 10**np.random.uniform(1, 4.7, size=3)
     pars = dict(
Index: sasmodels/models/lamellar.py
===================================================================
--- sasmodels/models/lamellar.py	(revision 304c775207a9dbbadb23ad414848454d66b556d1)
+++ sasmodels/models/lamellar.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -89,4 +89,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     thickness = 10**np.random.uniform(1, 4)
     pars = dict(
Index: sasmodels/models/lamellar_hg.py
===================================================================
--- sasmodels/models/lamellar_hg.py	(revision 304c775207a9dbbadb23ad414848454d66b556d1)
+++ sasmodels/models/lamellar_hg.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -100,4 +100,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     thickness = 10**np.random.uniform(1, 4)
     length_head = thickness * np.random.uniform(0, 1)
Index: sasmodels/models/lamellar_hg_stack_caille.py
===================================================================
--- sasmodels/models/lamellar_hg_stack_caille.py	(revision 304c775207a9dbbadb23ad414848454d66b556d1)
+++ sasmodels/models/lamellar_hg_stack_caille.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -123,4 +123,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     total_thickness = 10**np.random.uniform(2, 4.7)
     Nlayers = np.random.randint(2, 200)
Index: sasmodels/models/lamellar_stack_caille.py
===================================================================
--- sasmodels/models/lamellar_stack_caille.py	(revision 304c775207a9dbbadb23ad414848454d66b556d1)
+++ sasmodels/models/lamellar_stack_caille.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -101,4 +101,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     total_thickness = 10**np.random.uniform(2, 4.7)
     Nlayers = np.random.randint(2, 200)
Index: sasmodels/models/lamellar_stack_paracrystal.py
===================================================================
--- sasmodels/models/lamellar_stack_paracrystal.py	(revision 304c775207a9dbbadb23ad414848454d66b556d1)
+++ sasmodels/models/lamellar_stack_paracrystal.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -133,4 +133,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     total_thickness = 10**np.random.uniform(2, 4.7)
     Nlayers = np.random.randint(2, 200)
Index: sasmodels/models/line.py
===================================================================
--- sasmodels/models/line.py	(revision 108e70e22154db937a6d46f66be6dd70408cce6d)
+++ sasmodels/models/line.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -58,14 +58,15 @@
 
 
-def Iqxy(qx, qy, *args):
+def Iqxy(qx, qy, intercept, slope):
     """
     :param qx:   Input q_x-value
     :param qy:   Input q_y-value
-    :param args: Remaining arguments
+    :param intercept:   Intrecept in linear model
+    :param slope:       Slope in linear model
     :return:     2D-Intensity
     """
     # TODO: SasView documents 2D intensity as Iq(qx)*Iq(qy), but returns Iq(qy)
     # Note: SasView.run([r, theta]) does return Iq(qx)*Iq(qy)
-    return Iq(qx, *args)*Iq(qy, *args)
+    return Iq(qx, intercept, slope)*Iq(qy, intercept, slope)
 
 Iqxy.vectorized = True  # Iqxy accepts an array of qx qy values
@@ -80,4 +81,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     scale = 10**np.random.uniform(0, 3)
     slope = np.random.uniform(-1, 1)*1e2
Index: sasmodels/models/linear_pearls.py
===================================================================
--- sasmodels/models/linear_pearls.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/linear_pearls.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -66,4 +66,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     radius = 10**np.random.uniform(1, 3) # 1 - 1000
     edge_sep = 10**np.random.uniform(0, 3)  # 1 - 1000
@@ -76,5 +77,5 @@
     return pars
 
-"""
+_ = """
 Tests temporarily disabled, until single-double precision accuracy issue solved.
 
Index: sasmodels/models/lorentz.py
===================================================================
--- sasmodels/models/lorentz.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/lorentz.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -50,4 +50,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     pars = dict(
         #background=0,
Index: sasmodels/models/mass_fractal.py
===================================================================
--- sasmodels/models/mass_fractal.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/mass_fractal.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -88,4 +88,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     radius = 10**np.random.uniform(0.7, 4)
     cutoff_length = 10**np.random.uniform(0.7, 2)*radius
Index: sasmodels/models/mass_surface_fractal.py
===================================================================
--- sasmodels/models/mass_surface_fractal.py	(revision 71b751d6a3a4ef4164f3b322841ddf3875c025c5)
+++ sasmodels/models/mass_surface_fractal.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -96,4 +96,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     fractal_dim = np.random.uniform(0, 6)
     surface_portion = np.random.uniform(0, 1)
@@ -122,5 +123,6 @@
 tests = [
 
-    # Accuracy tests based on content in test/utest_other_models.py  All except first, changed so rg_cluster is the larger, RKH 30 May 2018
+    # Accuracy tests based on content in test/utest_other_models.py  All
+    # except first, changed so rg_cluster is the larger, RKH 30 May 2018
     [{'fractal_dim_mass':   1.8,
       'fractal_dim_surf':   2.3,
Index: sasmodels/models/mono_gauss_coil.py
===================================================================
--- sasmodels/models/mono_gauss_coil.py	(revision c5f7aa9167ef7808c5141b6ace833e53ea36518a)
+++ sasmodels/models/mono_gauss_coil.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -79,4 +79,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     rg = 10**np.random.uniform(0, 4)
     #rg = 1e3
Index: sasmodels/models/multilayer_vesicle.py
===================================================================
--- sasmodels/models/multilayer_vesicle.py	(revision ee60aa768b29c58e20b19e94578ec3a94a0889d7)
+++ sasmodels/models/multilayer_vesicle.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -149,4 +149,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     volfraction = 10**np.random.uniform(-3, -0.5)  # scale from 0.1% to 30%
     radius = 10**np.random.uniform(0, 2.5) # core less than 300 A
Index: sasmodels/models/onion.py
===================================================================
--- sasmodels/models/onion.py	(revision ee60aa768b29c58e20b19e94578ec3a94a0889d7)
+++ sasmodels/models/onion.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -367,4 +367,6 @@
     return np.asarray(z), np.asarray(rho)
 
+# TODO: no random parameter function for onion model
+
 demo = {
     "sld_solvent": 2.2,
Index: sasmodels/models/parallelepiped.py
===================================================================
--- sasmodels/models/parallelepiped.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/parallelepiped.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -180,5 +180,5 @@
    14 (1961) 185-211
 .. [#] R Nayuk and K Huber, *Z. Phys. Chem.*, 226 (2012) 837-854
-L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949). 
+L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949).
 
 Authorship and Verification
@@ -192,5 +192,5 @@
 
 import numpy as np
-from numpy import pi, inf, sqrt, sin, cos
+from numpy import inf
 
 name = "parallelepiped"
@@ -233,5 +233,5 @@
 have_Fq = True
 effective_radius_type = [
-    "equivalent cylinder excluded volume", "equivalent volume sphere", 
+    "equivalent cylinder excluded volume", "equivalent volume sphere",
     "half length_a", "half length_b", "half length_c",
     "equivalent circular cross-section", "half ab diagonal", "half diagonal",
@@ -239,4 +239,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     length = 10**np.random.uniform(1, 4.7, size=3)
     pars = dict(
@@ -261,5 +262,5 @@
 # rkh 7/4/17 add random unit test for 2d, note make all params different,
 # 2d values not tested against other codes or models
-qx, qy = 0.2 * cos(pi/6.), 0.2 * sin(pi/6.)
+qx, qy = 0.2 * np.cos(np.pi/6.), 0.2 * np.sin(np.pi/6.)
 tests = [[{}, 0.2, 0.17758004974],
          [{}, [0.2], [0.17758004974]],
Index: sasmodels/models/peak_lorentz.py
===================================================================
--- sasmodels/models/peak_lorentz.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/peak_lorentz.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -60,4 +60,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     peak_pos = 10**np.random.uniform(-3, -1)
     peak_hwhm = peak_pos * 10**np.random.uniform(-3, 0)
Index: sasmodels/models/pearl_necklace.py
===================================================================
--- sasmodels/models/pearl_necklace.py	(revision cf3d0cec93db2669bfb7d8037c69b08ae0761c83)
+++ sasmodels/models/pearl_necklace.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -53,9 +53,9 @@
 R Schweins and K Huber, *Particle Scattering Factor of Pearl Necklace Chains*,
 *Macromol. Symp.* 211 (2004) 25-42 2004
-L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949). 
+L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949).
 """
 
 import numpy as np
-from numpy import inf, pi
+from numpy import inf
 
 name = "pearl_necklace"
@@ -96,9 +96,8 @@
 source = ["lib/sas_Si.c", "lib/sas_3j1x_x.c", "pearl_necklace.c"]
 single = False  # use double precision unless told otherwise
-effective_radius_type = [
-    "equivalent volume sphere", 
-    ]
-    
+effective_radius_type = ["equivalent volume sphere"]
+
 def random():
+    """Return a random parameter set for the model."""
     radius = 10**np.random.uniform(1, 3) # 1 - 1000
     thick_string = 10**np.random.uniform(0, np.log10(radius)-1) # 1 - radius/10
@@ -122,6 +121,6 @@
             thick_string_pd=0.2, thick_string_pd_n=5,
            )
-# ER function is not being used here, not that it is likely very sensible to 
-# include an S(Q) with this model, the default in sasview 5.0 would be to the 
+# ER function is not being used here, not that it is likely very sensible to
+# include an S(Q) with this model, the default in sasview 5.0 would be to the
 # "unconstrained" radius_effective.
 #tests = [[{}, 0.001, 17380.245], [{}, 'ER', 115.39502]]
Index: sasmodels/models/poly_gauss_coil.py
===================================================================
--- sasmodels/models/poly_gauss_coil.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/poly_gauss_coil.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -106,4 +106,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     rg = 10**np.random.uniform(0, 4)
     #rg = 1e3
Index: sasmodels/models/polymer_excl_volume.py
===================================================================
--- sasmodels/models/polymer_excl_volume.py	(revision aa900158f288ffb14cd35ce414d65dcc836750dd)
+++ sasmodels/models/polymer_excl_volume.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -163,4 +163,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     rg = 10**np.random.uniform(0, 4)
     porod_exp = np.random.uniform(1e-3, 6)
Index: sasmodels/models/polymer_micelle.py
===================================================================
--- sasmodels/models/polymer_micelle.py	(revision 791281c646d7d88231faab78ed9dcccdee98d83b)
+++ sasmodels/models/polymer_micelle.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -113,4 +113,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     radius_core = 10**np.random.uniform(1, 3)
     rg = radius_core * 10**np.random.uniform(-2, -0.3)
Index: sasmodels/models/porod.py
===================================================================
--- sasmodels/models/porod.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/porod.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -47,4 +47,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     sld, solvent = np.random.uniform(-0.5, 12, size=2)
     radius = 10**np.random.uniform(1, 4.7)
Index: sasmodels/models/power_law.py
===================================================================
--- sasmodels/models/power_law.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/power_law.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -52,4 +52,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     power = np.random.uniform(1, 6)
     pars = dict(
Index: sasmodels/models/pringle.py
===================================================================
--- sasmodels/models/pringle.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/pringle.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -50,5 +50,5 @@
 
 import numpy as np
-from numpy import inf, pi
+from numpy import inf
 
 name = "pringle"
@@ -75,7 +75,11 @@
 source = ["lib/polevl.c", "lib/sas_J0.c", "lib/sas_J1.c",
           "lib/sas_JN.c", "lib/gauss76.c", "pringle.c"]
-effective_radius_type = ["equivalent cylinder excluded volume", "equivalent volume sphere", "radius"]
+effective_radius_type = [
+    "equivalent cylinder excluded volume",
+    "equivalent volume sphere",
+    "radius"]
 
 def random():
+    """Return a random parameter set for the model."""
     alpha, beta = 10**np.random.uniform(-1, 1, size=2)
     radius = 10**np.random.uniform(1, 3)
Index: sasmodels/models/raspberry.py
===================================================================
--- sasmodels/models/raspberry.py	(revision ee60aa768b29c58e20b19e94578ec3a94a0889d7)
+++ sasmodels/models/raspberry.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -155,4 +155,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     # Limit volume fraction to 20% each
     volfraction_lg = 10**np.random.uniform(-3, -0.3)
Index: sasmodels/models/rectangular_prism.py
===================================================================
--- sasmodels/models/rectangular_prism.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/rectangular_prism.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -32,5 +32,5 @@
 .. math::
 
-  A_P\,(q) =
+    A_P\,(q) =
       \frac{\sin \left( \tfrac{1}{2}qC \cos\theta \right) }{\tfrac{1}{2} qC \cos\theta}
       \,\times\,
@@ -48,12 +48,12 @@
 
 .. math::
-  P(q) =  \frac{2}{\pi} \int_0^{\frac{\pi}{2}} \,
-  \int_0^{\frac{\pi}{2}} A_P^2(q) \, \sin\theta \, d\theta \, d\phi
+    P(q) =  \frac{2}{\pi} \int_0^{\frac{\pi}{2}} \,
+    \int_0^{\frac{\pi}{2}} A_P^2(q) \, \sin\theta \, d\theta \, d\phi
 
 And the 1D scattering intensity is calculated as
 
 .. math::
-  I(q) = \text{scale} \times V \times (\rho_\text{p} -
-  \rho_\text{solvent})^2 \times P(q)
+    I(q) = \text{scale} \times V \times (\rho_\text{p} -
+    \rho_\text{solvent})^2 \times P(q)
 
 where $V$ is the volume of the rectangular prism, $\rho_\text{p}$
@@ -100,10 +100,10 @@
 R Nayuk and K Huber, *Z. Phys. Chem.*, 226 (2012) 837-854
 
-L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949). 
+L. Onsager, Ann. New York Acad. Sci. 51, 627-659 (1949).
 
 """
 
 import numpy as np
-from numpy import pi, inf, sqrt
+from numpy import inf
 
 name = "rectangular_prism"
@@ -140,5 +140,5 @@
 have_Fq = True
 effective_radius_type = [
-    "equivalent cylinder excluded volume", "equivalent volume sphere", 
+    "equivalent cylinder excluded volume", "equivalent volume sphere",
     "half length_a", "half length_b", "half length_c",
     "equivalent circular cross-section", "half ab diagonal", "half diagonal",
@@ -146,4 +146,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     a, b, c = 10**np.random.uniform(1, 4.7, size=3)
     pars = dict(
Index: sasmodels/models/rpa.py
===================================================================
--- sasmodels/models/rpa.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/rpa.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -150,2 +150,4 @@
     else:
         return HIDE_ALL
+
+# TODO: no random parameters generated for RPA
Index: sasmodels/models/sc_paracrystal.py
===================================================================
--- sasmodels/models/sc_paracrystal.py	(revision da7b26b2f3ecdd4d6eea063846bb148e3def2477)
+++ sasmodels/models/sc_paracrystal.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -1,8 +1,8 @@
 r"""
-.. warning:: This model and this model description are under review following 
-             concerns raised by SasView users. If you need to use this model, 
-             please email help@sasview.org for the latest situation. *The 
+.. warning:: This model and this model description are under review following
+             concerns raised by SasView users. If you need to use this model,
+             please email help@sasview.org for the latest situation. *The
              SasView Developers. September 2018.*
-             
+
 Definition
 ----------
@@ -153,4 +153,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     # copied from bcc_paracrystal
     radius = 10**np.random.uniform(1.3, 4)
Index: sasmodels/models/sphere.py
===================================================================
--- sasmodels/models/sphere.py	(revision 81751c29b389be731a04ab84a1a93dedf718e16a)
+++ sasmodels/models/sphere.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -67,9 +67,10 @@
              ]
 
-source = ["lib/sas_3j1x_x.c","sphere.c"]
+source = ["lib/sas_3j1x_x.c", "sphere.c"]
 have_Fq = True
 effective_radius_type = ["radius"]
 
 def random():
+    """Return a random parameter set for the model."""
     radius = 10**np.random.uniform(1.3, 4)
     pars = dict(
Index: sasmodels/models/spherical_sld.py
===================================================================
--- sasmodels/models/spherical_sld.py	(revision ee60aa768b29c58e20b19e94578ec3a94a0889d7)
+++ sasmodels/models/spherical_sld.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -269,4 +269,5 @@
     return np.asarray(z), np.asarray(rho)
 
+# TODO: no random parameter generator for spherical SLD.
 
 demo = {
Index: sasmodels/models/spinodal.py
===================================================================
--- sasmodels/models/spinodal.py	(revision 93fe8a1613531fbb5a9270c8cee68cad9b4642cb)
+++ sasmodels/models/spinodal.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -3,36 +3,36 @@
 ----------
 
-This model calculates the SAS signal of a phase separating system 
-undergoing spinodal decomposition. The scattering intensity $I(q)$ is calculated 
-as 
+This model calculates the SAS signal of a phase separating system
+undergoing spinodal decomposition. The scattering intensity $I(q)$ is calculated
+as
 
 .. math::
     I(q) = I_{max}\frac{(1+\gamma/2)x^2}{\gamma/2+x^{2+\gamma}}+B
 
-where $x=q/q_0$, $q_0$ is the peak position, $I_{max}$ is the intensity 
-at $q_0$ (parameterised as the $scale$ parameter), and $B$ is a flat 
-background. The spinodal wavelength, $\Lambda$, is given by $2\pi/q_0$. 
+where $x=q/q_0$, $q_0$ is the peak position, $I_{max}$ is the intensity
+at $q_0$ (parameterised as the $scale$ parameter), and $B$ is a flat
+background. The spinodal wavelength, $\Lambda$, is given by $2\pi/q_0$.
 
-The definition of $I_{max}$ in the literature varies. Hashimoto *et al* (1991) 
-define it as 
+The definition of $I_{max}$ in the literature varies. Hashimoto *et al* (1991)
+define it as
 
 .. math::
     I_{max} = \Lambda^3\Delta\rho^2
-    
-whereas Meier & Strobl (1987) give 
+
+whereas Meier & Strobl (1987) give
 
 .. math::
     I_{max} = V_z\Delta\rho^2
-    
+
 where $V_z$ is the volume per monomer unit.
 
-The exponent $\gamma$ is equal to $d+1$ for off-critical concentration 
-mixtures (smooth interfaces) and $2d$ for critical concentration mixtures 
-(entangled interfaces), where $d$ is the dimensionality (ie, 1, 2, 3) of the 
-system. Thus 2 <= $\gamma$ <= 6. A transition from $\gamma=d+1$ to $\gamma=2d$ 
-is expected near the percolation threshold. 
+The exponent $\gamma$ is equal to $d+1$ for off-critical concentration
+mixtures (smooth interfaces) and $2d$ for critical concentration mixtures
+(entangled interfaces), where $d$ is the dimensionality (ie, 1, 2, 3) of the
+system. Thus 2 <= $\gamma$ <= 6. A transition from $\gamma=d+1$ to $\gamma=2d$
+is expected near the percolation threshold.
 
-As this function tends to zero as $q$ tends to zero, in practice it may be 
-necessary to combine it with another function describing the low-angle 
+As this function tends to zero as $q$ tends to zero, in practice it may be
+necessary to combine it with another function describing the low-angle
 scattering, or to simply omit the low-angle scattering from the fit.
 
@@ -41,13 +41,13 @@
 
 H. Furukawa. Dynamics-scaling theory for phase-separating unmixing mixtures:
-Growth rates of droplets and scaling properties of autocorrelation functions. 
+Growth rates of droplets and scaling properties of autocorrelation functions.
 Physica A 123, 497 (1984).
 
-H. Meier & G. Strobl. Small-Angle X-ray Scattering Study of Spinodal 
-Decomposition in Polystyrene/Poly(styrene-co-bromostyrene) Blends. 
+H. Meier & G. Strobl. Small-Angle X-ray Scattering Study of Spinodal
+Decomposition in Polystyrene/Poly(styrene-co-bromostyrene) Blends.
 Macromolecules 20, 649-654 (1987).
 
-T. Hashimoto, M. Takenaka & H. Jinnai. Scattering Studies of Self-Assembling 
-Processes of Polymer Blends in Spinodal Decomposition. 
+T. Hashimoto, M. Takenaka & H. Jinnai. Scattering Studies of Self-Assembling
+Processes of Polymer Blends in Spinodal Decomposition.
 J. Appl. Cryst. 24, 457-466 (1991).
 
@@ -68,5 +68,5 @@
 
       List of default parameters:
-      
+
       Imax = correlation peak intensity at q_0
       background = incoherent background
@@ -74,5 +74,5 @@
       q_0 = correlation peak position [1/A]
       x = q/q_0"""
-      
+
 category = "shape-independent"
 
@@ -100,4 +100,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     pars = dict(
         scale=10**np.random.uniform(1, 3),
Index: sasmodels/models/squarewell.py
===================================================================
--- sasmodels/models/squarewell.py	(revision 304c775207a9dbbadb23ad414848454d66b556d1)
+++ sasmodels/models/squarewell.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -131,4 +131,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     pars = dict(
         scale=1, background=0,
Index: sasmodels/models/stacked_disks.py
===================================================================
--- sasmodels/models/stacked_disks.py	(revision ef07e950965796e2a3a8156fcade98ce5e403001)
+++ sasmodels/models/stacked_disks.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -147,4 +147,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     radius = 10**np.random.uniform(1, 4.7)
     total_stack = 10**np.random.uniform(1, 4.7)
Index: sasmodels/models/star_polymer.py
===================================================================
--- sasmodels/models/star_polymer.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/star_polymer.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -87,4 +87,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     pars = dict(
         #background=0,
Index: sasmodels/models/stickyhardsphere.py
===================================================================
--- sasmodels/models/stickyhardsphere.py	(revision 304c775207a9dbbadb23ad414848454d66b556d1)
+++ sasmodels/models/stickyhardsphere.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -100,4 +100,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     pars = dict(
         scale=1, background=0,
Index: sasmodels/models/surface_fractal.py
===================================================================
--- sasmodels/models/surface_fractal.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/surface_fractal.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -78,4 +78,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     radius = 10**np.random.uniform(1, 4)
     fractal_dim_surf = np.random.uniform(1, 3-1e-6)
Index: sasmodels/models/teubner_strey.py
===================================================================
--- sasmodels/models/teubner_strey.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/teubner_strey.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -103,4 +103,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     d = 10**np.random.uniform(1, 4)
     xi = 10**np.random.uniform(-0.3, 2)*d
Index: sasmodels/models/triaxial_ellipsoid.py
===================================================================
--- sasmodels/models/triaxial_ellipsoid.py	(revision 99658f6e997e12486269a52113af19f5089e0ef0)
+++ sasmodels/models/triaxial_ellipsoid.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -158,7 +158,11 @@
 source = ["lib/sas_3j1x_x.c", "lib/gauss76.c", "triaxial_ellipsoid.c"]
 have_Fq = True
-effective_radius_type = ["equivalent biaxial ellipsoid average curvature", "equivalent volume sphere", "min radius", "max radius"]
+effective_radius_type = [
+    "equivalent biaxial ellipsoid average curvature",
+    "equivalent volume sphere", "min radius", "max radius",
+    ]
 
 def random():
+    """Return a random parameter set for the model."""
     a, b, c = 10**np.random.uniform(1, 4.7, size=3)
     pars = dict(
Index: sasmodels/models/two_lorentzian.py
===================================================================
--- sasmodels/models/two_lorentzian.py	(revision 71b751d6a3a4ef4164f3b322841ddf3875c025c5)
+++ sasmodels/models/two_lorentzian.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -92,4 +92,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     scale = 10**np.random.uniform(0, 4, 2)
     length = 10**np.random.uniform(1, 4, 2)
Index: sasmodels/models/two_power_law.py
===================================================================
--- sasmodels/models/two_power_law.py	(revision ef07e950965796e2a3a8156fcade98ce5e403001)
+++ sasmodels/models/two_power_law.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -78,5 +78,4 @@
        power_2=4.0,
       ):
-
     """
     :param q:                   Input q-value (float or [float, float])
@@ -98,4 +97,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     coefficient_1 = 1
     crossover = 10**np.random.uniform(-3, -1)
Index: sasmodels/models/unified_power_Rg.py
===================================================================
--- sasmodels/models/unified_power_Rg.py	(revision 2d81cfe3be42579d563407fb9275b7b516fee234)
+++ sasmodels/models/unified_power_Rg.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -96,4 +96,5 @@
 
 def Iq(q, level, rg, power, B, G):
+    """Return I(q) for unified power Rg model."""
     level = int(level + 0.5)
     if level == 0:
@@ -118,4 +119,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     level = np.minimum(np.random.poisson(0.5) + 1, 6)
     n = level
Index: sasmodels/models/vesicle.py
===================================================================
--- sasmodels/models/vesicle.py	(revision 304c775207a9dbbadb23ad414848454d66b556d1)
+++ sasmodels/models/vesicle.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -70,5 +70,5 @@
 
 import numpy as np
-from numpy import pi, inf
+from numpy import inf
 
 name = "vesicle"
@@ -103,4 +103,5 @@
 
 def random():
+    """Return a random parameter set for the model."""
     total_radius = 10**np.random.uniform(1.3, 5)
     radius = total_radius * np.random.uniform(0, 1)
Index: sasmodels/multiscat.py
===================================================================
--- sasmodels/multiscat.py	(revision 2c4a190a64dff2df7c3a9d3ea13d6d566b439ecd)
+++ sasmodels/multiscat.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -103,5 +103,5 @@
 USE_FAST = True  # OpenCL faster, less accurate math
 
-class ICalculator:
+class ICalculator(object):
     """
     Multiple scattering calculator
@@ -113,5 +113,5 @@
         raise NotImplementedError()
 
-    def ifft(self, Iq):
+    def ifft(self, fourier_frame):
         """
         Compute the inverse FFT for an image, complex -> complex.
@@ -119,5 +119,5 @@
         raise NotImplementedError()
 
-    def mulitple_scattering(self, Iq):
+    def multiple_scattering(self, Iq, p, coverage=0.99):
         r"""
         Compute multiple scattering for I(q) given scattering probability p.
@@ -250,5 +250,5 @@
         gpu_poly = cl_array.to_device(self.queue, poly)
         self.plan.execute(gpu_data.data)
-        degree, data_size= poly.shape[0], frame.shape[0]*frame.shape[1]
+        degree, data_size = poly.shape[0], frame.shape[0]*frame.shape[1]
         self.polyval1(
             self.queue, [data_size], None,
@@ -575,4 +575,5 @@
 
 def main():
+    """Command line interface to multiple scattering calculator."""
     parser = argparse.ArgumentParser(
         description="Compute multiple scattering",
@@ -612,7 +613,8 @@
     theory = call_kernel(kernel, pars)
     Iq = res.apply(theory) + bg
-    plot_and_save_powers(res, theory, Iq, outfile=opts.outfile, background=bg)
-
-def plot_and_save_powers(res, theory, result, plot=True, outfile="", background=0.):
+    _plot_and_save_powers(res, theory, Iq, outfile=opts.outfile, background=bg)
+
+def _plot_and_save_powers(res, theory, result, plot=True,
+                          outfile="", background=0.):
     import pylab
     probability, coverage = res.probability, res.coverage
@@ -678,4 +680,5 @@
 
 def plotxy(q, Iq):
+    """Plot q, Iq or (qx, qy), Iqxy."""
     import pylab
     # q is a tuple of (q,) or (qx, qy)
Index: sasmodels/product.py
===================================================================
--- sasmodels/product.py	(revision b171acdab344b2827f9bdca29d66411c2484991f)
+++ sasmodels/product.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -18,7 +18,7 @@
 import numpy as np  # type: ignore
 
-from .modelinfo import ParameterTable, ModelInfo, Parameter, parse_parameter
+from .modelinfo import ParameterTable, ModelInfo, parse_parameter
 from .kernel import KernelModel, Kernel
-from .details import make_details, dispersion_mesh
+from .details import make_details
 
 # pylint: disable=unused-import
@@ -28,5 +28,5 @@
     pass
 else:
-    from .modelinfo import ParameterSet
+    from .modelinfo import ParameterSet, Parameter
 # pylint: enable=unused-import
 
@@ -42,22 +42,26 @@
 VOLFRAC_ID = "volfraction"
 def make_extra_pars(p_info):
+    # type: (ModelInfo) -> List[Parameter]
+    """
+    Create parameters for structure_factor_mode and radius_effective_mode.
+    """
     pars = []
     if p_info.have_Fq:
         par = parse_parameter(
-                STRUCTURE_MODE_ID,
-                "",
-                0,
-                [["P*S","P*(1+beta*(S-1))"]],
-                "",
-                "Structure factor calculation")
+            STRUCTURE_MODE_ID,
+            "",
+            0,
+            [["P*S", "P*(1+beta*(S-1))"]],
+            "",
+            "Structure factor calculation")
         pars.append(par)
     if p_info.effective_radius_type is not None:
         par = parse_parameter(
-                RADIUS_MODE_ID,
-                "",
-                1,
-                [["unconstrained"] + p_info.effective_radius_type],
-                "",
-                "Effective radius calculation")
+            RADIUS_MODE_ID,
+            "",
+            1,
+            [["unconstrained"] + p_info.effective_radius_type],
+            "",
+            "Effective radius calculation")
         pars.append(par)
     return pars
@@ -104,4 +108,5 @@
     parameters.max_pd = p_pars.max_pd + s_pars.max_pd
     def random():
+        """Random set of model parameters for product model"""
         combined_pars = p_info.random()
         s_names = set(par.id for par in s_pars.kernel_parameters)
@@ -132,4 +137,5 @@
         profile_pars = set(p.id for p in p_info.parameters.kernel_parameters)
         def profile(**kwargs):
+            """Return SLD profile of the form factor as a function of radius."""
             # extract the profile args
             kwargs = dict((k, v) for k, v in kwargs.items() if k in profile_pars)
@@ -173,5 +179,5 @@
         effective_radius, # type: float
         beta_mode,        # type: bool
-        ):
+    ):
     # type: (...) -> OrderedDict[str, Union[np.ndarray, float]]
     """
@@ -199,4 +205,7 @@
 
 class ProductModel(KernelModel):
+    """
+    Model definition for product model.
+    """
     def __init__(self, model_info, P, S):
         # type: (ModelInfo, KernelModel, KernelModel) -> None
@@ -229,4 +238,5 @@
         s_kernel = self.S.make_kernel(q_vectors)
         return ProductKernel(self.info, p_kernel, s_kernel)
+    make_kernel.__doc__ = KernelModel.make_kernel.__doc__
 
     def release(self):
@@ -240,4 +250,7 @@
 
 class ProductKernel(Kernel):
+    """
+    Instantiated kernel for product model.
+    """
     def __init__(self, model_info, p_kernel, s_kernel):
         # type: (ModelInfo, Kernel, Kernel) -> None
@@ -248,5 +261,5 @@
         self.results = []  # type: List[np.ndarray]
 
-    def __call__(self, call_details, values, cutoff, magnetic):
+    def Iq(self, call_details, values, cutoff, magnetic):
         # type: (CallDetails, np.ndarray, float, bool) -> np.ndarray
 
@@ -263,5 +276,5 @@
         beta_mode_offset = 2+p_npars+s_npars
         beta_mode = (values[beta_mode_offset] > 0) if have_beta_mode else False
-        if beta_mode and self.p_kernel.dim== '2d':
+        if beta_mode and self.p_kernel.dim == '2d':
             raise NotImplementedError("beta not yet supported for 2D")
 
@@ -327,5 +340,6 @@
         # polydispersity distribution slot in the values array due to
         # implementation details in kernel_iq.c.
-        #print("R_eff=%d:%g, volfrac=%g, volume ratio=%g"%(radius_type, effective_radius, volfrac, volume_ratio))
+        #print("R_eff=%d:%g, volfrac=%g, volume ratio=%g"
+        #      % (radius_type, effective_radius, volfrac, volume_ratio))
         if radius_type > 0:
             # set the value to the model R_eff and set the weight to 1
@@ -360,6 +374,10 @@
         return final_result
 
+    Iq.__doc__ = Kernel.Iq.__doc__
+    __call__ = Iq
+
     def release(self):
         # type: () -> None
+        """Free resources associated with the kernel."""
         self.p_kernel.release()
         self.s_kernel.release()
Index: sasmodels/rst2html.py
===================================================================
--- sasmodels/rst2html.py	(revision 1fbadb20289a15dcd2b077eb8ef47e0dbda5eef1)
+++ sasmodels/rst2html.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -142,4 +142,6 @@
 
 def load_rst_as_html(filename):
+    # type: (str) -> str
+    """Load rst from file and convert to html"""
     from os.path import expanduser
     with open(expanduser(filename)) as fid:
@@ -149,4 +151,6 @@
 
 def wxview(html, url="", size=(850, 540)):
+    # type: (str, str, Tuple[int, int]) -> "wx.Frame"
+    """View HTML in a wx dialog"""
     import wx
     from wx.html2 import WebView
@@ -158,4 +162,6 @@
 
 def view_html_wxapp(html, url=""):
+    # type: (str, str) -> None
+    """HTML viewer app in wx"""
     import wx  # type: ignore
     app = wx.App()
@@ -164,4 +170,6 @@
 
 def view_url_wxapp(url):
+    # type: (str) -> None
+    """URL viewer app in wx"""
     import wx  # type: ignore
     from wx.html2 import WebView
@@ -174,4 +182,6 @@
 
 def qtview(html, url=""):
+    # type: (str, str) -> "QWebView"
+    """View HTML in a Qt dialog"""
     try:
         from PyQt5.QtWebKitWidgets import QWebView
@@ -186,4 +196,6 @@
 
 def view_html_qtapp(html, url=""):
+    # type: (str, str) -> None
+    """HTML viewer app in Qt"""
     import sys
     try:
@@ -196,4 +208,6 @@
 
 def view_url_qtapp(url):
+    # type: (str) -> None
+    """URL viewer app in Qt"""
     import sys
     try:
@@ -217,4 +231,5 @@
 
 def can_use_qt():
+    # type: () -> bool
     """
     Return True if QWebView exists.
@@ -233,4 +248,6 @@
 
 def view_help(filename, qt=False):
+    # type: (str, bool) -> None
+    """View rst or html file.  If *qt* use q viewer, otherwise use wx."""
     import os
 
@@ -252,4 +269,6 @@
 
 def main():
+    # type: () -> None
+    """Command line interface to rst or html viewer."""
     import sys
     view_help(sys.argv[1], qt=False)
Index: sasmodels/sasview_model.py
===================================================================
--- sasmodels/sasview_model.py	(revision 21c93c37dac1eb1749656b388dd13e5fa29e63f3)
+++ sasmodels/sasview_model.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -15,5 +15,5 @@
 import traceback
 import logging
-from os.path import basename, splitext, abspath, getmtime
+from os.path import basename, splitext, abspath
 try:
     import _thread as thread
@@ -31,5 +31,4 @@
 from . import modelinfo
 from .details import make_kernel_args, dispersion_mesh
-from .kernelcl import reset_environment
 
 # pylint: disable=unused-import
@@ -688,5 +687,5 @@
                      qx,     # type: Sequence[float]
                      qy=None # type: Optional[Sequence[float]]
-                     ):
+                    ):
         # type: (...) -> Tuple[np.ndarray, Callable[[], collections.OrderedDict[str, np.ndarray]]]
         """
@@ -938,5 +937,5 @@
     # type: () -> None
     """
-    Load and run cylinder model as sas-models-CylinderModel
+    Load and run cylinder model as sas.models.CylinderModel
     """
     if not SUPPORT_OLD_STYLE_PLUGINS:
@@ -980,4 +979,7 @@
 
 def magnetic_demo():
+    """
+    Demostrate call to magnetic model.
+    """
     Model = _make_standard_model('sphere')
     model = Model()
Index: sasmodels/sesans.py
===================================================================
--- sasmodels/sesans.py	(revision fa79f5c490394c8fb6afdf72be2204a960727194)
+++ sasmodels/sesans.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -48,5 +48,8 @@
 
     def apply(self, Iq):
-        # tye: (np.ndarray) -> np.ndarray
+        # type: (np.ndarray) -> np.ndarray
+        """
+        Apply the SESANS transform to the computed I(q).
+        """
         G0 = np.dot(self._H0, Iq)
         G = np.dot(self._H.T, Iq)
Index: sasmodels/special.py
===================================================================
--- sasmodels/special.py	(revision fba9ca0239129ed28284a0b435ba2a9524913f36)
+++ sasmodels/special.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -311,5 +311,6 @@
 
 # Gaussians
-class Gauss:
+class Gauss(object):
+    """Gauss-Legendre integration weights"""
     def __init__(self, w, z):
         self.n = len(w)
Index: sasmodels/weights.py
===================================================================
--- sasmodels/weights.py	(revision e2592f02ab8e424de77e5b72ae9adfc84b47a185)
+++ sasmodels/weights.py	(revision b297ba9e837aada12de87cd46ea7a01bfe549da7)
@@ -58,5 +58,4 @@
             # For orientation, the jitter is relative to 0 not the angle
             center = 0
-            pass
         if sigma == 0 or self.npts < 2:
             if lb <= center <= ub:
@@ -123,7 +122,7 @@
     default = dict(npts=35, width=0, nsigmas=1.73205)
     def _weights(self, center, sigma, lb, ub):
-         x = self._linspace(center, sigma, lb, ub)
-         x = x[np.fabs(x-center) <= np.fabs(sigma)*sqrt(3.0)]
-         return x, np.ones_like(x)
+        x = self._linspace(center, sigma, lb, ub)
+        x = x[np.fabs(x-center) <= np.fabs(sigma)*sqrt(3.0)]
+        return x, np.ones_like(x)
 
 class LogNormalDispersion(Dispersion):
