Repository: SALib/SALib

Issue:
MorrisAnalyzer fails for single group
I am using v 1.4.5 of the SALib and I'm getting the following error when analyzing one single variable:
```
  File "C:\...\SALib\sample\morris\morris.py", line 113, in sample
    sample_morris = _sample_morris(problem, N, num_levels)
  File "C:\...\SALib\sample\morris\morris.py", line 148, in _sample_morris
    group_membership, _ = compute_groups_matrix(groups)
  File "C:\...\SALib\util\__init__.py", line 254, in compute_groups_matrix
    num_vars = len(groups)
TypeError: object of type 'bool' has no len()
```

This results out of these two lines in morris.py:
```
    groups = _check_groups(problem)
    group_membership, _ = compute_groups_matrix(groups)
```
`_check_group` returns false for a single group. 

Two solutions:
1. Allow single groups
2. Raise a more meaningful error. If Morris analysis is not possible for single variables, the error message should state that.

Discussion:

Patch:
diff --git a/src/SALib/util/problem.py b/src/SALib/util/problem.py
index 896e37f..f49a154 100644
--- a/src/SALib/util/problem.py
+++ b/src/SALib/util/problem.py
@@ -38,6 +38,8 @@ class ProblemSpec(dict):
         self._analysis = None
 
         self['num_vars'] = len(self['names'])
+        if 'groups' not in self:
+            self['groups'] = None
 
         self._add_samplers()
         self._add_analyzers()
@@ -276,7 +278,6 @@ class ProblemSpec(dict):
 
         return self
 
-
     def analyze(self, func, *args, **kwargs):
         """Analyze sampled results using given function.
 
@@ -300,6 +301,14 @@ class ProblemSpec(dict):
         ----------
         self : ProblemSpec object
         """
+        if self['num_vars'] == 1 or (self['groups'] and len('groups') == 1):
+            msg = (
+                "There is only a single parameter or group defined. There is "
+                "no point in conducting sensitivity analysis as any and all"
+                "effect(s) will be mapped to the single parameter/group."
+            )
+            raise ValueError(msg)
+
         if 'nprocs' in kwargs:
             # Call parallel method instead
             return self.analyze_parallel(func, *args, **kwargs)


Test patch:
diff --git a/tests/test_problem_spec.py b/tests/test_problem_spec.py
index 24e8659..baab542 100644
--- a/tests/test_problem_spec.py
+++ b/tests/test_problem_spec.py
@@ -139,3 +139,41 @@ def test_parallel_multi_output():
 
     assert np.testing.assert_equal(sp.results, psp.results) is None, "Model results not equal!"
     assert np.testing.assert_equal(sp.analysis, psp.analysis) is None, "Analysis results not equal!"
+
+
+
+def test_single_parameter():
+    """Ensures error is raised when attempting to conduct SA on a single parameter."""
+
+    sp = ProblemSpec({
+        'names': ['x1'],
+        'bounds': [[-1, 1]],
+        'outputs': ['Y']
+    })
+
+    def func(X):
+        return X[1] * X[0]
+
+    sp.sample_latin(50)
+    sp.evaluate(func)
+
+    with pytest.raises(ValueError):
+        sp.analyze_hdmr()
+
+def test_single_group():
+    """Ensures error is raised when attempting to conduct SA on a single group."""
+    sp = ProblemSpec({
+        'names': ['x1'],
+        'bounds': [[-1, 1]],
+        'groups': ['G1'],
+        'outputs': ['Y']
+    })
+
+    def func(X):
+        return X[1] * X[0]
+
+    sp.sample_latin(50)
+    sp.evaluate(func)
+
+    with pytest.raises(ValueError):
+        sp.analyze_hdmr()