Repository: PMEAL/OpenPNM

Issue:
Find rate between two sets of pores
Here's a handy function that calculates the rate between two sets of pores `P1` and `P2`. I'm not sure if it's worth adding it to `openpnm`.

```python
def rate_interface(alg, P1, P2):
    if np.intersect1d(P1, P2).size != 0:
        raise Exception("P1 and P2 must not share any pores.")
    net = alg.network
    Ts_int = find_interface_throats(net, P1, P2)
    conns_int = net.conns[Ts_int].copy()
    # Account for throat direction, i.e. if there exists [i, j] in conns
    # such that i is in P2 and j is in P1, it must be replaced with [j, i]
    mask = np.in1d(conns_int[:, 0], P2)
    rates = alg.rate(throats=Ts_int, mode="single")
    rates[mask] *= -1
    return rates.sum()

def find_interface_throats(network, P1, P2):
    r"""
    Finds all throats between two sets pores: ``P1`` and ``P2``.

    This method requires that ``P1`` and ``P2`` not share any pores.
    """
    Ts1 = network.find_neighbor_throats(pores=P1, mode="xor")
    Ts2 = network.find_neighbor_throats(pores=P2, mode="xor")
    return np.intersect1d(Ts1, Ts2)
```

The only issue is that this method assumes that the rate returned by `GenericTransport.rate` is direction-dependent, which is not currently the case. I propose making the `rate` method, direction dependent. @jgostick and I already had a little conversation regarding this in #1835.

**Edit**: I also propose adding this method to `GenericTransport` (of course if other people think that's a good idea).

Discussion:

Patch:
diff --git a/openpnm/topotools/_topotools.py b/openpnm/topotools/_topotools.py
index ce44e9c9a..2cbf51a9b 100644
--- a/openpnm/topotools/_topotools.py
+++ b/openpnm/topotools/_topotools.py
@@ -1890,6 +1890,39 @@ def filter_pores_by_z(network, pores, z=1):
     hits = pores[orphans]
     return hits
 
+
+def find_interface_throats(network, P1, P2):
+    """
+    Finds all throats between two sets pores with the given labels.
+
+    Parameters
+    ----------
+    network : GenericNetwork
+        The network on which the query is to be performed
+    P1 : array_like str
+        The (label of) first set of pores
+    P2 : array_like or str
+        The (label of) second set of pores
+
+    Returns
+    -------
+    ndarray
+        List of interface throats between the two given sets of pores
+
+    Notes
+    -----
+    This method requires that the two labels do not share any pores.
+
+    """
+    P1 = network.pores(P1) if isinstance(P1, str) else P1
+    P2 = network.pores(P2) if isinstance(P2, str) else P2
+    if np.intersect1d(P1, P2).size != 0:
+        raise Exception("P1 and P2 must not share any pores.")
+    Ts1 = network.find_neighbor_throats(pores=P1, mode="xor")
+    Ts2 = network.find_neighbor_throats(pores=P2, mode="xor")
+    return np.intersect1d(Ts1, Ts2)
+
+
 end_all()
 
 


Test patch:
diff --git a/tests/unit/topotools/TopotoolsTest.py b/tests/unit/topotools/TopotoolsTest.py
index 3c7e9012f..6f3283234 100644
--- a/tests/unit/topotools/TopotoolsTest.py
+++ b/tests/unit/topotools/TopotoolsTest.py
@@ -586,6 +586,21 @@ class TopotoolsTest:
         with pytest.raises(Exception):
             _ = op.topotools.get_spacing(net)
 
+    def test_find_interface_throats(self):
+        net = op.network.Cubic([5, 1, 1])
+        with pytest.raises(Exception):
+            op.topotools.find_interface_throats(net, [1, 2, 3], [3, 4, 5])
+        # Test pores that do have an interface
+        Ts_int = op.topotools.find_interface_throats(net, 0, 1)
+        assert_allclose(Ts_int, np.array([0]))
+        # Test pores without an interface
+        Ts_int = op.topotools.find_interface_throats(net, 2, 4)
+        assert_allclose(Ts_int, np.array([]))
+        # Test calling the function with labels
+        net["pore.internal"] = ~net["pore.surface"]
+        Ts_int = op.topotools.find_interface_throats(net, "internal", "surface")
+        assert_allclose(Ts_int, np.array([0, 3]))
+
 
 if __name__ == '__main__':