Repository: simpeg/simpeg
Pull request #1536 (2024-09-19 20:41:06)

Replace `indActive` in VRM simulations for `active_cells`
#### Summary

Deprecate `indActive` argument in VRM simulations and replace it by `active_cells`. Add tests for the deprecations. Update tutorials and examples that were using the old `indActive` argument.

#### PR Checklist
* [ ] If this is a work in progress PR, set as a Draft PR
* [ ] Linted my code according to the [style guides](https://docs.simpeg.xyz/content/getting_started/contributing/code-style.html).
* [ ] Added [tests](https://docs.simpeg.xyz/content/getting_started/practices.html#testing) to verify changes to the code.
* [ ] Added necessary documentation to any new functions/classes following the
      expect [style](https://docs.simpeg.xyz/content/getting_started/practices.html#documentation).
* [ ] Marked as ready for review (if this is was a draft PR), and converted
      to a Pull Request
* [ ] Tagged ``@simpeg/simpeg-developers`` when ready for review.

#### Reference issue
<!--Example: write "Closes #NNNN" to automatically close that issue on merge.-->

Part of #1121

#### What does this implement/fix?
<!--Please explain your changes.-->

#### Additional information
<!--Any additional information you think is important.-->


<!--
Once all tests pass and the code has been reviewed and approved, it will be merged into main
-->


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/simpeg/simpeg/pull/1536?dropdown=coverage&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:
> Project coverage is 74.36%. Comparing base [(`048ef80`)](https://app.codecov.io/gh/simpeg/simpeg/commit/048ef809fa550446305c752b1fd137cf1837bb60?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=simpeg) to head [(`9dc6804`)](https://app.codecov.io/gh/simpeg/simpeg/commit/9dc6804fc628282ffde83048b43f30205859ea55?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=simpeg).
> Report is 1 commits behind head on main.

> :exclamation:  There is a different number of reports uploaded between BASE (048ef80) and HEAD (9dc6804). Click for more details.
> 
> <details><summary>HEAD has 13 uploads less than BASE</summary>
>
>| Flag | BASE (048ef80) | HEAD (9dc6804) |
>|------|------|------|
>||22|9|
></details>

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


```diff
@@             Coverage Diff             @@
##             main    #1536       +/-   ##
===========================================
- Coverage   86.23%   74.36%   -11.88%     
===========================================
  Files         397      377       -20     
  Lines       50711    48145     -2566     
  Branches     5645     5498      -147     
===========================================
- Hits        43730    35801     -7929     
- Misses       5527    10839     +5312     
- Partials     1454     1505       +51     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/simpeg/simpeg/pull/1536?dropdown=coverage&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: @jcapriot would you take a look at this?

Patch:
diff --git a/examples/08-vrm/plot_fwd_vrm.py b/examples/08-vrm/plot_fwd_vrm.py
index fba26e608..9b8756632 100644
--- a/examples/08-vrm/plot_fwd_vrm.py
+++ b/examples/08-vrm/plot_fwd_vrm.py
@@ -110,7 +110,7 @@ survey_vrm = VRM.Survey(src_list_vrm)
 problem_vrm = VRM.Simulation3DLinear(
     mesh,
     survey=survey_vrm,
-    indActive=topoCells,
+    active_cells=topoCells,
     refinement_factor=3,
     refinement_distance=[1.25, 2.5, 3.75],
 )
diff --git a/examples/08-vrm/plot_inv_vrm_eq.py b/examples/08-vrm/plot_inv_vrm_eq.py
index a8558023c..873a7b718 100644
--- a/examples/08-vrm/plot_inv_vrm_eq.py
+++ b/examples/08-vrm/plot_inv_vrm_eq.py
@@ -124,7 +124,7 @@ survey_vrm = VRM.Survey(source_listVRM)
 problem_vrm = VRM.Simulation3DLinear(
     mesh,
     survey=survey_vrm,
-    indActive=topoCells,
+    active_cells=topoCells,
     refinement_factor=3,
     refinement_distance=[1.25, 2.5, 3.75],
 )
@@ -176,7 +176,7 @@ actCells = (mesh.gridCC[:, 2] < 0.0) & (mesh.gridCC[:, 2] > -2.0)
 problem_inv = VRM.Simulation3DLinear(
     mesh,
     survey=survey_vrm,
-    indActive=actCells,
+    active_cells=actCells,
     refinement_factor=3,
     refinement_distance=[1.25, 2.5, 3.75],
 )
diff --git a/simpeg/electromagnetics/viscous_remanent_magnetization/simulation.py b/simpeg/electromagnetics/viscous_remanent_magnetization/simulation.py
index 27cfb040f..d3d583535 100644
--- a/simpeg/electromagnetics/viscous_remanent_magnetization/simulation.py
+++ b/simpeg/electromagnetics/viscous_remanent_magnetization/simulation.py
@@ -1,3 +1,4 @@
+import warnings
 import discretize
 import numpy as np
 import scipy.sparse as sp
@@ -15,6 +16,7 @@ from ...utils import (
 from .survey import SurveyVRM
 from .receivers import Point, SquareLoop
 
+from ...utils.code_utils import deprecate_property
 
 ############################################
 # BASE VRM PROBLEM CLASS
@@ -32,6 +34,7 @@ class BaseVRMSimulation(BaseSimulation):
         survey=None,
         refinement_factor=None,
         refinement_distance=None,
+        active_cells=None,
         indActive=None,
         **kwargs,
     ):
@@ -48,9 +51,26 @@ class BaseVRMSimulation(BaseSimulation):
                 * np.arange(1, refinement_factor + 1)
             )
         self.refinement_distance = refinement_distance
-        if indActive is None:
-            indActive = np.ones(self.mesh.n_cells, dtype=bool)
-        self.indActive = indActive
+
+        # Deprecate indActive argument
+        if indActive is not None:
+            if active_cells is not None:
+                raise TypeError(
+                    "Cannot pass both 'active_cells' and 'indActive'."
+                    "'indActive' has been deprecated and will be removed in "
+                    " SimPEG v0.24.0, please use 'active_cells' instead.",
+                )
+            warnings.warn(
+                "'indActive' has been deprecated and will be removed in "
+                " SimPEG v0.24.0, please use 'active_cells' instead.",
+                FutureWarning,
+                stacklevel=2,
+            )
+            active_cells = indActive
+
+        if active_cells is None:
+            active_cells = np.ones(self.mesh.n_cells, dtype=bool)
+        self.active_cells = active_cells
 
     @BaseSimulation.mesh.setter
     def mesh(self, value):
@@ -108,18 +128,29 @@ class BaseVRMSimulation(BaseSimulation):
         )
 
     @property
-    def indActive(self):
+    def active_cells(self):
         """Topography active cells.
 
         Returns
         -------
         (mesh.n_cells) numpy.ndarray of bool
         """
-        return self._indActive
+        return self._active_cells
 
-    @indActive.setter
-    def indActive(self, value):
-        self._indActive = validate_active_indices("indActive", value, self.mesh.n_cells)
+    @active_cells.setter
+    def active_cells(self, value):
+        self._active_cells = validate_active_indices(
+            "active_cells", value, self.mesh.n_cells
+        )
+
+    indActive = deprecate_property(
+        active_cells,
+        "indActive",
+        "active_cells",
+        removal_version="0.24.0",
+        future_warn=True,
+        error=False,
+    )
 
     def _getH0matrix(self, xyz, pp):
         """
@@ -697,12 +728,12 @@ class BaseVRMSimulation(BaseSimulation):
     def _getAMatricies(self):
         """Returns the full geometric operator"""
 
-        indActive = self.indActive
+        active_cells = self.active_cells
 
         # GET CELL INFORMATION FOR FORWARD MODELING
         meshObj = self.mesh
-        xyzc = meshObj.gridCC[indActive, :]
-        xyzh = meshObj.h_gridded[indActive, :]
+        xyzc = meshObj.gridCC[active_cells, :]
+        xyzh = meshObj.h_gridded[active_cells, :]
 
         # GET LIST OF A MATRICIES
         A = []
@@ -811,7 +842,7 @@ class Simulation3DLinear(BaseVRMSimulation):
         self.xi = xi
         self.xiMap = xiMap
 
-        nAct = list(self.indActive).count(True)
+        nAct = list(self.active_cells).count(True)
         if self.xiMap is None:
             self.xiMap = maps.IdentityMap(nP=nAct)
 
diff --git a/tutorials/10-vrm/plot_fwd_1_vrm_layer.py b/tutorials/10-vrm/plot_fwd_1_vrm_layer.py
index b783113a1..4553ce56b 100644
--- a/tutorials/10-vrm/plot_fwd_1_vrm_layer.py
+++ b/tutorials/10-vrm/plot_fwd_1_vrm_layer.py
@@ -173,7 +173,7 @@ ind_active = np.ones(mesh.nC, dtype="bool")
 simulation = vrm.Simulation3DLinear(
     mesh,
     survey=survey,
-    indActive=ind_active,
+    active_cells=ind_active,
     refinement_factor=2,
     refinement_distance=[2.0, 4.0],
 )
diff --git a/tutorials/10-vrm/plot_fwd_2_vrm_topsoil.py b/tutorials/10-vrm/plot_fwd_2_vrm_topsoil.py
index a1a60bec9..6f2857eb7 100644
--- a/tutorials/10-vrm/plot_fwd_2_vrm_topsoil.py
+++ b/tutorials/10-vrm/plot_fwd_2_vrm_topsoil.py
@@ -228,7 +228,7 @@ plt.show()
 simulation = vrm.Simulation3DLinear(
     mesh,
     survey=survey,
-    indActive=ind_active,
+    active_cells=ind_active,
     refinement_factor=2,
     refinement_distance=[2.0, 4.0],
 )
diff --git a/tutorials/10-vrm/plot_fwd_3_vrm_tem.py b/tutorials/10-vrm/plot_fwd_3_vrm_tem.py
index fece2a72e..bdeb87ac5 100644
--- a/tutorials/10-vrm/plot_fwd_3_vrm_tem.py
+++ b/tutorials/10-vrm/plot_fwd_3_vrm_tem.py
@@ -256,7 +256,7 @@ ind_active = np.ones(mesh.nC, dtype="bool")
 vrm_simulation = vrm.Simulation3DLogUniform(
     mesh,
     survey=vrm_survey,
-    indActive=ind_active,
+    active_cells=ind_active,
     refinement_factor=1,
     refinement_distance=[100.0],
     chi0=chi0_model,


Test patch:
diff --git a/tests/em/vrm/test_vrmfwd.py b/tests/em/vrm/test_vrmfwd.py
index ea751f0af..78d7eb57c 100644
--- a/tests/em/vrm/test_vrmfwd.py
+++ b/tests/em/vrm/test_vrmfwd.py
@@ -1,3 +1,4 @@
+import pytest
 import unittest
 import numpy as np
 import discretize
@@ -523,5 +524,87 @@ class VRM_fwd_tests(unittest.TestCase):
         self.assertTrue(Test)
 
 
+class TestDeprecatedIndActive:
+    """Test deprecated ``indActive`` argument in viscous remanent mag simulations."""
+
+    CLASSES = (
+        vrm.BaseVRMSimulation,
+        vrm.Simulation3DLinear,
+        vrm.Simulation3DLogUniform,
+    )
+    OLD_NAME = "indActive"
+    NEW_NAME = "active_cells"
+
+    @pytest.fixture
+    def mesh(self):
+        """Sample mesh."""
+        return discretize.TensorMesh([10, 10, 10], "CCN")
+
+    @pytest.fixture
+    def active_cells(self, mesh):
+        """Sample active cells for the mesh."""
+        active_cells = np.ones(mesh.n_cells, dtype=bool)
+        active_cells[0] = False
+        return active_cells
+
+    def get_message_duplicated_error(self, old_name, new_name, version="v0.24.0"):
+        msg = (
+            f"Cannot pass both '{new_name}' and '{old_name}'."
+            f"'{old_name}' has been deprecated and will be removed in "
+            f" SimPEG {version}, please use '{new_name}' instead."
+        )
+        return msg
+
+    def get_message_deprecated_warning(self, old_name, new_name, version="v0.24.0"):
+        msg = (
+            f"'{old_name}' has been deprecated and will be removed in "
+            f" SimPEG {version}, please use '{new_name}' instead."
+        )
+        return msg
+
+    @pytest.mark.parametrize("simulation", CLASSES)
+    def test_warning_argument(self, mesh, active_cells, simulation):
+        """
+        Test if warning is raised after passing ``indActive`` to the constructor.
+        """
+        msg = self.get_message_deprecated_warning(self.OLD_NAME, self.NEW_NAME)
+        with pytest.warns(FutureWarning, match=msg):
+            simulation(mesh, indActive=active_cells)
+
+    @pytest.mark.parametrize("simulation", CLASSES)
+    def test_error_duplicated_argument(self, mesh, active_cells, simulation):
+        """
+        Test error after passing ``indActive`` and ``active_cells`` to the constructor.
+        """
+        msg = self.get_message_duplicated_error(self.OLD_NAME, self.NEW_NAME)
+        with pytest.raises(TypeError, match=msg):
+            simulation(mesh, active_cells=active_cells, indActive=active_cells)
+
+    @pytest.mark.parametrize("simulation", CLASSES)
+    def test_warning_accessing_property(self, mesh, active_cells, simulation):
+        """
+        Test warning when trying to access the ``indActive`` property.
+        """
+        sim = simulation(mesh, active_cells=active_cells)
+        msg = f"{self.OLD_NAME} has been deprecated, please use {self.NEW_NAME}"
+        with pytest.warns(FutureWarning, match=msg):
+            old_ind_active = sim.indActive
+        np.testing.assert_allclose(sim.active_cells, old_ind_active)
+
+    @pytest.mark.parametrize("simulation", CLASSES)
+    def test_warning_setter(self, mesh, active_cells, simulation):
+        """
+        Test warning when trying to set the ``indActive`` property.
+        """
+        sim = simulation(mesh, active_cells=active_cells)
+        # Define new active cells to pass to the setter
+        new_active_cells = active_cells.copy()
+        new_active_cells[-4:] = False
+        msg = f"{self.OLD_NAME} has been deprecated, please use {self.NEW_NAME}"
+        with pytest.warns(FutureWarning, match=msg):
+            sim.indActive = new_active_cells
+        np.testing.assert_allclose(sim.active_cells, new_active_cells)
+
+
 if __name__ == "__main__":
     unittest.main()