Index: sasmodels/custom/__init__.py
===================================================================
--- sasmodels/custom/__init__.py	(revision 2a0c7a685e857b3f7d3721e9eaf84f3854af4a75)
+++ sasmodels/custom/__init__.py	(revision 3c852d4604cf51f0fd434203b6ffb125dc07e927)
@@ -8,5 +8,7 @@
 to occur without error.
 """
+from __future__ import division, print_function
 
+import sys
 import os
 from os.path import basename, splitext
@@ -26,4 +28,7 @@
     def load_module_from_path(fullname, path):
         """load module from *path* as *fullname*"""
+        # Clear out old definitions, if any
+        if fullname in sys.modules:
+            del sys.modules[fullname]
         module = imp.load_source(fullname, os.path.expanduser(path))
         #os.unlink(path+"c")  # remove the automatic pyc file
Index: sasmodels/model_test.py
===================================================================
--- sasmodels/model_test.py	(revision 897ca7f478f1466ca3d839c8d89b623c240a431a)
+++ sasmodels/model_test.py	(revision 479d0f3037754800fa16a1958b2bdbe5fe7b20b8)
@@ -121,5 +121,5 @@
 
             # test using dll if desired
-            if 'dll' in loaders:
+            if 'dll' in loaders or not core.HAVE_OPENCL:
                 test_name = "Model: %s, Kernel: dll"%model_name
                 test_method_name = "test_%s_dll" % model_info.id
@@ -270,5 +270,5 @@
 
     # Build a test suite containing just the model
-    loaders = ['opencl']
+    loaders = ['opencl'] if core.HAVE_OPENCL else ['dll']
     models = [model]
     try:
@@ -288,9 +288,18 @@
         stream.writeln(tb)
 
-    # Check if there are user defined tests.
-    # Yes, it is naughty to peek into the structure of the test suite, and
-    # to assume that it contains only one test.
-    if not suite._tests[0].info.tests:
-        stream.writeln("Note: %s has no user defined tests."%model)
+    # Warn if there are no user defined tests.
+    # Note: the test suite constructed above only has one test in it, which
+    # runs through some smoke tests to make sure the model runs, then runs
+    # through the input-output pairs given in the model definition file.  To
+    # check if any such pairs are defined, therefore, we just need to check if
+    # they are in the first test of the test suite.  We do this with an
+    # iterator since we don't have direct access to the list of tests in the
+    # test suite.
+    for test in suite:
+        if not test.info.tests:
+            stream.writeln("Note: %s has no user defined tests."%model)
+        break
+    else:
+        stream.writeln("Note: no test suite created --- this should never happen")
 
 
@@ -325,8 +334,8 @@
         models = models[1:]
     elif models and models[0] == 'opencl_and_dll':
-        loaders = ['opencl', 'dll']
+        loaders = ['opencl', 'dll'] if core.HAVE_OPENCL else ['dll']
         models = models[1:]
     else:
-        loaders = ['opencl', 'dll']
+        loaders = ['opencl', 'dll'] if core.HAVE_OPENCL else ['dll']
     if not models:
         print("""\
@@ -357,7 +366,15 @@
     Run "nosetests sasmodels" on the command line to invoke it.
     """
-    tests = make_suite(['opencl', 'dll'], ['all'])
+    loaders = ['opencl', 'dll'] if core.HAVE_OPENCL else ['dll']
+    tests = make_suite(loaders, ['all'])
     for test_i in tests:
-        yield test_i.run_all
+        # In order for nosetest to see the correct test name, need to set
+        # the description attribute of the returned function.  Since we
+        # can't do this for the returned instance, wrap it in a lambda and
+        # set the description on the lambda.  Otherwise we could just do:
+        #    yield test_i.run_all
+        L = lambda: test_i.run_all()
+        L.description = test_i.test_name
+        yield L
 
 
Index: sasmodels/models/multilayer_vesicle.py
===================================================================
--- sasmodels/models/multilayer_vesicle.py	(revision 9a4811ac2bdb93459d640d111cba9f1016660790)
+++ sasmodels/models/multilayer_vesicle.py	(revision e77872ef7445f58a9eb6ad1eb0015b7b63d53fac)
@@ -5,6 +5,7 @@
 This model is a trivial extension of the core_shell_sphere function to include
 *N* shells where the core is filled with solvent and the shells are interleaved
-with layers of solvent. For *N = 1*, this returns the same as the vesicle model.
-The shell thicknessess and SLD are constant across all shells as expected for
+with layers of solvent. For *N = 1*, this returns the same as the vesicle model,
+except for the normalisation, which here is to outermost volume.
+The shell thicknessess and SLD are constant for all shells as expected for
 a multilayer vesicle.
 
@@ -24,6 +25,7 @@
 .. note:
     The outer most radius
-    $radius + n_pairs * thicn_shell + (n_pairs - 1) * thick_solvent$
-    is used as the effective radius for *S(Q)* when $P(Q) * S(Q)$ is applied.
+    $radius + n_pairs * thick_shell + (n_pairs - 1) * thick_solvent$
+    is used for both the volume fraction normalization and for the 
+    effective radius for *S(Q)* when $P(Q) * S(Q)$ is applied.
 
 For information about polarised and magnetic scattering, see
@@ -62,5 +64,5 @@
     sld_solvent: solvent scattering length density
     sld: shell scattering length density
-    n_pairs:number of pairs of water/shell
+    n_pairs:number of "shell plus solvent" layer pairs
     background: incoherent background
         """
@@ -71,10 +73,10 @@
 parameters = [
     ["volfraction", "",  0.05, [0.0, 1],  "", "volume fraction of vesicles"],
-    ["radius", "Ang", 60.0, [0.0, inf],  "", "Core radius of the multishell"],
-    ["thick_shell", "Ang",        10.0, [0.0, inf],  "", "Shell thickness"],
-    ["thick_solvent", "Ang",        10.0, [0.0, inf],  "", "Water thickness"],
-    ["sld_solvent",    "1e-6/Ang^2",  6.4, [-inf, inf], "sld", "Core scattering length density"],
+    ["radius", "Ang", 60.0, [0.0, inf],  "", "radius of solvent filled core"],
+    ["thick_shell", "Ang",        10.0, [0.0, inf],  "", "thickness of one shell"],
+    ["thick_solvent", "Ang",        10.0, [0.0, inf],  "", "solvent thickness between shells"],
+    ["sld_solvent",    "1e-6/Ang^2",  6.4, [-inf, inf], "sld", "solvent scattering length density"],
     ["sld",   "1e-6/Ang^2",  0.4, [-inf, inf], "sld", "Shell scattering length density"],
-    ["n_pairs",     "",            2.0, [1.0, inf],  "", "Number of pairs of water and shell"],
+    ["n_pairs",     "",            2.0, [1.0, inf],  "", "Number of shell plus solvent layer pairs"],
     ]
 # pylint: enable=bad-whitespace, line-too-long
Index: sasmodels/models/vesicle.py
===================================================================
--- sasmodels/models/vesicle.py	(revision 42356c8890c04b71169941b12a36fc0e07e2034b)
+++ sasmodels/models/vesicle.py	(revision e77872ef7445f58a9eb6ad1eb0015b7b63d53fac)
@@ -75,5 +75,5 @@
         thickness: the shell thickness
         sld: the shell SLD
-        sld_slovent: the solvent (and core) SLD
+        sld_solvent: the solvent (and core) SLD
         background: incoherent background
         volfraction: shell volume fraction
Index: sasmodels/sasview_model.py
===================================================================
--- sasmodels/sasview_model.py	(revision 9f37726009b16a6e8fc43252be52c39cb7c8bd4a)
+++ sasmodels/sasview_model.py	(revision 9f37726009b16a6e8fc43252be52c39cb7c8bd4a)
@@ -123,16 +123,22 @@
         if model.name == "":
             model.name = splitext(basename(path))[0]
+        if not hasattr(model, 'filename'):
+            model.filename = kernel_module.__file__
     except AttributeError:
         model_info = modelinfo.make_model_info(kernel_module)
         model = _make_model_from_info(model_info)
 
-    # If we are trying to load a model that already exists,
-    # append a version number to its name.
-    # Note that models appear to be periodically reloaded 
-    # by SasView and keeping track of whether we are reloading 
-    # a model or loading it for the first time is tricky.
-    # For now, just allow one custom model of a given name.
-    if model.name in MODELS:
-        model.name = "%s_v2" % model.name
+    # If a model name already exists and we are loading a different model,
+    # use the model file name as the model name.
+    if model.name in MODELS and not model.filename == MODELS[model.name].filename:
+        _previous_name = model.name
+        model.name = model.id
+        
+        # If the new model name is still in the model list (for instance,
+        # if we put a cylinder.py in our plug-in directory), then append
+        # an identifier.
+        if model.name in MODELS and not model.filename == MODELS[model.name].filename:
+            model.name = model.id + '_user'
+        logging.info("Model %s already exists: using %s [%s]", _previous_name, model.name, model.filename)
 
     MODELS[model.name] = model
@@ -163,4 +169,5 @@
     attrs = _generate_model_attributes(model_info)
     attrs['__init__'] = __init__
+    attrs['filename'] = model_info.filename
     ConstructedModel = type(model_info.name, (SasviewModel,), attrs) # type: SasviewModelType
     return ConstructedModel
@@ -320,4 +327,8 @@
         else:
             hidden = set()
+        if self._model_info.structure_factor:
+            hidden.add('scale')
+            hidden.add('background')
+            self._model_info.parameters.defaults['background'] = 0.
 
         self._persistency_dict = {}
@@ -665,5 +676,7 @@
                 return [self.multiplicity], [1.0]
             else:
-                return [np.NaN], [1.0]
+                # For hidden parameters use the default value.
+                value = self._model_info.parameters.defaults.get(par.name, np.NaN)
+                return [value], [1.0]
         elif par.polydisperse:
             dis = self.dispersion[par.name]
@@ -686,4 +699,15 @@
     cylinder = Cylinder()
     return cylinder.evalDistribution([0.1, 0.1])
+
+def test_structure_factor():
+    # type: () -> float
+    """
+    Test that a sasview model (cylinder) can be run.
+    """
+    Model = _make_standard_model('hardsphere')
+    model = Model()
+    value = model.evalDistribution([0.1, 0.1])
+    if np.isnan(value):
+        raise ValueError("hardsphere returns null")
 
 def test_rpa():
