Repository: simpeg/simpeg

Issue:
ENH: Add method to expose weights keys in regularizations
### Proposed new feature or change:

Would be nice to add a public method to regularization classes that exposes the keys of the `_weights` dictionary, so every array in it can be accessed without the need to know all the keys.

This new method should live in `BaseRegularization` and could look like this:

```python
@property
def weights_keys(self) -> list[str]:
    """Return keys of cell weights."""
    return list(self._weights.keys())
```

---

This idea came up after the SimPEG Meeting of 2024-01-03.

Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/simpeg/simpeg/pull/1320?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=simpeg) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`182ef1e`)](https://app.codecov.io/gh/simpeg/simpeg/commit/182ef1e63e5f7637992edc92154e84612164695b?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=simpeg) 82.34% compared to head [(`c58968d`)](https://app.codecov.io/gh/simpeg/simpeg/pull/1320?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=simpeg) 49.62%.


<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##             main    #1320       +/-   ##
===========================================
- Coverage   82.34%   49.62%   -32.73%     
===========================================
  Files         165      165               
  Lines       25228    25231        +3     
===========================================
- Hits        20774    12520     -8254     
- Misses       4454    12711     +8257     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/simpeg/simpeg/pull/1320?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=simpeg).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=simpeg).

santisoler: @thibaut-kobold, would you like to review this PR?

I saw you were curious in the Issue, that's why I asked. There's no rush to do so, feel free to take your time.
santisoler: Also, @domfournier, you might be interested in taking a look at this one.

Patch:
diff --git a/SimPEG/regularization/base.py b/SimPEG/regularization/base.py
index 1c89fb4ec..8e91c704d 100644
--- a/SimPEG/regularization/base.py
+++ b/SimPEG/regularization/base.py
@@ -383,6 +383,13 @@ class BaseRegularization(BaseObjectiveFunction):
             self._weights[key] = values
         self._W = None
 
+    @property
+    def weights_keys(self) -> list[str]:
+        """
+        Return the keys for the existing cell weights
+        """
+        return list(self._weights.keys())
+
     def remove_weights(self, key):
         """Removes the weights for the key provided.
 


Test patch:
diff --git a/tests/base/test_regularization.py b/tests/base/test_regularization.py
index ceb910baf..8e9f769b9 100644
--- a/tests/base/test_regularization.py
+++ b/tests/base/test_regularization.py
@@ -6,7 +6,13 @@ import inspect
 
 import discretize
 from SimPEG import maps, objective_function, regularization, utils
-from SimPEG.regularization import BaseRegularization, WeightedLeastSquares
+from SimPEG.regularization import (
+    BaseRegularization,
+    WeightedLeastSquares,
+    Smallness,
+    SmoothnessFirstOrder,
+    SmoothnessSecondOrder,
+)
 from SimPEG.objective_function import ComboObjectiveFunction
 
 
@@ -657,6 +663,67 @@ class TestParent:
             regularization.parent = invalid_parent
 
 
+class TestWeightsKeys:
+    """
+    Test weights_keys property of regularizations
+    """
+
+    @pytest.fixture
+    def mesh(self):
+        """Sample mesh."""
+        return discretize.TensorMesh([8, 7, 6])
+
+    def test_empty_weights(self, mesh):
+        """
+        Test weights_keys when no weight is defined
+        """
+        reg = BaseRegularization(mesh)
+        assert reg.weights_keys == []
+
+    def test_user_defined_weights_as_dict(self, mesh):
+        """
+        Test weights_keys after user defined weights as dictionary
+        """
+        weights = dict(dummy_weight=np.ones(mesh.n_cells))
+        reg = BaseRegularization(mesh, weights=weights)
+        assert reg.weights_keys == ["dummy_weight"]
+
+    def test_user_defined_weights_as_array(self, mesh):
+        """
+        Test weights_keys after user defined weights as dictionary
+        """
+        weights = np.ones(mesh.n_cells)
+        reg = BaseRegularization(mesh, weights=weights)
+        assert reg.weights_keys == ["user_weights"]
+
+    @pytest.mark.parametrize(
+        "regularization_class", (Smallness, SmoothnessFirstOrder, SmoothnessSecondOrder)
+    )
+    def test_volume_weights(self, mesh, regularization_class):
+        """
+        Test weights_keys has "volume" by default on some regularizations
+        """
+        reg = regularization_class(mesh)
+        assert reg.weights_keys == ["volume"]
+
+    @pytest.mark.parametrize(
+        "regularization_class",
+        (BaseRegularization, Smallness, SmoothnessFirstOrder, SmoothnessSecondOrder),
+    )
+    def test_multiple_weights(self, mesh, regularization_class):
+        """
+        Test weights_keys has "volume" by default on some regularizations
+        """
+        weights = dict(
+            dummy_weight=np.ones(mesh.n_cells), other_weights=np.ones(mesh.n_cells)
+        )
+        reg = regularization_class(mesh, weights=weights)
+        if regularization_class == BaseRegularization:
+            assert reg.weights_keys == ["dummy_weight", "other_weights"]
+        else:
+            assert reg.weights_keys == ["dummy_weight", "other_weights", "volume"]
+
+
 class TestDeprecatedArguments:
     """
     Test errors after simultaneously passing new and deprecated arguments.