Repository: PMEAL/OpenPNM
Pull request #1295 (2019-05-31 01:43:23)

Add target to pores and throats
The standard ``pores`` and ``throats`` methods now have a ``target`` argument that means you can use ``pn.pores('left', target=get)`` to get the indices of the 'left' pores on the ``geo`` object.  Backwards compatible and default behavior is same as before.


Patch:
diff --git a/openpnm/core/Base.py b/openpnm/core/Base.py
index b9af6f7ca..03d7e8d2a 100644
--- a/openpnm/core/Base.py
+++ b/openpnm/core/Base.py
@@ -719,7 +719,7 @@ class Base(dict):
         ind = ind.astype(dtype=int)
         return ind
 
-    def pores(self, labels='all', mode='or', asmask=False):
+    def pores(self, labels='all', mode='or', asmask=False, target=None):
         r"""
         Returns pore indicies where given labels exist, according to the logic
         specified by the ``mode`` argument.
@@ -755,6 +755,11 @@ class Base(dict):
             If ``True`` then a boolean array of length Np is returned with
             ``True`` values indicating the pores that satisfy the query.
 
+        target : OpenPNM Base object
+            If given, the returned indices will be indexed relative to the
+            ``target`` object.  This can be used to determine how indices on
+            one object map onto another object.
+
         Returns
         -------
         A Numpy array containing pore indices filtered by the logic specified
@@ -763,6 +768,7 @@ class Base(dict):
         See Also
         --------
         throats
+        map_pores
 
         Notes
         -----
@@ -785,8 +791,13 @@ class Base(dict):
         array([ 4,  9, 14, 19, 24])
         """
         ind = self._get_indices(element='pore', labels=labels, mode=mode)
+        if target is not None:
+            ind = target.map_pores(pores=ind, origin=self, filtered=True)
         if asmask:
-            ind = self.tomask(pores=ind)
+            if target is not None:
+                ind = target.tomask(pores=ind)
+            else:
+                ind = self.tomask(pores=ind)
         return ind
 
     @property
@@ -796,7 +807,7 @@ class Base(dict):
         """
         return sp.arange(0, self.Np)
 
-    def throats(self, labels='all', mode='or', asmask=False):
+    def throats(self, labels='all', mode='or', asmask=False, target=None):
         r"""
         Returns throat locations where given labels exist, according to the
         logic specified by the ``mode`` argument.
@@ -833,6 +844,11 @@ class Base(dict):
             If ``True`` then a boolean array of length Nt is returned with
             ``True`` values indicating the throats that satisfy the query.
 
+        target : OpenPNM Base object
+            If given, the returned indices will be indexed relative to the
+            ``target`` object.  This can be used to determine how indices on
+            one object map onto another object.
+
         Returns
         -------
         A Numpy array containing throat indices filtered by the logic specified
@@ -841,6 +857,7 @@ class Base(dict):
         See Also
         --------
         pores
+        map_throats
 
         Examples
         --------
@@ -852,8 +869,13 @@ class Base(dict):
 
         """
         ind = self._get_indices(element='throat', labels=labels, mode=mode)
+        if target is not None:
+            ind = target.map_throats(throats=ind, origin=self, filtered=True)
         if asmask:
-            ind = self.tomask(throats=ind)
+            if target is not None:
+                ind = target.tomask(throats=ind)
+            else:
+                ind = self.tomask(throats=ind)
         return ind
 
     @property
@@ -903,6 +925,11 @@ class Base(dict):
         on the ``origin`` object.  Can be an array or a tuple containing an
         array and a mask, depending on the value of ``filtered``.
 
+        See Also
+        --------
+        pores
+        map_throats
+
         """
         ids = origin['pore._id'][pores]
         return self._map(element='pore', ids=ids, filtered=filtered)
@@ -929,8 +956,13 @@ class Base(dict):
         Returns
         -------
         Throat indices on the calling object corresponding to the same throats
-        on the target object.  Can be an array or a tuple containing an array
-        and a mask, depending on the value of ``filtered``.
+        on the ``origin`` object.  Can be an array or a tuple containing an
+        array and a mask, depending on the value of ``filtered``.
+
+        See Also
+        --------
+        throats
+        map_pores
 
         """
         ids = origin['throat._id'][throats]


Test patch:
diff --git a/tests/unit/core/BaseTest.py b/tests/unit/core/BaseTest.py
index 498b4e283..881f97005 100644
--- a/tests/unit/core/BaseTest.py
+++ b/tests/unit/core/BaseTest.py
@@ -111,6 +111,36 @@ class BaseTest:
         b = self.net.pores(labels=['top', 'front'], mode='or')
         assert sp.all(sp.where(a)[0] == b)
 
+    def test_pores_with_target(self):
+        net = op.network.Cubic(shape=[2, 2, 2])
+        geo1 = op.geometry.GenericGeometry(network=net,
+                                           pores=[1, 3, 5, 7],
+                                           throats=range(6))
+        geo2 = op.geometry.GenericGeometry(network=net,
+                                           pores=[0, 2, 4, 6],
+                                           throats=range(6, 12))
+        assert sp.all(net.pores('top', target=geo1) == [0, 1, 2, 3])
+        assert len(net.pores('top', target=geo2)) == 0
+        mapped = net.map_pores(pores=[0, 1, 2, 3], origin=geo1)
+        assert sp.all(mapped == net.pores('geo_01'))
+        mapped = net.map_pores(pores=[0, 1, 2, 3], origin=geo2)
+        assert sp.all(mapped == net.pores('geo_02'))
+
+    def test_throats_with_target(self):
+        net = op.network.Cubic(shape=[2, 2, 2])
+        geo1 = op.geometry.GenericGeometry(network=net,
+                                           pores=[1, 3, 5, 7],
+                                           throats=range(6))
+        geo2 = op.geometry.GenericGeometry(network=net,
+                                           pores=[0, 2, 4, 6],
+                                           throats=range(6, 12))
+        assert sp.all(net.throats('surface', target=geo1) == [0, 1, 2, 3, 4, 5])
+        assert sp.all(net.throats('surface', target=geo2) == [0, 1, 2, 3, 4, 5])
+        mapped = net.map_throats(throats=[0, 1, 2, 3, 4, 5], origin=geo1)
+        assert sp.all(mapped == net.throats('geo_01'))
+        mapped = net.map_throats(throats=[0, 1, 2, 3, 4, 5], origin=geo2)
+        assert sp.all(mapped == net.throats('geo_02'))
+
     def test_throats(self):
         a = self.net.throats()
         assert sp.all(a == sp.arange(0, self.net.Nt))