Repository: PMEAL/OpenPNM
Pull request #1255 (2019-04-22 22:49:31)

remove build_A from AD alg, add cd model, fix tests, improve alg.results
Related to issue #1253.
1/ Kept both **AdvectionDiffusion** and **Dispersion** algorithms (the former inherits from the latter)
2/ Added a new **ad_dif_conductance** model for the **AdvectionDiffusion** algorithm (which inherits from the **dispersive_conductance**) and deleted the **_build_A** from the **AdvectionDiffusion** algorithm
3/ Fixed tests
4/ Improved the **alg.results** method in **TransientReactiveTransport**
5/ Other changes to make sure the **outflow_BC** and **rate** methods work correctly


Patch:
diff --git a/example_ad_02.py b/example_ad_02.py
new file mode 100644
index 000000000..499caabee
--- /dev/null
+++ b/example_ad_02.py
@@ -0,0 +1,63 @@
+import openpnm as op
+import numpy as np
+
+# work space and project
+ws = op.Workspace()
+proj = ws.new_project()
+
+# network
+np.random.seed(7)
+n = 10
+net = op.network.Cubic(shape=[n, n, 1], spacing=1e-4, project=proj)
+
+# geometry
+geo = op.geometry.StickAndBall(network=net,
+                               pores=net.Ps,
+                               throats=net.Ts)
+
+# phase
+phase = op.phases.Water(network=net)
+
+# physics
+phys = op.physics.GenericPhysics(network=net,
+                                 phase=phase,
+                                 geometry=geo)
+phase['pore.diffusivity'] = 2e-09
+phase['throat.diffusivity'] = 2e-09
+
+mod1 = op.models.physics.hydraulic_conductance.hagen_poiseuille
+phys.add_model(propname='throat.hydraulic_conductance',
+               model=mod1, regen_mode='normal')
+
+# algorithms: Stokes flow
+sf = op.algorithms.StokesFlow(network=net, phase=phase)
+sf.set_value_BC(pores=net.pores('left'), values=0)
+sf.set_value_BC(pores=net.pores('right'), values=0.1)
+sf.run()
+phase.update(sf.results())
+
+
+# algorithms: dispersion
+mod2 = op.models.physics.diffusive_conductance.ordinary_diffusion
+phys.add_model(propname='throat.diffusive_conductance',
+               model=mod2, regen_mode='normal')
+
+mod3 = op.models.physics.ad_dif_conductance.ad_dif
+phys.add_model(propname='throat.ad_dif_conductance',
+               model=mod3, regen_mode='normal')
+
+dis = op.algorithms.AdvectionDiffusion(network=net, phase=phase)
+dis.set_value_BC(pores=net.pores('back'), values=1)
+dis.set_value_BC(pores=net.pores('front'), values=0.5)
+dis.run()
+
+dis2 = op.algorithms.TransientAdvectionDiffusion(network=net, phase=phase)
+dis2.set_value_BC(pores=net.pores('back'), values=1)
+dis2.set_value_BC(pores=net.pores('front'), values=0.5)
+dis2.setup(t_initial=0, t_final=300, t_step=1, t_output=50, t_tolerance=1e-20,
+           t_scheme='implicit')
+dis2.run()
+
+# output results to a vtk file
+# phase.update(dis.results())
+# proj.export_data(phases=[phase], filename='OUT', filetype='VTK')
diff --git a/openpnm/algorithms/AdvectionDiffusion.py b/openpnm/algorithms/AdvectionDiffusion.py
index 35a6c0c80..be6a38ba9 100644
--- a/openpnm/algorithms/AdvectionDiffusion.py
+++ b/openpnm/algorithms/AdvectionDiffusion.py
@@ -1,5 +1,4 @@
 import numpy as np
-from scipy.sparse.csgraph import laplacian
 from openpnm.algorithms import ReactiveTransport
 from openpnm.utils import logging
 logger = logging.getLogger(__name__)
@@ -7,23 +6,21 @@ logger = logging.getLogger(__name__)
 
 class AdvectionDiffusion(ReactiveTransport):
     r"""
-    A subclass of GenericTransport to simulate advection diffusion
+    A subclass of GenericTransport to simulate advection-diffusion
 
     """
 
     def __init__(self, settings={}, phase=None, **kwargs):
         def_set = {'phase': None,
                    'quantity': 'pore.concentration',
+                   'conductance': 'throat.ad_dif_conductance',
                    'diffusive_conductance': 'throat.diffusive_conductance',
                    'hydraulic_conductance': 'throat.hydraulic_conductance',
                    'pressure': 'pore.pressure',
-                   's_scheme': 'powerlaw',
+                   's_scheme': 'exponential',
                    'gui': {'setup':        {'phase': None,
                                             'quantity': '',
-                                            'diffusive_conductance': '',
-                                            'hydraulic_conductance': '',
-                                            'pressure': '',
-                                            's_scheme': ''},
+                                            'conductance': ''},
                            'set_rate_BC':  {'pores': None,
                                             'values': None},
                            'set_value_BC': {'pores': None,
@@ -38,8 +35,9 @@ class AdvectionDiffusion(ReactiveTransport):
         if phase is not None:
             self.setup(phase=phase)
 
-    def setup(self, phase=None, quantity='', diffusive_conductance='',
-              hydraulic_conductance='', pressure='', s_scheme='', **kwargs):
+    def setup(self, phase=None, quantity='', conductance='',
+              diffusive_conductance='', hydraulic_conductance='', pressure='',
+              s_scheme='', **kwargs):
         r"""
 
         """
@@ -47,6 +45,8 @@ class AdvectionDiffusion(ReactiveTransport):
             self.settings['phase'] = phase.name
         if quantity:
             self.settings['quantity'] = quantity
+        if conductance:
+            self.settings['conductance'] = conductance
         if diffusive_conductance:
             self.settings['diffusive_conductance'] = diffusive_conductance
         if hydraulic_conductance:
@@ -57,47 +57,6 @@ class AdvectionDiffusion(ReactiveTransport):
             self.settings['s_scheme'] = s_scheme
         super().setup(**kwargs)
 
-    def _build_A(self, force=False):
-        s_dis = self.settings['s_scheme']
-        network = self.project.network
-        phase = self.project.phases()[self.settings['phase']]
-        conns = network['throat.conns']
-
-        P = phase[self.settings['pressure']]
-        gh = phase[self.settings['hydraulic_conductance']]
-        gd = phase[self.settings['diffusive_conductance']]
-        gd = np.tile(gd, 2)
-
-        Qij = -gh*np.diff(P[conns], axis=1).squeeze()
-        Qij = np.append(Qij, -Qij)
-
-        Peij = Qij/gd
-        Peij[(Peij < 1e-10) & (Peij >= 0)] = 1e-10
-        Peij[(Peij > -1e-10) & (Peij <= 0)] = -1e-10
-        Qij = Peij*gd
-
-        if force:
-            self._pure_A = None
-        if self._pure_A is None:
-            if s_dis == 'upwind':
-                w = gd + np.maximum(0, -Qij)
-                A = network.create_adjacency_matrix(weights=w)
-            elif s_dis == 'hybrid':
-                w = np.maximum(0, np.maximum(-Qij, gd-Qij/2))
-                A = network.create_adjacency_matrix(weights=w)
-            elif s_dis == 'powerlaw':
-                w = gd * np.maximum(0, (1 - 0.1*np.abs(Peij))**5) + \
-                    np.maximum(0, -Qij)
-                A = network.create_adjacency_matrix(weights=w)
-            elif s_dis == 'exponential':
-                w = -Qij / (1 - np.exp(Peij))
-                A = network.create_adjacency_matrix(weights=w)
-            else:
-                raise Exception('Unrecognized discretization scheme: ' + s_dis)
-            A = laplacian(A)
-            self._pure_A = A
-        self.A = self._pure_A.copy()
-
     def set_outflow_BC(self, pores, mode='merge'):
         r"""
         Adds outflow boundary condition to the selected pores.
diff --git a/openpnm/algorithms/Dispersion.py b/openpnm/algorithms/Dispersion.py
index 7722a1195..c495a5957 100644
--- a/openpnm/algorithms/Dispersion.py
+++ b/openpnm/algorithms/Dispersion.py
@@ -1,18 +1,16 @@
-import numpy as np
-from openpnm.algorithms import ReactiveTransport
+from openpnm.algorithms.AdvectionDiffusion import AdvectionDiffusion
 from openpnm.utils import logging
 logger = logging.getLogger(__name__)
 
 
-class Dispersion(ReactiveTransport):
+class Dispersion(AdvectionDiffusion):
     r"""
-    A subclass of GenericTransport to simulate advection diffusion
+    A subclass of GenericTransport to simulate dispersion
 
     """
 
     def __init__(self, settings={}, phase=None, **kwargs):
         def_set = {'phase': None,
-                   'quantity': 'pore.concentration',
                    'conductance': 'throat.dispersive_conductance',
                    'gui': {'setup':        {'phase': None,
                                             'quantity': '',
@@ -30,143 +28,3 @@ class Dispersion(ReactiveTransport):
         self.settings.update(settings)
         if phase is not None:
             self.setup(phase=phase)
-
-    def setup(self, phase=None, quantity='', conductance='', **kwargs):
-        r"""
-
-        """
-        if phase:
-            self.settings['phase'] = phase.name
-        if quantity:
-            self.settings['quantity'] = quantity
-        if conductance:
-            self.settings['conductance'] = conductance
-        super().setup(**kwargs)
-
-    def set_outflow_BC(self, pores, mode='merge'):
-        r"""
-        Adds outflow boundary condition to the selected pores.
-
-        Outflow condition simply means that the gradient of the solved
-        quantity does not change, i.e. is 0.
-
-        """
-        # Hijack the parse_mode function to verify mode/pores argument
-        mode = self._parse_mode(mode, allowed=['merge', 'overwrite', 'remove'],
-                                single=True)
-        pores = self._parse_indices(pores)
-        # Calculating A[i,i] values to ensure the outflow condition
-        network = self.project.network
-        phase = self.project.phases()[self.settings['phase']]
-        throats = network.find_neighbor_throats(pores=pores)
-        C12 = network['throat.conns'][throats]
-        P12 = phase[self.settings['pressure']][C12]
-        gh = phase[self.settings['hydraulic_conductance']][throats]
-        Q12 = -gh * np.diff(P12, axis=1).squeeze()
-        Qp = np.zeros(self.Np)
-        np.add.at(Qp, C12[:, 0], -Q12)
-        np.add.at(Qp, C12[:, 1], Q12)
-        # Store boundary values
-        if ('pore.bc_outflow' not in self.keys()) or (mode == 'overwrite'):
-            self['pore.bc_outflow'] = np.nan
-        self['pore.bc_outflow'][pores] = Qp[pores]
-
-    def _apply_BCs(self):
-        # Apply Dirichlet and rate BCs
-        ReactiveTransport._apply_BCs(self)
-        if 'pore.bc_outflow' not in self.keys():
-            return
-        # Apply outflow BC
-        diag = self.A.diagonal()
-        ind = np.isfinite(self['pore.bc_outflow'])
-        diag[ind] += self['pore.bc_outflow'][ind]
-        self.A.setdiag(diag)
-
-    def rate(self, pores=[], throats=[], mode='group'):
-        r"""
-        Calculates the net rate of material moving into a given set of pores or
-        throats
-
-        Parameters
-        ----------
-        pores : array_like
-            The pores for which the rate should be calculated
-
-        throats : array_like
-            The throats through which the rate should be calculated
-
-        mode : string, optional
-            Controls how to return the rate.  Options are:
-
-            *'group'*: (default) Returns the cumulative rate of material
-            moving into the given set of pores
-
-            *'single'* : Calculates the rate for each pore individually
-
-        Returns
-        -------
-        If ``pores`` are specified, then the returned values indicate the
-        net rate of material exiting the pore or pores.  Thus a positive
-        rate indicates material is leaving the pores, and negative values
-        mean material is entering.
-
-        If ``throats`` are specified the rate is calculated in the direction of
-        the gradient, thus is always positive.
-
-        If ``mode`` is 'single' then the cumulative rate through the given
-        pores (or throats) are returned as a vector, if ``mode`` is 'group'
-        then the individual rates are summed and returned as a scalar.
-
-        """
-        pores = self._parse_indices(pores)
-        throats = self._parse_indices(throats)
-
-        network = self.project.network
-        C12 = network["throat.conns"]
-        phase = self.project.phases()[self.settings['phase']]
-
-        P = phase[self.settings['pressure']]
-        X = self[self.settings['quantity']]
-        gh = phase[self.settings['hydraulic_conductance']]
-        gd = phase[self.settings['diffusive_conductance']]
-
-        Q12 = -gh * np.diff(P[C12], axis=1).squeeze()
-        Pe12 = Q12 / gd
-        Pe12 = np.abs(Pe12).clip(min=1e-10) * np.sign(Pe12)
-        Q12 = Pe12 * gd
-
-        X12 = X[C12]
-        dX = -np.diff(X12, axis=1).squeeze()
-
-        s_dis = self.settings['s_scheme']
-
-        if s_dis == 'upwind':
-            w = gd + np.maximum(0, -Q12)
-            Qt = -Q12 * X12[:, 0] - dX * w
-        elif s_dis == 'hybrid':
-            w = np.maximum(0, np.maximum(-Q12, gd-Q12/2))
-            Qt = -Q12 * X12[:, 0] - dX * w
-        elif s_dis == 'powerlaw':
-            w = gd * np.maximum(0, (1 - 0.1*np.abs(Pe12))**5) + \
-                np.maximum(0, -Q12)
-            Qt = -Q12 * X12[:, 0] - dX * w
-        elif s_dis == 'exponential':
-            w = -Q12 / (1 - np.exp(Pe12))
-            Qt = -Q12 * X12[:, 0] - dX * w
-
-        if len(throats) and len(pores):
-            raise Exception('Must specify either pores or throats, not both')
-
-        if len(throats):
-            R = np.absolute(Qt[throats])
-
-        if len(pores):
-            Qp = np.zeros((self.Np,))
-            np.add.at(Qp, C12[:, 0], -Qt)
-            np.add.at(Qp, C12[:, 1], Qt)
-            R = Qp[pores]
-
-        if mode == 'group':
-            R = np.sum(R)
-
-        return np.array(R, ndmin=1)
diff --git a/openpnm/algorithms/TransientAdvectionDiffusion.py b/openpnm/algorithms/TransientAdvectionDiffusion.py
index f45c18182..84445d5ea 100644
--- a/openpnm/algorithms/TransientAdvectionDiffusion.py
+++ b/openpnm/algorithms/TransientAdvectionDiffusion.py
@@ -7,7 +7,7 @@ class TransientAdvectionDiffusion(TransientReactiveTransport,
                                   AdvectionDiffusion):
     r"""
     A subclass of GenericTransport to perform steady and transient simulations
-    of pure diffusion and advection diffusion problems.
+    of pure diffusion and advection-diffusion problems.
 
     """
 
@@ -15,10 +15,7 @@ class TransientAdvectionDiffusion(TransientReactiveTransport,
         def_set = {'phase': None,
                    'gui': {'setup':        {'phase': None,
                                             'quantity': '',
-                                            'diffusive_conductance': '',
-                                            'hydraulic_conductance': '',
-                                            'pressure': '',
-                                            's_scheme': '',
+                                            'conductance': '',
                                             't_initial': None,
                                             't_final': None,
                                             't_step': None,
@@ -40,14 +37,17 @@ class TransientAdvectionDiffusion(TransientReactiveTransport,
         if phase is not None:
             self.setup(phase=phase)
 
-    def setup(self, phase=None, quantity='', diffusive_conductance='',
-              hydraulic_conductance='', pressure='', s_scheme='',
-              t_initial=None, t_final=None, t_step=None, t_output=None,
-              t_tolerance=None, t_scheme='', **kwargs):
+    def setup(self, phase=None, quantity='', conductance='',
+              diffusive_conductance='', hydraulic_conductance='', pressure='',
+              s_scheme='', t_initial=None, t_final=None, t_step=None,
+              t_output=None, t_tolerance=None, t_precision=None, t_scheme='',
+              **kwargs):
         if phase:
             self.settings['phase'] = phase.name
         if quantity:
             self.settings['quantity'] = quantity
+        if conductance:
+            self.settings['conductance'] = conductance
         if diffusive_conductance:
             self.settings['diffusive_conductance'] = diffusive_conductance
         if hydraulic_conductance:
@@ -56,16 +56,18 @@ class TransientAdvectionDiffusion(TransientReactiveTransport,
             self.settings['pressure'] = pressure
         if s_scheme:
             self.settings['s_scheme'] = s_scheme
-        if t_initial:
+        if t_initial is not None:
             self.settings['t_initial'] = t_initial
-        if t_final:
+        if t_final is not None:
             self.settings['t_final'] = t_final
-        if t_step:
+        if t_step is not None:
             self.settings['t_step'] = t_step
         if t_output is not None:
             self.settings['t_output'] = t_output
-        if t_tolerance:
+        if t_tolerance is not None:
             self.settings['t_tolerance'] = t_tolerance
+        if t_precision is not None:
+            self.settings['t_tolerance'] = t_precision
         if t_scheme:
             self.settings['t_scheme'] = t_scheme
         self.settings.update(kwargs)
diff --git a/openpnm/algorithms/TransientReactiveTransport.py b/openpnm/algorithms/TransientReactiveTransport.py
index 959b069b9..b14aa39ac 100644
--- a/openpnm/algorithms/TransientReactiveTransport.py
+++ b/openpnm/algorithms/TransientReactiveTransport.py
@@ -140,17 +140,17 @@ class TransientReactiveTransport(ReactiveTransport):
             self.settings['quantity'] = quantity
         if conductance:
             self.settings['conductance'] = conductance
-        if t_initial:
+        if t_initial is not None:
             self.settings['t_initial'] = t_initial
-        if t_final:
+        if t_final is not None:
             self.settings['t_final'] = t_final
-        if t_step:
+        if t_step is not None:
             self.settings['t_step'] = t_step
         if t_output is not None:
             self.settings['t_output'] = t_output
-        if t_tolerance:
+        if t_tolerance is not None:
             self.settings['t_tolerance'] = t_tolerance
-        if t_precision:
+        if t_precision is not None:
             self.settings['t_precision'] = t_precision
         if t_scheme:
             self.settings['t_scheme'] = t_scheme
@@ -406,11 +406,13 @@ class TransientReactiveTransport(ReactiveTransport):
 
         Parameters
         ----------
-        times : scalar, ND-array, or list
+        times : scalar, ND-array, list of scalars, None, or string
             Time steps to be returned. The default value is None which results
             in returning all time steps. If times is a scalar, only the
             corresponding time step is returned. If times is an ND-array or a
-            list, time steps in the provided array or list are returned.
+            list of scalars, time steps in the provided array or list are
+            returned. If times is 'final' or 'actual', the current value of the
+            quantity is returned.
 
         t_precision : integer
             The time precision (number of decimal places). Default value is 12.
@@ -426,6 +428,8 @@ class TransientReactiveTransport(ReactiveTransport):
         q = [k for k in list(self.keys()) if quantity in k]
         if times is None:
             t = q
+        elif times in ['final', 'actual']:
+            t = [quantity]
         elif type(times) in [np.ndarray, list, float, int]:
             out = np.array(times)
             out = np.unique(out)
diff --git a/openpnm/models/physics/__init__.py b/openpnm/models/physics/__init__.py
index e4fc96120..ee0f0eea7 100644
--- a/openpnm/models/physics/__init__.py
+++ b/openpnm/models/physics/__init__.py
@@ -23,3 +23,4 @@ from . import poisson_shape_factors
 from . import meniscus
 from . import ionic_conductance
 from . import ad_dif_mig_conductance
+from . import ad_dif_conductance
diff --git a/openpnm/models/physics/ad_dif_conductance.py b/openpnm/models/physics/ad_dif_conductance.py
new file mode 100644
index 000000000..e497f94c0
--- /dev/null
+++ b/openpnm/models/physics/ad_dif_conductance.py
@@ -0,0 +1,218 @@
+r"""
+
+.. autofunction:: openpnm.models.physics.ad_dif_conductance.ad_dif
+.. autofunction:: openpnm.models.physics.ad_dif_conductance.generic_conductance
+
+"""
+
+import scipy as _sp
+
+
+def ad_dif(target,
+           pore_area='pore.area',
+           throat_area='throat.area',
+           pore_diffusivity='pore.diffusivity',
+           throat_diffusivity='throat.diffusivity',
+           conduit_lengths='throat.conduit_lengths',
+           conduit_shape_factors='throat.poisson_shape_factors',
+           pore_pressure='pore.pressure',
+           throat_hydraulic_conductance='throat.hydraulic_conductance',
+           throat_diffusive_conductance='throat.diffusive_conductance',
+           s_scheme='powerlaw'):
+    r"""
+    Calculate the advective-diffusive conductance of conduits in network, where
+    a conduit is ( 1/2 pore - full throat - 1/2 pore ). See the notes section.
+
+    Parameters
+    ----------
+    target : OpenPNM Object
+        The object which this model is associated with. This controls the
+        length of the calculated array, and also provides access to other
+        necessary properties.
+
+    pore_area : string
+        Dictionary key of the pore area values
+
+    throat_area : string
+        Dictionary key of the throat area values
+
+    pore_diffusivity : string
+        Dictionary key of the pore diffusivity values
+
+    throat_diffusivity : string
+        Dictionary key of the throat diffusivity values
+
+    conduit_lengths : string
+        Dictionary key of the conduit length values
+
+    conduit_shape_factors : string
+        Dictionary key of the conduit DIFFUSION shape factor values
+
+    pore_pressure : string
+        Dictionary key of the pore pressure values
+
+   throat_hydraulic_conductance : string
+       Dictionary key of the throat hydraulic conductance values
+
+   throat_diffusive_conductance : string
+       Dictionary key of the throat diffusive conductance values
+
+   s_scheme : string
+       Name of the space discretization scheme to use
+
+    Returns
+    -------
+    g : ndarray
+        Array containing advective-diffusive conductance values for conduits in
+        the geometry attached to the given physics object.
+
+    Notes
+    -----
+    (1) This function requires that all the necessary phase properties already
+    be calculated.
+
+    (2) This function calculates the specified property for the *entire*
+    network then extracts the values for the appropriate throats at the end.
+
+    (3) This function assumes cylindrical throats with constant cross-section
+    area. Corrections for different shapes and variable cross-section area can
+    be imposed by passing the proper flow_shape_factor argument.
+
+    """
+    return generic_conductance(
+        target=target,
+        transport_type='ad_dif',
+        pore_area=pore_area,
+        throat_area=throat_area,
+        pore_diffusivity=pore_diffusivity,
+        throat_diffusivity=throat_diffusivity,
+        conduit_lengths=conduit_lengths,
+        conduit_shape_factors=conduit_shape_factors,
+        pore_pressure=pore_pressure,
+        throat_hydraulic_conductance=throat_hydraulic_conductance,
+        throat_diffusive_conductance=throat_diffusive_conductance,
+        s_scheme=s_scheme)
+
+
+def generic_conductance(target, transport_type, pore_area, throat_area,
+                        pore_diffusivity, throat_diffusivity,
+                        conduit_lengths, conduit_shape_factors, **kwargs):
+    r"""
+    Calculate the generic conductance (could be mass, thermal, electrical,
+    ionic, or hydraylic) of conduits in the network, where a conduit is
+    ( 1/2 pore - full throat - 1/2 pore ).
+
+    Parameters
+    ----------
+    target : OpenPNM Object
+        The object which this model is associated with. This controls the
+        length of the calculated array, and also provides access to other
+        necessary properties.
+
+    transport_type : string
+        Dictionary key of the transport type
+
+    pore_area : string
+        Dictionary key of the pore area values
+
+    throat_area : string
+        Dictionary key of the throat area values
+
+    pore_diffusivity : string
+        Dictionary key of the pore diffusivity values
+
+    throat_diffusivity : string
+        Dictionary key of the throat diffusivity values
+
+    conduit_lengths : string
+        Dictionary key of the conduit length values
+
+    conduit_shape_factors : string
+        Dictionary key of the conduit DIFFUSION shape factor values
+
+    Returns
+    -------
+    g : ndarray
+        Array containing conductance values for conduits in the geometry
+        attached to the given physics object.
+
+    Notes
+    -----
+    (1) This function requires that all the necessary phase properties already
+    be calculated.
+
+    (2) This function calculates the specified property for the *entire*
+    network then extracts the values for the appropriate throats at the end.
+
+    (3) This function assumes cylindrical throats with constant cross-section
+    area. Corrections for different shapes and variable cross-section area can
+    be imposed by passing the proper shape factor.
+
+    (4) shape_factor depends on the physics of the problem, i.e. diffusion-like
+    processes and fluid flow need different shape factors.
+
+    """
+    network = target.project.network
+    throats = network.map_throats(throats=target.Ts, origin=target)
+    phase = target.project.find_phase(target)
+    cn = network['throat.conns'][throats]
+    # Getting conduit lengths
+    L1 = network[conduit_lengths + '.pore1'][throats]
+    Lt = network[conduit_lengths + '.throat'][throats]
+    L2 = network[conduit_lengths + '.pore2'][throats]
+    # Preallocating g
+    g1, g2, gt = _sp.zeros((3, len(Lt)))
+    # Setting g to inf when Li = 0 (ex. boundary pores)
+    # INFO: This is needed since area could also be zero, which confuses NumPy
+    m1, m2, mt = [Li != 0 for Li in [L1, L2, Lt]]
+    g1[~m1] = g2[~m2] = gt[~mt] = _sp.inf
+    # Getting shape factors
+    try:
+        SF1 = phase[conduit_shape_factors+'.pore1'][throats]
+        SFt = phase[conduit_shape_factors+'.throat'][throats]
+        SF2 = phase[conduit_shape_factors+'.pore2'][throats]
+    except KeyError:
+        SF1 = SF2 = SFt = 1.0
+    # Find g for half of pore 1, throat, and half of pore 2
+    if transport_type in ['dispersion', 'ad_dif']:
+        for k, v in kwargs.items():
+            if k == 'pore_pressure':
+                pore_pressure = v
+            elif k == 'throat_hydraulic_conductance':
+                throat_hydraulic_conductance = v
+            elif k == 'throat_diffusive_conductance':
+                throat_diffusive_conductance = v
+            elif k == 's_scheme':
+                s_scheme = v
+
+        P = phase[pore_pressure]
+        gh = phase[throat_hydraulic_conductance]
+        gd = phase[throat_diffusive_conductance]
+        gd = _sp.tile(gd, 2)
+
+        Qij = -gh*_sp.diff(P[cn], axis=1).squeeze()
+        Qij = _sp.append(Qij, -Qij)
+
+        Peij = Qij/gd
+        Peij[(Peij < 1e-10) & (Peij >= 0)] = 1e-10
+        Peij[(Peij > -1e-10) & (Peij <= 0)] = -1e-10
+        Qij = Peij*gd
+
+        if s_scheme == 'upwind':
+            w = gd + _sp.maximum(0, -Qij)
+        elif s_scheme == 'hybrid':
+            w = _sp.maximum(0, _sp.maximum(-Qij, gd-Qij/2))
+        elif s_scheme == 'powerlaw':
+            w = gd * _sp.maximum(0, (1 - 0.1*_sp.absolute(Peij))**5) + \
+                _sp.maximum(0, -Qij)
+        elif s_scheme == 'exponential':
+            w = -Qij / (1 - _sp.exp(Peij))
+        else:
+            raise Exception('Unrecognized discretization scheme: ' + s_scheme)
+        w = _sp.reshape(w, (network.Nt, 2), order='F')
+        return w
+    else:
+        raise Exception('Unknown keyword for "transport_type", can only be' +
+                        ' "dispersion"')
+    # Apply shape factors and calculate the final conductance
+    return (1/gt/SFt + 1/g1/SF1 + 1/g2/SF2)**(-1)
diff --git a/openpnm/models/physics/ad_dif_mig_conductance.py b/openpnm/models/physics/ad_dif_mig_conductance.py
index 80e1ef33e..41848f7e8 100644
--- a/openpnm/models/physics/ad_dif_mig_conductance.py
+++ b/openpnm/models/physics/ad_dif_mig_conductance.py
@@ -1,7 +1,7 @@
 r"""
 
-.. autofunction:: openpnm.models.physics.diffusive_conductance.ad_dif_mig
-.. autofunction:: openpnm.models.physics.diffusive_conductance.generic_conductance
+.. autofunction:: openpnm.models.physics.ad_dif_mig_conductance.ad_dif_mig
+.. autofunction:: openpnm.models.physics.ad_dif_mig_conductance.generic_conductance
 
 """
 
diff --git a/openpnm/models/physics/dispersive_conductance.py b/openpnm/models/physics/dispersive_conductance.py
index 3717f283e..fe53691fc 100644
--- a/openpnm/models/physics/dispersive_conductance.py
+++ b/openpnm/models/physics/dispersive_conductance.py
@@ -1,11 +1,11 @@
 r"""
 
-.. autofunction:: openpnm.models.physics.diffusive_conductance.dispersion
-.. autofunction:: openpnm.models.physics.diffusive_conductance.generic_conductance
+.. autofunction:: openpnm.models.physics.dispersive_conductance.dispersion
+.. autofunction:: openpnm.models.physics.ad_dif_conductance.generic_conductance
 
 """
 
-import scipy as _sp
+from .ad_dif_conductance import generic_conductance
 
 
 def dispersion(target,
@@ -20,9 +20,8 @@ def dispersion(target,
                throat_diffusive_conductance='throat.diffusive_conductance',
                s_scheme='powerlaw'):
     r"""
-    Calculate the advective-diffusive conductance of conduits in network,
-    where a conduit is ( 1/2 pore - full throat - 1/2 pore ).
-    See the notes section.
+    Calculate the dispersive conductance of conduits in network, where
+    a conduit is ( 1/2 pore - full throat - 1/2 pore ). See the notes section.
 
     Parameters
     ----------
@@ -93,127 +92,3 @@ def dispersion(target,
         throat_hydraulic_conductance=throat_hydraulic_conductance,
         throat_diffusive_conductance=throat_diffusive_conductance,
         s_scheme=s_scheme)
-
-
-def generic_conductance(target, transport_type, pore_area, throat_area,
-                        pore_diffusivity, throat_diffusivity,
-                        conduit_lengths, conduit_shape_factors, **kwargs):
-    r"""
-    Calculate the generic conductance (could be mass, thermal, electrical,
-    ionic, or hydraylic) of conduits in the network, where a conduit is
-    ( 1/2 pore - full throat - 1/2 pore ).
-
-    Parameters
-    ----------
-    target : OpenPNM Object
-        The object which this model is associated with. This controls the
-        length of the calculated array, and also provides access to other
-        necessary properties.
-
-    transport_type : string
-        Dictionary key of the transport type
-
-    pore_area : string
-        Dictionary key of the pore area values
-
-    throat_area : string
-        Dictionary key of the throat area values
-
-    pore_diffusivity : string
-        Dictionary key of the pore diffusivity values
-
-    throat_diffusivity : string
-        Dictionary key of the throat diffusivity values
-
-    conduit_lengths : string
-        Dictionary key of the conduit length values
-
-    conduit_shape_factors : string
-        Dictionary key of the conduit DIFFUSION shape factor values
-
-    Returns
-    -------
-    g : ndarray
-        Array containing conductance values for conduits in the geometry
-        attached to the given physics object.
-
-    Notes
-    -----
-    (1) This function requires that all the necessary phase properties already
-    be calculated.
-
-    (2) This function calculates the specified property for the *entire*
-    network then extracts the values for the appropriate throats at the end.
-
-    (3) This function assumes cylindrical throats with constant cross-section
-    area. Corrections for different shapes and variable cross-section area can
-    be imposed by passing the proper shape factor.
-
-    (4) shape_factor depends on the physics of the problem, i.e. diffusion-like
-    processes and fluid flow need different shape factors.
-
-    """
-    network = target.project.network
-    throats = network.map_throats(throats=target.Ts, origin=target)
-    phase = target.project.find_phase(target)
-    cn = network['throat.conns'][throats]
-    # Getting conduit lengths
-    L1 = network[conduit_lengths + '.pore1'][throats]
-    Lt = network[conduit_lengths + '.throat'][throats]
-    L2 = network[conduit_lengths + '.pore2'][throats]
-    # Preallocating g
-    g1, g2, gt = _sp.zeros((3, len(Lt)))
-    # Setting g to inf when Li = 0 (ex. boundary pores)
-    # INFO: This is needed since area could also be zero, which confuses NumPy
-    m1, m2, mt = [Li != 0 for Li in [L1, L2, Lt]]
-    g1[~m1] = g2[~m2] = gt[~mt] = _sp.inf
-    # Getting shape factors
-    try:
-        SF1 = phase[conduit_shape_factors+'.pore1'][throats]
-        SFt = phase[conduit_shape_factors+'.throat'][throats]
-        SF2 = phase[conduit_shape_factors+'.pore2'][throats]
-    except KeyError:
-        SF1 = SF2 = SFt = 1.0
-    # Find g for half of pore 1, throat, and half of pore 2
-    if transport_type == 'dispersion':
-        for k, v in kwargs.items():
-            if k == 'pore_pressure':
-                pore_pressure = v
-            elif k == 'throat_hydraulic_conductance':
-                throat_hydraulic_conductance = v
-            elif k == 'throat_diffusive_conductance':
-                throat_diffusive_conductance = v
-            elif k == 's_scheme':
-                s_scheme = v
-
-        P = phase[pore_pressure]
-        gh = phase[throat_hydraulic_conductance]
-        gd = phase[throat_diffusive_conductance]
-        gd = _sp.tile(gd, 2)
-
-        Qij = -gh*_sp.diff(P[cn], axis=1).squeeze()
-        Qij = _sp.append(Qij, -Qij)
-
-        Peij = Qij/gd
-        Peij[(Peij < 1e-10) & (Peij >= 0)] = 1e-10
-        Peij[(Peij > -1e-10) & (Peij <= 0)] = -1e-10
-        Qij = Peij*gd
-
-        if s_scheme == 'upwind':
-            w = gd + _sp.maximum(0, -Qij)
-        elif s_scheme == 'hybrid':
-            w = _sp.maximum(0, _sp.maximum(-Qij, gd-Qij/2))
-        elif s_scheme == 'powerlaw':
-            w = gd * _sp.maximum(0, (1 - 0.1*_sp.absolute(Peij))**5) + \
-                _sp.maximum(0, -Qij)
-        elif s_scheme == 'exponential':
-            w = -Qij / (1 - _sp.exp(Peij))
-        else:
-            raise Exception('Unrecognized discretization scheme: ' + s_scheme)
-        w = _sp.reshape(w, (network.Nt, 2), order='F')
-        return w
-    else:
-        raise Exception('Unknown keyword for "transport_type", can only be' +
-                        ' "dispersion"')
-    # Apply shape factors and calculate the final conductance
-    return (1/gt/SFt + 1/g1/SF1 + 1/g2/SF2)**(-1)


Test patch:
diff --git a/tests/unit/algorithms/AdvectionDiffusionTest.py b/tests/unit/algorithms/AdvectionDiffusionTest.py
index abdacdaa7..186e3b360 100644
--- a/tests/unit/algorithms/AdvectionDiffusionTest.py
+++ b/tests/unit/algorithms/AdvectionDiffusionTest.py
@@ -11,6 +11,9 @@ class AdvectionDiffusionTest:
         self.geo = op.geometry.GenericGeometry(network=self.net,
                                                pores=self.net.Ps,
                                                throats=self.net.Ts)
+        self.geo['throat.conduit_lengths.pore1'] = 0.1
+        self.geo['throat.conduit_lengths.throat'] = 0.6
+        self.geo['throat.conduit_lengths.pore2'] = 0.1
 
         self.phase = op.phases.GenericPhase(network=self.net)
         self.phys = op.physics.GenericPhysics(network=self.net,
@@ -32,7 +35,12 @@ class AdvectionDiffusionTest:
         self.ad.set_value_BC(pores=self.net.pores('front'), values=0)
 
     def test_powerlaw_advection_diffusion_diffusion(self):
-        self.ad.setup(s_scheme='powerlaw')
+        mod = op.models.physics.ad_dif_conductance.ad_dif
+        self.phys.add_model(propname='throat.ad_dif_conductance_powerlaw',
+                            model=mod, s_scheme='powerlaw')
+        self.phys.regenerate_models()
+
+        self.ad.setup(conductance='throat.ad_dif_conductance_powerlaw')
         self.ad.run()
         x = [0., 0., 0.,
              0.89653, 0.89653, 0.89653,
@@ -42,7 +50,11 @@ class AdvectionDiffusionTest:
         assert_allclose(actual=y, desired=x)
 
     def test_upwind_advection_diffusion_diffusion(self):
-        self.ad.setup(s_scheme='upwind')
+        mod = op.models.physics.ad_dif_conductance.ad_dif
+        self.phys.add_model(propname='throat.ad_dif_conductance_upwind',
+                            model=mod, s_scheme='upwind')
+        self.phys.regenerate_models()
+        self.ad.setup(conductance='throat.ad_dif_conductance_upwind')
         self.ad.run()
         x = [0., 0., 0.,
              0.86486, 0.86486, 0.86486,
@@ -52,7 +64,12 @@ class AdvectionDiffusionTest:
         assert_allclose(actual=y, desired=x)
 
     def test_hybrid_advection_diffusion_diffusion(self):
-        self.ad.setup(s_scheme='hybrid')
+        mod = op.models.physics.ad_dif_conductance.ad_dif
+        self.phys.add_model(propname='throat.ad_dif_conductance_hybrid',
+                            model=mod, s_scheme='hybrid')
+        self.phys.regenerate_models()
+
+        self.ad.setup(conductance='throat.ad_dif_conductance_hybrid')
         self.ad.run()
         x = [0., 0., 0.,
              0.89908, 0.89908, 0.89908,
@@ -62,7 +79,12 @@ class AdvectionDiffusionTest:
         assert_allclose(actual=y, desired=x)
 
     def test_exponential_advection_diffusion_diffusion(self):
-        self.ad.setup(s_scheme='exponential')
+        mod = op.models.physics.ad_dif_conductance.ad_dif
+        self.phys.add_model(propname='throat.ad_dif_conductance_exponential',
+                            model=mod, s_scheme='exponential')
+        self.phys.regenerate_models()
+
+        self.ad.setup(conductance='throat.ad_dif_conductance_exponential')
         self.ad.run()
         x = [0., 0., 0.,
              0.89688173, 0.89688173, 0.89688173,
@@ -72,14 +94,11 @@ class AdvectionDiffusionTest:
         assert_allclose(actual=y, desired=x)
 
     def test_outflow_BC(self):
-        for scheme in ['upwind', 'hybrid', 'powerlaw', 'exponential']:
+        for s_scheme in ['upwind', 'hybrid', 'powerlaw', 'exponential']:
             ad = op.algorithms.AdvectionDiffusion(network=self.net,
                                                   phase=self.phase)
             ad.setup(quantity='pore.concentration',
-                     diffusive_conductance='throat.diffusive_conductance',
-                     hydraulic_conductance='throat.hydraulic_conductance',
-                     pressure='pore.pressure',
-                     s_scheme=scheme)
+                     conductance='throat.ad_dif_conductance_'+s_scheme)
 
             ad.set_value_BC(pores=self.net.pores('back'), values=2)
             ad.set_outflow_BC(pores=self.net.pores('front'))
@@ -89,14 +108,12 @@ class AdvectionDiffusionTest:
             assert_allclose(actual=y, desired=2.0)
 
     def test_rate(self):
-        for scheme in ['upwind', 'hybrid', 'powerlaw', 'exponential']:
+        for s_scheme in ['upwind', 'hybrid', 'powerlaw', 'exponential']:
             ad = op.algorithms.AdvectionDiffusion(network=self.net,
                                                   phase=self.phase)
             ad.setup(quantity='pore.concentration',
-                     diffusive_conductance='throat.diffusive_conductance',
-                     hydraulic_conductance='throat.hydraulic_conductance',
-                     pressure='pore.pressure',
-                     s_scheme=scheme)
+                     conductance='throat.ad_dif_conductance_'+s_scheme,
+                     s_scheme=s_scheme)
 
             ad.set_value_BC(pores=self.net.pores('back'), values=2)
             ad.set_value_BC(pores=self.net.pores('front'), values=0)
@@ -105,7 +122,7 @@ class AdvectionDiffusionTest:
             mdot_inlet = ad.rate(pores=self.net.pores("back"))[0]
             mdot_outlet = ad.rate(pores=self.net.pores("front"))[0]
             temp = sp.random.choice(self.net.pores(["back", "front"],
-                                                    mode="not"),
+                                                   mode="not"),
                                     size=3, replace=False)
             mdot_internal = ad.rate(pores=temp)[0]
             # Ensure no mass is generated within the network
diff --git a/tests/unit/algorithms/TransientAdvectionDiffusionTest.py b/tests/unit/algorithms/TransientAdvectionDiffusionTest.py
index 12ca03599..f32e1f7c9 100644
--- a/tests/unit/algorithms/TransientAdvectionDiffusionTest.py
+++ b/tests/unit/algorithms/TransientAdvectionDiffusionTest.py
@@ -1,6 +1,5 @@
 import openpnm as op
 import scipy as sp
-import pytest
 
 
 class TransientAdvectionDiffusionTest:
@@ -19,6 +18,9 @@ class TransientAdvectionDiffusionTest:
         self.phys['throat.diffusive_conductance'] = 1e-15
         self.phys['throat.hydraulic_conductance'] = 1e-15
         self.geo['pore.volume'] = 1e-27
+        self.geo['throat.conduit_lengths.pore1'] = 0.1
+        self.geo['throat.conduit_lengths.throat'] = 0.6
+        self.geo['throat.conduit_lengths.pore2'] = 0.1
 
     def test_transient_advection_diffusion(self):
         sf = op.algorithms.StokesFlow(network=self.net, phase=self.phase)
@@ -29,14 +31,20 @@ class TransientAdvectionDiffusionTest:
         sf.run()
         self.phase[sf.settings['quantity']] = sf[sf.settings['quantity']]
 
+        mod = op.models.physics.ad_dif_conductance.ad_dif
+        self.phys.add_model(propname='throat.ad_dif_conductance', model=mod,
+                            s_scheme='powerlaw')
+        self.phys.regenerate_models()
+
         ad = op.algorithms.TransientAdvectionDiffusion(network=self.net,
                                                        phase=self.phase)
-        ad.setup(quantity='pore.concentration',
+        ad.setup(phase=self.phase, quantity='pore.concentration',
+                 conductance='throat.ad_dif_conductance',
                  diffusive_conductance='throat.diffusive_conductance',
                  hydraulic_conductance='throat.hydraulic_conductance',
-                 pressure='pore.pressure', t_initial=0, t_final=100,
-                 t_step=1, t_output=50, t_tolerance=1e-20,
-                 s_scheme='powerlaw', t_scheme='implicit')
+                 pressure='pore.pressure', t_initial=0, t_final=100, t_step=1,
+                 t_output=50, t_tolerance=1e-20, t_precision=12,
+                 s_scheme='implicit')
         ad.set_IC(0)
         ad.set_value_BC(pores=self.net.pores('back'), values=2)
         ad.set_value_BC(pores=self.net.pores('front'), values=0)