Index: sasmodels/compare_many.py
===================================================================
--- sasmodels/compare_many.py	(revision 424fe00c428dbe537e7616afc49f2450cc8bbc69)
+++ sasmodels/compare_many.py	(revision 5124c9691da75ad42fc248d52971ed75dcec0d32)
@@ -106,5 +106,6 @@
     header = ('\n"Model","%s","Count","%d","Dimension","%s"'
               % (name, N, "2D" if is_2d else "1D"))
-    if not mono: header += ',"Cutoff",%g'%(cutoff,)
+    if not mono:
+        header += ',"Cutoff",%g'%(cutoff,)
     print(header)
 
@@ -161,9 +162,10 @@
     max_diff = [0]
     for k in range(N):
-        print("%s %d"%(name, k), file=sys.stderr)
+        print("Model %s %d"%(name, k+1), file=sys.stderr)
         seed = np.random.randint(1e6)
         pars_i = randomize_pars(model_info, pars, seed)
         constrain_pars(model_info, pars_i)
-        constrain_new_to_old(model_info, pars_i)
+        if 'sasview' in (base, comp):
+            constrain_new_to_old(model_info, pars_i)
         if mono:
             pars_i = suppress_pd(pars_i)
@@ -204,6 +206,7 @@
     print("""\
 
-MODEL is the model name of the model or "all" for all the models
-in alphabetical order.
+MODEL is the model name of the model or one of the model types listed in
+sasmodels.core.list_models (all, py, c, double, single, opencl, 1d, 2d,
+nonmagnetic, magnetic).  Model types can be combined, such as 2d+single.
 
 COUNT is the number of randomly generated parameter sets to try. A value
@@ -237,8 +240,11 @@
         return
 
-    model = argv[0]
-    if not (model in MODELS) and (model != "all"):
-        print('Bad model %s.  Use "all" or one of:'%model)
+    target = argv[0]
+    try:
+        model_list = [target] if target in MODELS else core.list_models(target)
+    except ValueError:
+        print('Bad model %s.  Use model type or one of:'%model)
         print_models()
+        print('model types: all, py, c, double, single, opencl, 1d, 2d, nonmagnetic, magnetic')
         return
     try:
@@ -258,5 +264,4 @@
     data, index = make_data({'qmax':1.0, 'is2d':is2D, 'nq':Nq, 'res':0.,
                              'accuracy': 'Low', 'view':'log', 'zero': False})
-    model_list = [model] if model != "all" else MODELS
     for model in model_list:
         compare_instance(model, data, index, N=count, mono=mono,
Index: sasmodels/core.py
===================================================================
--- sasmodels/core.py	(revision 52e9a450543ba1ed18c9b30bfe89ce5288d4a831)
+++ sasmodels/core.py	(revision 5124c9691da75ad42fc248d52971ed75dcec0d32)
@@ -69,10 +69,19 @@
         * magnetic: models with an sld
         * nommagnetic: models without an sld
-    """
-    if kind and kind not in KINDS:
+
+    For multiple conditions, combine with plus.  For example, *c+single+2d*
+    would return all oriented models implemented in C which can be computed
+    accurately with single precision arithmetic.
+    """
+    if kind and any(k not in KINDS for k in kind.split('+')):
         raise ValueError("kind not in " + ", ".join(KINDS))
     files = sorted(glob(joinpath(generate.MODEL_PATH, "[a-zA-Z]*.py")))
     available_models = [basename(f)[:-3] for f in files]
-    selected = [name for name in available_models if _matches(name, kind)]
+    if kind and '+' in kind:
+        all_kinds = kind.split('+')
+        condition = lambda name: all(_matches(name, k) for k in all_kinds)
+    else:
+        condition = lambda name: _matches(name, kind)
+    selected = [name for name in available_models if condition(name)]
 
     return selected
Index: sasmodels/model_test.py
===================================================================
--- sasmodels/model_test.py	(revision 1a6cd57bd7b12db89d69b335e984c55413295845)
+++ sasmodels/model_test.py	(revision e09d1e017dd0768860a2d828014007a0017b66e8)
@@ -97,4 +97,19 @@
         is_py = callable(model_info.Iq)
 
+        # Some OpenCL drivers seem to be flaky, and are not producing the
+        # expected result.  Since we don't have known test values yet for
+        # all of our models, we are instead going to compare the results
+        # for the 'smoke test' (that is, evaluation at q=0.1 for the default
+        # parameters just to see that the model runs to completion) between
+        # the OpenCL and the DLL.  To do this, we define a 'stash' which is
+        # shared between OpenCL and DLL tests.  This is just a list.  If the
+        # list is empty (which it will be when DLL runs, if the DLL runs
+        # first), then the results are appended to the list.  If the list
+        # is not empty (which it will be when OpenCL runs second), the results
+        # are compared to the results stored in the first element of the list.
+        # This is a horrible stateful hack which only makes sense because the
+        # test suite is thrown away after being run once.
+        stash = []
+
         if is_py:  # kernel implemented in python
             test_name = "Model: %s, Kernel: python"%model_name
@@ -103,7 +118,20 @@
                                  test_method_name,
                                  platform="dll",  # so that
-                                 dtype="double")
+                                 dtype="double",
+                                 stash=stash)
             suite.addTest(test)
         else:   # kernel implemented in C
+
+            # test using dll if desired
+            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
+                test = ModelTestCase(test_name, model_info,
+                                     test_method_name,
+                                     platform="dll",
+                                     dtype="double",
+                                     stash=stash)
+                suite.addTest(test)
+
             # test using opencl if desired and available
             if 'opencl' in loaders and core.HAVE_OPENCL:
@@ -116,16 +144,7 @@
                 test = ModelTestCase(test_name, model_info,
                                      test_method_name,
-                                     platform="ocl", dtype=None)
+                                     platform="ocl", dtype=None,
+                                     stash=stash)
                 #print("defining", test_name)
-                suite.addTest(test)
-
-            # test using dll if desired
-            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
-                test = ModelTestCase(test_name, model_info,
-                                     test_method_name,
-                                     platform="dll",
-                                     dtype="double")
                 suite.addTest(test)
 
@@ -144,10 +163,11 @@
         """
         def __init__(self, test_name, model_info, test_method_name,
-                     platform, dtype):
-            # type: (str, ModelInfo, str, str, DType) -> None
+                     platform, dtype, stash):
+            # type: (str, ModelInfo, str, str, DType, List[Any]) -> None
             self.test_name = test_name
             self.info = model_info
             self.platform = platform
             self.dtype = dtype
+            self.stash = stash  # container for the results of the first run
 
             setattr(self, test_method_name, self.run_all)
@@ -174,22 +194,66 @@
                 ]
 
-            tests = self.info.tests
+            tests = smoke_tests + self.info.tests
             try:
                 model = build_model(self.info, dtype=self.dtype,
                                     platform=self.platform)
-                for test in smoke_tests + tests:
-                    self.run_one(model, test)
-
-                if not tests and self.platform == "dll":
-                    ## Uncomment the following to make forgetting the test
-                    ## values an error.  Only do so for the "dll" tests
-                    ## to reduce noise from both opencl and dll, and because
-                    ## python kernels use platform="dll".
-                    #raise Exception("No test cases provided")
-                    pass
+                results = [self.run_one(model, test) for test in tests]
+                if self.stash:
+                    for test, target, actual in zip(tests, self.stash[0], results):
+                        assert np.all(abs(target-actual)<2e-5*abs(actual)),\
+                            "expected %s but got %s for %s"%(target, actual, test[0])
+                else:
+                    self.stash.append(results)
+
+                # Check for missing tests.  Only do so for the "dll" tests
+                # to reduce noise from both opencl and dll, and because
+                # python kernels use platform="dll".
+                if self.platform == "dll":
+                    missing = []
+                    ## Uncomment the following to require test cases
+                    #missing = self._find_missing_tests()
+                    if missing:
+                        raise ValueError("Missing tests for "+", ".join(missing))
 
             except:
                 annotate_exception(self.test_name)
                 raise
+
+        def _find_missing_tests(self):
+            # type: () -> None
+            """make sure there are 1D, 2D, ER and VR tests as appropriate"""
+            model_has_VR = callable(self.info.VR)
+            model_has_ER = callable(self.info.ER)
+            model_has_1D = True
+            model_has_2D = any(p.type == 'orientation'
+                               for p in self.info.parameters.kernel_parameters)
+
+            # Lists of tests that have a result that is not None
+            single = [test for test in self.info.tests
+                      if not isinstance(test[2], list) and test[2] is not None]
+            tests_has_VR = any(test[1] == 'VR' for test in single)
+            tests_has_ER = any(test[1] == 'ER' for test in single)
+            tests_has_1D_single = any(isinstance(test[1], float) for test in single)
+            tests_has_2D_single = any(isinstance(test[1], tuple) for test in single)
+
+            multiple = [test for test in self.info.tests
+                        if isinstance(test[2], list)
+                            and not all(result is None for result in test[2])]
+            tests_has_1D_multiple = any(isinstance(test[1][0], float)
+                                        for test in multiple)
+            tests_has_2D_multiple = any(isinstance(test[1][0], tuple)
+                                        for test in multiple)
+
+            missing = []
+            if model_has_VR and not tests_has_VR:
+                missing.append("VR")
+            if model_has_ER and not tests_has_ER:
+                missing.append("ER")
+            if model_has_1D and not (tests_has_1D_single or tests_has_1D_multiple):
+                missing.append("1D")
+            if model_has_2D and not (tests_has_2D_single or tests_has_2D_multiple):
+                missing.append("2D")
+
+            return missing
 
         def run_one(self, model, test):
@@ -207,7 +271,7 @@
 
             if x[0] == 'ER':
-                actual = [call_ER(model.info, pars)]
+                actual = np.array([call_ER(model.info, pars)])
             elif x[0] == 'VR':
-                actual = [call_VR(model.info, pars)]
+                actual = np.array([call_VR(model.info, pars)])
             elif isinstance(x[0], tuple):
                 qx, qy = zip(*x)
@@ -238,4 +302,5 @@
                                     'f(%s); expected:%s; actual:%s'
                                     % (xi, yi, actual_yi))
+            return actual
 
     return ModelTestCase
Index: sasmodels/modelinfo.py
===================================================================
--- sasmodels/modelinfo.py	(revision 85fe7f81bca37e67a39808fbf8e07e7d8be316b1)
+++ sasmodels/modelinfo.py	(revision 5124c9691da75ad42fc248d52971ed75dcec0d32)
@@ -730,6 +730,4 @@
     info.docs = kernel_module.__doc__
     info.category = getattr(kernel_module, 'category', None)
-    info.single = getattr(kernel_module, 'single', True)
-    info.opencl = getattr(kernel_module, 'opencl', True)
     info.structure_factor = getattr(kernel_module, 'structure_factor', False)
     info.profile_axes = getattr(kernel_module, 'profile_axes', ['x', 'y'])
@@ -745,4 +743,7 @@
     info.profile = getattr(kernel_module, 'profile', None) # type: ignore
     info.sesans = getattr(kernel_module, 'sesans', None) # type: ignore
+    # Default single and opencl to True for C models.  Python models have callable Iq.
+    info.opencl = getattr(kernel_module, 'opencl', not callable(info.Iq))
+    info.single = getattr(kernel_module, 'single', not callable(info.Iq))
 
     # multiplicity info
