Repository: PMEAL/OpenPNM
Pull request #2248 (2021-12-15 21:29:39)

Updating doc strings


Patch:
diff --git a/.gitignore b/.gitignore
index efe0befc5..8ebe852b4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -154,3 +154,6 @@ public/
 
 # Sphinx docs
 **/generated
+openpnm/core/.DS_Store
+*/.DS_Store
+*.DS_Store
diff --git a/openpnm/__init__.py b/openpnm/__init__.py
index d5f833e2d..9f4758cfb 100644
--- a/openpnm/__init__.py
+++ b/openpnm/__init__.py
@@ -22,9 +22,9 @@ from . import geometry
 from . import phases
 from . import physics
 from . import models
-from . import algorithms
 from . import solvers
 from . import integrators
+from . import algorithms
 from . import materials
 from . import topotools
 from . import io
diff --git a/openpnm/algorithms/_advection_diffusion.py b/openpnm/algorithms/_advection_diffusion.py
index d9c5a17ad..5c7c701c8 100644
--- a/openpnm/algorithms/_advection_diffusion.py
+++ b/openpnm/algorithms/_advection_diffusion.py
@@ -63,17 +63,20 @@ class AdvectionDiffusion(ReactiveTransport):
         pores : array_like
             The pore indices where the condition should be applied
         mode : str, optional
-            Controls how the boundary conditions are applied. Options are:
-
-                'merge' (default)
-                    Adds supplied boundary conditions to already existing
-                    conditions, and also overwrites any existing values.
-                    If at rate or value BC exists at the given locations,
-                    these are deleted, and outflow conditions are given
-                    priority.
-                'overwrite'
-                    Deletes all boundary conditions of the given type then
-                    adds the specified new ones.
+            Controls how the boundary conditions are applied. The default value
+            is 'merge'. Options are:
+
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'merge'      Adds supplied boundary conditions to already existing
+                         conditions, and also overwrites any existing values.
+                         If at rate or value BC exists at the given locations,
+                         these are deleted, and outflow conditions are given
+                         priority.
+            'overwrite'  Deletes all boundary conditions of the given type then
+                         adds the specified new ones.
+            ===========  =====================================================
 
         Notes
         -----
diff --git a/openpnm/algorithms/_generic_transport.py b/openpnm/algorithms/_generic_transport.py
index 4352e3ee9..84aef6658 100644
--- a/openpnm/algorithms/_generic_transport.py
+++ b/openpnm/algorithms/_generic_transport.py
@@ -1,12 +1,16 @@
-import warnings
 import numpy as np
+import scipy.sparse.linalg
+import warnings
 import scipy.sparse.csgraph as spgr
-from openpnm.topotools import is_fully_connected
+from copy import deepcopy
+from scipy.spatial import ConvexHull
+from scipy.spatial import cKDTree
+from openpnm.topotools import iscoplanar, is_fully_connected, dimensionality
 from openpnm.algorithms import GenericAlgorithm
 from openpnm.utils import logging, prettify_logger_message
 from openpnm.utils import Docorator, SettingsAttr
+from openpnm.utils import is_symmetric
 from openpnm.solvers import PardisoSpsolve
-from ._solution import SteadyStateSolution
 docstr = Docorator()
 logger = logging.getLogger(__name__)
 
@@ -429,9 +433,6 @@ class GenericTransport(GenericAlgorithm):
         in the ``settings`` attribute.
 
         """
-        # Initialize the solution object
-        self.soln = SteadyStateSolution(np.full(self.Np, fill_value=np.nan))
-        self.soln.is_converged = False
         logger.info('Running GenericTransport')
         solver = PardisoSpsolve() if solver is None else solver
         # Perform pre-solve validations
@@ -444,7 +445,6 @@ class GenericTransport(GenericAlgorithm):
         # Build A and b, then solve the system of equations
         self._update_A_and_b()
         self._run_special(solver=solver, x0=x0)
-        return self.soln
 
     def _run_special(self, solver, x0, w=1):
         # Make sure A,b are STILL well-defined
@@ -454,9 +454,6 @@ class GenericTransport(GenericAlgorithm):
         self.x = w * x_new + (1 - w) * self.x
         # Update A and b using the recent solution
         self._update_A_and_b()
-        # Update SteadyStateSolution object on algorithm
-        self.soln[:] = self.x
-        self.soln.is_converged = not bool(exit_code)
 
     def _update_A_and_b(self):
         r"""
diff --git a/openpnm/algorithms/_ordinary_percolation.py b/openpnm/algorithms/_ordinary_percolation.py
index 1e2b4aadd..43868180e 100644
--- a/openpnm/algorithms/_ordinary_percolation.py
+++ b/openpnm/algorithms/_ordinary_percolation.py
@@ -24,8 +24,15 @@ class OrdinaryPercolationSettings:
         inlets
     mode : str
         Controls whether pore or throat entry threshold values are used.
-        If ``'site'`` the pore entry is considered, if ``'bond'`` then
-        throat values are considered.
+        Options are:
+
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'site'       the pore entry is considered
+            'bond'       the throat values are considered.
+            ===========  =====================================================
+
     pore_entry_threshold : str
         The dictionary key for the pore entry pressure array
     throat_entry_threshold : str
diff --git a/openpnm/algorithms/_reactive_transport.py b/openpnm/algorithms/_reactive_transport.py
index 13d54ff78..6a2f6cf38 100644
--- a/openpnm/algorithms/_reactive_transport.py
+++ b/openpnm/algorithms/_reactive_transport.py
@@ -4,7 +4,6 @@ from scipy.optimize.nonlin import TerminationCondition
 from openpnm.algorithms import GenericTransport
 from openpnm.utils import logging
 from openpnm.utils import TypedList, Docorator, SettingsAttr
-from ._solution import SteadyStateSolution
 docstr = Docorator()
 logger = logging.getLogger(__name__)
 
@@ -239,19 +238,15 @@ class ReactiveTransport(GenericTransport):
         condition = TerminationCondition(f_rtol=f_rtol, x_rtol=x_rtol)
 
         for i in range(maxiter):
-            self.soln.num_iter = i + 1
             res = self._get_residual()
-            is_converged = bool(condition.check(f=res, x=xold, dx=dx))
-            if is_converged:
-                self.soln.is_converged = is_converged
+            self.is_converged = condition.check(f=res, x=xold, dx=dx)
+            if self.is_converged:
                 logger.info(f'Solution converged, residual norm: {norm(res):.4e}')
                 return
             super()._run_special(solver=solver, x0=xold, w=w)
             dx = self.x - xold
             xold = self.x
             logger.info(f'Iteration #{i:<4d} | Residual norm: {norm(res):.4e}')
-
-        self.soln.is_converged = False
         logger.critical(f"{self.name} didn't converge after {maxiter} iterations")
 
     def _update_A_and_b(self):
diff --git a/openpnm/algorithms/_solution.py b/openpnm/algorithms/_solution.py
deleted file mode 100644
index 2fa0bcf77..000000000
--- a/openpnm/algorithms/_solution.py
+++ /dev/null
@@ -1,53 +0,0 @@
-import numpy as np
-from scipy.interpolate import interp1d
-
-
-class Solution(np.ndarray):
-    """Brief description of 'Solution'"""
-    pass
-
-
-class SteadyStateSolution(np.ndarray):
-    r"""Brief description of 'SteadyStateSolution'"""
-
-    def __new__(cls, x):
-        obj = np.asarray(x).view(cls)
-        return obj
-
-
-class TransientSolution(Solution):
-    """Brief description of 'TransientSolution'"""
-
-    def __new__(cls, t, x):
-        obj = np.asarray(x).view(cls)
-        obj.t = np.asarray(t).view(cls)
-        return obj
-
-    def _create_interpolant(self):
-        self._interpolant = interp1d(self.t, self, bounds_error=True)
-
-    def interpolate(self, t):
-        """
-        Interpolates solution at time 't'.
-
-        Parameters
-        ----------
-        t : float
-            Time at which the solution is to be interpolated
-
-        Returns
-        -------
-        ndarray
-            Transient solution interpolated at the given time 't'
-
-        Notes
-        -----
-        't' must reside inside the 'tspan' used during integration.
-
-        """
-        # Cache interpolant to avoid overhead
-        if not hasattr(self, "_interpolant"):
-            self._create_interpolant()
-        return self._interpolant(t)
-
-    __call__ = interpolate
diff --git a/openpnm/core/_base.py b/openpnm/core/_base.py
index 8e72baeb7..3fac92c54 100644
--- a/openpnm/core/_base.py
+++ b/openpnm/core/_base.py
@@ -301,14 +301,19 @@ class Base(dict):
             and/or 'throat' arrays should be cleared.  The default is both.
         mode : str or List[str]
             This controls what is cleared from the object.  Options are:
-                **'props'** : Removes all numerical property values from
-                the object dictionary
-                **'model_data'** : Removes only numerical data that were
-                produced by an associated model
-                **'labels'** : Removes all labels from the object
-                dictionary, except those relating to the pore and throat
-                locations of associated objects
-                **'all'** : Removes both 'props' and 'labels'
+
+            ============  =====================================================
+            mode          meaning
+            ============  =====================================================
+            'props'       Removes all numerical property values from
+                          the object dictionary
+            'model_data'  Removes only numerical data that were
+                          produced by an associated model
+            'labels'      Removes all labels from the object
+                          dictionary, except those relating to the pore
+                          and throat locations of associated objects
+            'all'         Removes both 'props' and 'labels'
+            ============  =====================================================
 
         Notes
         -----
@@ -376,16 +381,19 @@ class Base(dict):
         mode : str, optional
             Controls which keys are returned. Options are:
 
-                **'labels'**
-                    Limits the returned list of keys to only 'labels'
-                    (boolean arrays)
-                **'props'**
-                    Limits he return list of keys to only 'props'
-                    (numerical arrays).
-                **'all'**
-                    Returns both 'labels' and 'props'. This is equivalent
-                    to sending a list of both 'labels' and 'props'.
-
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'labels'     Limits the returned list of keys to only 'labels'
+                         (boolean arrays)
+            'props'      Limits he return list of keys to only 'props'
+                         (numerical arrays).
+            'all'        Returns both 'labels' and 'props'. This is equivalent
+                         to sending a list of both 'labels' and 'props'.
+            ===========  =====================================================
+
+            Notes
+            -----
             If no mode is specified then the normal KeysView object is
             returned.
         deep : bool
@@ -452,13 +460,17 @@ class Base(dict):
             returned.  If no element is given, both are returned
         mode : str, optional
             Controls what type of properties are returned.  Options are:
-                **'all'** : Returns all properties on the object (default)
 
-                **'models'** : Returns only properties that are associated with a
-                model
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'all'        Returns all properties on the object (default)
+            'models'     Returns only properties that are associated with a
+                         model
+            'constants'  returns data values that were *not* generated by
+                         a model, but manaully created.
+            ===========  =====================================================
 
-                **'constants'** : returns data values that were *not* generated by
-                a model, but manaully created.
         deep : bool
             If set to ``True`` then the props on all associated subdomain
             objects are returned as well.
@@ -690,9 +702,9 @@ class Base(dict):
         if self._isa() in ['network', 'geometry']:
             subdomains = list(proj.geometries().values())
         elif self._isa() in ['phase', 'physics']:
-            subdomains= list(proj.find_physics(phase=self))
+            subdomains = list(proj.find_physics(phase=self))
         elif self._isa() in ['algorithm', 'base']:
-            subdomains= [self]
+            subdomains = [self]
         else:
             raise Exception('Unrecognized object type, cannot find dependents')
 
@@ -781,7 +793,7 @@ class Base(dict):
 
     def interpolate_data(self, propname, mode='mean'):
         r"""
-        Determines a pore (or throat) property as the average of it's
+        Determines a pore (or throat) property as the average of its
         neighboring throats (or pores)
 
         Parameters
@@ -789,8 +801,19 @@ class Base(dict):
         propname: str
             The dictionary key to the values to be interpolated.
         mode : str
-            The method used for interpolation.  Options are 'mean' (default),
-            'min', and 'max'.
+            The method used for interpolation. The default value is 'mean'
+            Options are:
+
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'mean'       returns the pore (or throat) property as the average
+                         of its neighboring throats (or pores)
+            'min'        returns the pore (or throat) property as the minimum
+                         of its neighboring throats (or pores)
+            'max'        returns the pore (or throat) property as the maximum
+                         of its neighboring throats (or pores)
+            ===========  =====================================================
 
         Returns
         -------
@@ -831,16 +854,19 @@ class Base(dict):
         mode : string
             How interpolation should be peformed for missing values. If values
             are present for both pores and throats, then this argument is
-            ignored.  The ``interpolate`` data method is used.  Options are:
-
-                * 'mean' (default)
-
-                **'mean'** (default):
-                    Finds the mean value of the neighboring pores (or throats)
-                * 'min'
-                    Finds the minimum of the neighboring pores (or throats)
-                * 'max'
-                    Finds the maximum of the neighboring pores (or throats)
+            ignored.  The ``interpolate`` data method is used. The default
+            value is 'mean'. Options are:
+
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'mean'       Finds the mean value of the neighboring pores
+                         (or throats)
+            'min'        Finds the minimum of the neighboring pores
+                         (or throats)
+            'max'        Finds the maximum of the neighboring pores
+                         (or throats)
+            ===========  =====================================================
 
         Returns
         -------
diff --git a/openpnm/core/_mixins.py b/openpnm/core/_mixins.py
index 33e313fef..83eb8704e 100644
--- a/openpnm/core/_mixins.py
+++ b/openpnm/core/_mixins.py
@@ -192,23 +192,22 @@ class LabelMixin:
             Controls how the query should be performed.  Only applicable
             when ``pores`` or ``throats`` are specified:
 
-            **'or', 'union', 'any'**: (default) Returns the labels that are
-            assigned to *any* of the given locations.
-
-            **'and', 'intersection', 'all'**: Labels that are present on *all*
-            the given locations.
-
-            **'xor', 'exclusive_or'** : Labels that are present on *only one*
-            of the given locations.
-
-            **'nor', 'none', 'not'**: Labels that are *not* present on any of
-            the given locations.
-
-            **'nand'**: Labels that are present on *all but one* of the given
-            locations
-
-            **'xnor'**: Labels that are present on *more than one* of the given
-            locations.  'nxor' is also accepted.
+            ==============  =====================================================
+            mode            meaning
+            ==============  =====================================================
+            'or'            Returns the labels that are assigned to *any* of the
+                            given locations. Also accepts 'union' and 'any'
+            'and'           Labels that are present on all the given locations.
+                            also accepts 'intersection' and 'all'
+            'xor'           Labels that are present on *only one*
+                            of the given locations.Also accepts 'exclusive_or'
+            'nor'           Labels that are *not* present on any of
+                            the given locations. Also accepts 'not' and 'none'
+            'nand'          Labels that are present on *all but one* of the given
+                            locations
+            'xnor'          Labels that are present on *more than one* of the given
+                            locations.
+            ==============  =====================================================
 
         Returns
         -------
@@ -385,24 +384,22 @@ class LabelMixin:
 
         mode : str
             Specifies how the query should be performed.  The options are:
-
-            **'or', 'union', 'any'** : (default) Pores with *one or more* of
-            the given labels are returned.
-
-            **'and', 'intersection', 'all'** : Pores with *all* of the given
-            labels are returned.
-
-            **'xor', 'exclusive_or'** : Pores with *only one* of the given
-            labels are returned.
-
-            **'nor', 'none', 'not'** : Pores with *none* of the given labels
-            are returned.
-
-            **'nand'** : Pores with *not all* of the given labels are
-            returned.
-
-            **'xnor'** : Pores with *more than one* of the given labels are
-            returned.
+            ==============  =====================================================
+            mode            meaning
+            ==============  =====================================================
+            'or'            Pores with *one or more* of the given labels are
+                            returned. Also accepts 'union' and 'any'.
+            'and'           Pores with all of the given labels are returned.
+                            Also accepts 'intersection' and 'all'.
+            'xor'           Pores with *only one* of the given labels are returned.
+                            Also accepts 'exclusive_or'.
+            'nor'           Pores with *none* of the given labels are returned.
+                            Also accepts 'not' and 'none'.
+            'nand'          Pores with *not all* of the given labels are
+                            returned.
+            'xnor'          Pores with *more than one* of the given labels are
+                            returned.
+            ==============  =====================================================
 
         asmask : bool
             If ``True`` then a boolean array of length Np is returned with
@@ -467,23 +464,22 @@ class LabelMixin:
         mode : str
             Specifies how the query should be performed.  The options are:
 
-            **'or', 'union', 'any'** : (default) Throats with *one or more* of
-            the given labels are returned.
-
-            **'and', 'intersection', 'all'** : Throats with *all* of the given
-            labels are returned.
-
-            **'xor', 'exclusive_or'** : Throats with *only one* of the given
-            labels are returned.
-
-            **'nor', 'none', 'not'** : Throats with *none* of the given labels
-            are returned.
-
-            **'nand'** : Throats with *not all* of the given labels are
-            returned.
-
-            **'xnor'** : Throats with *more than one* of the given labels are
-            returned.
+            ==============  =====================================================
+            mode            meaning
+            ==============  =====================================================
+            'or'            Throats with *one or more* of the given labels are
+                            returned. Also accepts 'union' and 'any'.
+            'and'           Throats with all of the given labels are returned.
+                            Also accepts 'intersection' and 'all'.
+            'xor'           Throats with *only one* of the given labels are returned.
+                            Also accepts 'exclusive_or'.
+            'nor'           Throats with *none* of the given labels are returned.
+                            Also accepts 'not' and 'none'.
+            'nand'          Throats with *not all* of the given labels are
+                            returned.
+            'xnor'          Throats with *more than one* of the given labels are
+                            returned.
+            ==============  =====================================================
 
         asmask : bool
             If ``True`` then a boolean array of length Nt is returned with
@@ -536,26 +532,25 @@ class LabelMixin:
             The labels to apply as a filter
 
         mode : str
-
-            Controls how the filter is applied.  Options include:
-
-            **'or', 'union', 'any'**: (default) Returns a list of the given
-            locations where *any* of the given labels exist.
-
-            **'and', 'intersection', 'all'**: Only locations where *all* the
-            given labels are found.
-
-            **'xor', 'exclusive_or'**: Only locations where exactly *one* of
-            the given labels are found.
-
-            **'nor', 'none', 'not'**: Only locations where *none* of the given
-            labels are found.
-
-            **'nand'** : Only locations with *some but not all* of the given
-            labels are returned.
-
-            **'xnor'** : Only locations with *more than one* of the given
-            labels are returned.
+            Controls how the filter is applied. The default value is
+            'or'. Options include:
+
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'or'         Returns a list of the given locations where *any* of
+                         the given labels exist. Also accepts 'union' and 'any'.
+            'and'        Only locations where *all* the given labels are
+                         found. Also accepts 'intersection' and 'all'.
+            'xor'        Only locations where exactly *one* of the given
+                         labels are found.
+            'nor'        Only locations where *none* of the given labels are
+                         found. Also accepts 'none' and 'not'
+            'nand'       Only locations with *some but not all* of the given
+                         labels are returned
+            'xnor'       Only locations with *more than one* of the given
+                         labels are returned
+            ===========  =====================================================
 
         Returns
         -------
@@ -613,23 +608,23 @@ class LabelMixin:
         mode : str, optional
             Specifies how the count should be performed.  The options are:
 
-            **'or', 'union', 'any'** : (default) Pores with *one or more* of
-            the given labels are counted.
-
-            **'and', 'intersection', 'all'** : Pores with *all* of the given
-            labels are counted.
+            ==============  =====================================================
+            mode            meaning
+            ==============  =====================================================
+            'or'            Pores with *one or more* of the given labels are
+                            counted. Also accepts 'union' and 'any'.
+            'and'           Pores with all of the given labels are returned.
+                            Also accepts 'intersection' and 'all'.
+            'xor'           Pores with *only one* of the given labels are
+                            counted. Also accepts 'exclusive_or'.
+            'nor'           Pores with *none* of the given labels are counted.
+                            Also accepts 'not' and 'none'.
+            'nand'          Pores with *not all* of the given labels are
+                            counted.
+            'xnor'          Pores with *more than one* of the given labels are
+                            counted.
+            ==============  =====================================================
 
-            **'xor', 'exclusive_or'** : Pores with *only one* of the given
-            labels are counted.
-
-            **'nor', 'none', 'not'** : Pores with *none* of the given labels
-            are counted.
-
-            **'nand'** : Pores with *some but not all* of the given labels are
-            counted.
-
-            **'xnor'** : Pores with *more than one* of the given labels are
-            counted.
 
         Returns
         -------
@@ -679,23 +674,22 @@ class LabelMixin:
         mode : str, optional
             Specifies how the count should be performed.  The options are:
 
-            **'or', 'union', 'any'** : (default) Throats with *one or more* of
-            the given labels are counted.
-
-            **'and', 'intersection', 'all'** : Throats with *all* of the given
-            labels are counted.
-
-            **'xor', 'exclusive_or'** : Throats with *only one* of the given
-            labels are counted.
-
-            **'nor', 'none', 'not'** : Throats with *none* of the given labels
-            are counted.
-
-            **'nand'** : Throats with *some but not all* of the given labels
-            are counted.
-
-            **'xnor'** : Throats with *more than one* of the given labels are
-            counted.
+            ==============  =====================================================
+            mode            meaning
+            ==============  =====================================================
+            'or'            Throats with *one or more* of the given labels are
+                            counted. Also accepts 'union' and 'any'.
+            'and'           Throats with all of the given labels are returned.
+                            Also accepts 'intersection' and 'all'.
+            'xor'           Throats with *only one* of the given labels are
+                            counted. Also accepts 'exclusive_or'.
+            'nor'           Throats with *none* of the given labels are counted.
+                            Also accepts 'not' and 'none'.
+            'nand'          Throats with *not all* of the given labels are
+                            counted.
+            'xnor'          Throats with *more than one* of the given labels are
+                            counted.
+            ==============  =====================================================
 
         Returns
         -------
diff --git a/openpnm/core/_models.py b/openpnm/core/_models.py
index bdf903949..40186fed0 100644
--- a/openpnm/core/_models.py
+++ b/openpnm/core/_models.py
@@ -278,20 +278,23 @@ class ModelsMixin:
             Controls how/when the model is run (See Notes for more details).
             Options are:
 
-            *'normal'* -  (default) The model is run directly upon being
-            assiged, and also run every time ``regenerate_models`` is called.
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'normal'     The model is run directly upon being
+                         assigned, and also run every time ``regenerate_models``
+                         is called.
+            'constant'   The model is run directly upon being assigned, but
+                         is not called again, thus making its data act like a
+                         constant. If, however, the data is deleted from the
+                         object it will be regenerated again.
+            'deferred'   Is not run upon being assigned, but is run the first
+                         time that ``regenerate_models`` is called.
+            'explicit'   Is only run if the model name is explicitly passed
+                         to the ``regenerate_models`` method.  This allows
+                         full control of when the model is run.
+            ===========  =====================================================
 
-            *'constant'* -  The model is run directly upon being assigned, but
-            is not called again, thus making it's data act like a constant.
-            If, however, the data is deleted from the object it will be
-            regenerated again.
-
-            *'deferred'* - Is not run upon being assigned, but is run the first
-            time that ``regenerate_models`` is called.
-
-            *'explicit'* - Is only run if the model name is explicitly passed
-            to the ``regenerate_models`` method.  This allows full control
-            of when the model is run.
 
         """
         if propname in kwargs.values():  # Prevent infinite loops of look-ups
@@ -413,11 +416,13 @@ class ModelsMixin:
         mode : list[str]
             Controls what is removed. Options are:
 
-            *'model'* : Removes the model but not any numerical data that may
-            already exist.
-
-            *'data'* : Removes the data but leaves the model.
-
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'model'      Removes the model but not any numerical data that may
+                         already exist.
+            data'        Removes the data but leaves the model.
+            ===========  =====================================================
         The default is both.
 
         """
diff --git a/openpnm/integrators/_scipy.py b/openpnm/integrators/_scipy.py
index 74ec21287..407f5a27f 100644
--- a/openpnm/integrators/_scipy.py
+++ b/openpnm/integrators/_scipy.py
@@ -1,8 +1,9 @@
+import numpy as np
 from scipy.integrate import solve_ivp
+from scipy.interpolate import interp1d
 from openpnm.integrators import Integrator
-from openpnm.algorithms._solution import TransientSolution
 
-__all__ = ['ScipyRK45']
+__all__ = ['ScipyRK45', 'Solution', 'TransientSolution']
 
 
 class ScipyRK45(Integrator):
@@ -53,3 +54,46 @@ class ScipyRK45(Integrator):
         if sol.success:
             return TransientSolution(sol.t, sol.y)
         raise Exception(sol.message)
+
+
+class Solution(np.ndarray):
+    """Brief description of 'Solution'"""
+    ...
+
+
+class TransientSolution(Solution):
+    """Brief description of 'TransientSolution'"""
+
+    def __new__(cls, t, x):
+        obj = np.asarray(x).view(cls)
+        obj.t = np.asarray(t).view(cls)
+        return obj
+
+    def _create_interpolant(self):
+        self._interpolant = interp1d(self.t, self, bounds_error=True)
+
+    def interpolate(self, t):
+        """
+        Interpolates solution at time 't'.
+
+        Parameters
+        ----------
+        t : float
+            Time at which the solution is to be interpolated
+
+        Returns
+        -------
+        ndarray
+            Transient solution interpolated at the given time 't'
+
+        Notes
+        -----
+        't' must reside inside the 'tspan' used during integration.
+
+        """
+        # Cache interpolant to avoid overhead
+        if not hasattr(self, "_interpolant"):
+            self._create_interpolant()
+        return self._interpolant(t)
+
+    __call__ = interpolate
diff --git a/openpnm/models/misc/neighbor_lookups.py b/openpnm/models/misc/neighbor_lookups.py
index 492a01c8c..c2a35f678 100644
--- a/openpnm/models/misc/neighbor_lookups.py
+++ b/openpnm/models/misc/neighbor_lookups.py
@@ -25,8 +25,19 @@ def from_neighbor_throats(target, prop, mode='min', ignore_nans=True):
         The dictionary key of the array containing the throat property to be
         used in the calculation.
     mode : str
-        Controls how the pore property is calculated.  Options are 'min',
-        'max' and 'mean'.
+        Controls how the pore property is calculated. The default value is
+        'min'. Options are:
+
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'min'        Returns the value of the minimum property of the
+                         neighboring throats
+            'max'        Returns the value of the maximum property of the
+                         neighboring throats
+            'mean'       Returns the value of the mean property of the
+                         neighboring throats
+            ===========  =====================================================
 
     Returns
     -------
@@ -78,8 +89,20 @@ def from_neighbor_pores(target, prop, mode='min', ignore_nans=True):
         The dictionary key to the array containing the pore property to be
         used in the calculation.
     mode : str
-        Controls how the throat property is calculated.  Options are 'min',
-        'max' and 'mean'.
+        Controls how the pore property is calculated. The default value is
+        'min'. Options are:
+
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'min'        Returns the value of the minimum property of the
+                         neighboring pores
+            'max'        Returns the value of the maximum property of the
+                         neighboring pores
+            'mean'       Returns the value of the mean property of the
+                         neighboring pores
+            ===========  =====================================================
+
     ignore_nans : bool (default is ``True``)
         If ``True`` the result will ignore ``nans`` in the neighbors
 
diff --git a/openpnm/network/_bravais.py b/openpnm/network/_bravais.py
index f19e146c7..96e03de53 100644
--- a/openpnm/network/_bravais.py
+++ b/openpnm/network/_bravais.py
@@ -34,15 +34,14 @@ class Bravais(GenericNetwork):
         directions. The default is 1.
     mode : str
         The type of lattice to create. Options are:
-
-            'sc'
-                Simple cubic (Same as ``Cubic``)
-            'bcc'
-                Body-centered cubic lattice
-            'fcc'
-                Face-centered cubic lattice
-            'hcp'
-                Hexagonal close packed (Note Implemented Yet)
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'sc'         Simple cubic (Same as ``Cubic``)
+            'bcc'        body-centered cubic lattice
+            'fcc'        Face-centered cubic lattice
+            'hcp'        Hexagonal close packed (Not Implemented Yet)
+            ===========  =====================================================
 
     name : str
         An optional name for the object to help identify it. If not given,
diff --git a/openpnm/network/_generic.py b/openpnm/network/_generic.py
index eaa421244..1232b0e42 100644
--- a/openpnm/network/_generic.py
+++ b/openpnm/network/_generic.py
@@ -420,22 +420,24 @@ class GenericNetwork(ParamMixin, Base, ModelsMixin, LabelMixin):
             throat, in the order they were sent.
         mode : str
             Specifies logic to filter the resulting list. Options are:
-                **'or'** : (default) All neighbors of the input pores.
-                This is also known as the 'union' in set theory or 'any'
-                in boolean logic. Both keywords are accepted and treated
-                as 'or'.
-                **'xor'** : Only neighbors of one and only one input pore.
-                This is useful for counting the pores that are not shared
-                by any of the input pores. This is known as 'exclusive_or'
-                in set theory, and is an accepted input.
-                **'xnor'** : Neighbors that are shared by two or more
-                input pores. This is equivalent to counting all neighbors
-                with 'or', minus those found with 'xor', and is useful for
-                finding neighbors that the inputs have in common.
-                **'and'** : Only neighbors shared by all input pores. This
-                is also known as 'intersection' in set theory and
-                (somtimes) as 'all' in boolean logic. Both keywords are
-                accepted and treated as 'and'.
+
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'or'         All neighbors of the input pores. Also accepts 'any'
+                         and 'union'.
+            'xor'        Only neighbors of one and only one input pore. This
+                         is useful for counting the pores that are not shared
+                         by any of the input pores. Also accepts
+                         'exclusive_or'.
+            'xnor'       Neighbors that are shared by two or more input pores.
+                         This is equivalent to counting all neighbors
+                         with 'or', minus those found with 'xor', and is
+                         useful for finding neighbors that the inputs have
+                         in common.
+            'and'        Only neighbors shared by all input pores. Also
+                         accepts 'intersection' and 'all'
+            ===========  =====================================================
 
         Returns
         -------
@@ -523,22 +525,24 @@ class GenericNetwork(ParamMixin, Base, ModelsMixin, LabelMixin):
             include N, even if this flag is ``True``.
         mode : str
             Specifies logic to filter the resulting list. Options are:
-                **'or'** : (default) All neighbors of the input pores.
-                This is also known as the 'union' in set theory or 'any'
-                in boolean logic. Both keywords are accepted and treated
-                as 'or'.
-                **'xor'** : Only neighbors of one and only one input pore.
-                This is useful for counting the pores that are not shared
-                by any of the input pores. This is known as 'exclusive_or'
-                in set theory, and is an accepted input.
-                **'xnor'** : Neighbors that are shared by two or more
-                input pores. This is equivalent to counting all neighbors
-                with 'or', minus those found with 'xor', and is useful for
-                finding neighbors that the inputs have in common.
-                **'and'** : Only neighbors shared by all input pores. This
-                is also known as 'intersection' in set theory and
-                (somtimes) as 'all' in boolean logic. Both keywords are
-                accepted and treated as 'and'.
+
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'or'         All neighbors of the input pores. Also accepts 'any'
+                         and 'union'.
+            'xor'        Only neighbors of one and only one input pore. This
+                         is useful for counting the pores that are not shared
+                         by any of the input pores. Also accepts
+                         'exclusive_or'.
+            'xnor'       Neighbors that are shared by two or more input pores.
+                         This is equivalent to counting all neighbors
+                         with 'or', minus those found with 'xor', and is
+                         useful for finding neighbors that the inputs have
+                         in common.
+            'and'        Only neighbors shared by all input pores. Also
+                         accepts 'intersection' and 'all'
+            ===========  =====================================================
 
         asmask : boolean
             If ``False`` (default), the returned result is a list of the
@@ -620,22 +624,24 @@ class GenericNetwork(ParamMixin, Base, ModelsMixin, LabelMixin):
             they were sent.
         mode : str
             Specifies logic to filter the resulting list. Options are:
-                **'or'** : (default) All neighbors of the input pores.
-                This is also known as the 'union' in set theory or 'any'
-                in boolean logic. Both keywords are accepted and treated
-                as 'or'.
-                **'xor'** : Only neighbors of one and only one input pore.
-                This is useful for counting the pores that are not shared
-                by any of the input pores. This is known as 'exclusive_or'
-                in set theory, and is an accepted input.
-                **'xnor'** : Neighbors that are shared by two or more
-                input pores. This is equivalent to counting all neighbors
-                with 'or', minus those found with 'xor', and is useful for
-                finding neighbors that the inputs have in common.
-                **'and'** : Only neighbors shared by all input pores. This
-                is also known as 'intersection' in set theory and
-                (somtimes) as 'all' in boolean logic. Both keywords are
-                accepted and treated as 'and'.
+
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'or'         All neighbors of the input throats. Also accepts 'any'
+                         and 'union'.
+            'xor'        Only neighbors of one and only one input throats. This
+                         is useful for counting the pores that are not shared
+                         by any of the input pores. Also accepts
+                         'exclusive_or'.
+            'xnor'       Neighbors that are shared by two or more input
+                         throats. This is equivalent to counting all neighbors
+                         with 'or', minus those found with 'xor', and is
+                         useful for finding neighbors that the inputs have
+                         in common.
+            'and'        Only neighbors shared by all input throats. Also
+                         accepts 'intersection' and 'all'
+            ===========  =====================================================
 
         asmask : boolean
             If ``False`` (default), the returned result is a list of the
@@ -716,22 +722,24 @@ class GenericNetwork(ParamMixin, Base, ModelsMixin, LabelMixin):
             ``True`` the sum total number of is counted.
         mode : str
             The logic to apply to the returned count of pores:
-                **'or'** : (default) All neighbors of the input pores.
-                This is also known as the 'union' in set theory or 'any'
-                in boolean logic. Both keywords are accepted and treated
-                as 'or'.
-                **'xor'** : Only neighbors of one and only one input pore.
-                This is useful for counting the pores that are not shared
-                by any of the input pores. This is known as 'exclusive_or'
-                in set theory, and is an accepted input.
-                **'xnor'** : Neighbors that are shared by two or more
-                input pores. This is equivalent to counting all neighbors
-                with 'or', minus those found with 'xor', and is useful for
-                finding neighbors that the inputs have in common.
-                **'and'** : Only neighbors shared by all input pores. This
-                is also known as 'intersection' in set theory and
-                (somtimes) as 'all' in boolean logic. Both keywords are
-                accepted and treated as 'and'.
+
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'or'         All neighbors of the input pores. Also accepts 'any'
+                         and 'union'.
+            'xor'        Only neighbors of one and only one input pore. This
+                         is useful for counting the pores that are not shared
+                         by any of the input pores. Also accepts
+                         'exclusive_or'.
+            'xnor'       Neighbors that are shared by two or more input pores.
+                         This is equivalent to counting all neighbors
+                         with 'or', minus those found with 'xor', and is
+                         useful for finding neighbors that the inputs have
+                         in common.
+            'and'        Only neighbors shared by all input pores. Also
+                         accepts 'intersection' and 'all'
+            ===========  =====================================================
 
         Returns
         -------
diff --git a/openpnm/phases/_multiphase.py b/openpnm/phases/_multiphase.py
index a00159b04..eebf77064 100644
--- a/openpnm/phases/_multiphase.py
+++ b/openpnm/phases/_multiphase.py
@@ -382,9 +382,18 @@ class MultiPhase(GenericPhase):
         Parameters
         ----------
         mode : str
-            Interpolation method, ex. 'mean' sets the throat occupancy as
-            the average of that in adjacent pores, while 'min' sets it to
-            the minimum of the two. Options are 'mean', 'min', and 'max'.
+            Interpolation method. Options are:
+
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'mean'       sets the throat occupancy as the average of that in
+                         adjacent pores.
+            'min'        sets the throat occupancy as the minimum value of
+                         that in adjacent pores.
+            'max'        sets the throat occupancy as the maximum value of
+                         that in adjacent pores.
+            ===========  =====================================================
 
         """
         self.settings['throat_occupancy'] = 'automatic'
diff --git a/openpnm/phases/mixtures/_generic_mixture.py b/openpnm/phases/mixtures/_generic_mixture.py
index 31bc71ee5..9682d684f 100644
--- a/openpnm/phases/mixtures/_generic_mixture.py
+++ b/openpnm/phases/mixtures/_generic_mixture.py
@@ -126,10 +126,15 @@ class GenericMixture(GenericPhase):
 
         Parameters
         ----------
-        mode : str {'all' (default), 'update'}
-            If 'all' then all concentrations will be updated based on the
-            current mole fractions.  If 'update', then only concentrations
-            that are missing or filled with *nans* will be updated.
+        mode : str
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'all'        all concentrations will be updated based on the
+                         current mole fractions. (default)
+            'update'     only concentrations that are missing or filled with
+                         *nans* will be updated.
+            ===========  =====================================================
 
         Notes
         -----
@@ -307,8 +312,14 @@ class GenericMixture(GenericPhase):
         component : GenericPhase
             The phase object of the component to add.  Can also be a list
             of phases to add more than one at a time.
-        mode : str {'add', 'remove'}
-            Indicates whether to add or remove the give component(s)
+        mode : str
+
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'add'        Adds the given components
+            'remove'     Removes the given components
+            ===========  =====================================================
 
         """
         if mode == 'add':
diff --git a/openpnm/physics/_generic.py b/openpnm/physics/_generic.py
index 8e7fae1fb..fa7337ffd 100644
--- a/openpnm/physics/_generic.py
+++ b/openpnm/physics/_generic.py
@@ -89,17 +89,18 @@ class GenericPhysics(ParamMixin, Subdomain, ModelsMixin):
         mode : str
             Options are:
 
-            * 'add' (default)
-                If the physics does not presently have an associated phase,
-                this will create associations, but no pore or throat
-                locations will assigned.  This must be done using the
-                ``set_geometry`` method.
-            * 'drop'
-                Associations with the existing phase will be removed.
-            * 'move'
-                Associations will be made with the new phase, and the pore
-                and throat locations from the current phase will be
-                transferred to the new one.
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'add'        If the physics does not presently have an associated
+                         phase, this will create associations, but no pore or
+                         throat locations will assigned. This must be done
+                         using the ``set_geometry`` method.
+            'drop'       Associations with the existing phase will be removed.
+            'move'       Associations will be made with the new phase, and the pore
+                         and throat locations from the current phase will be
+                         transferred to the new one.
+            ===========  =====================================================
 
         Notes
         -----
@@ -165,17 +166,17 @@ class GenericPhysics(ParamMixin, Subdomain, ModelsMixin):
         mode : str
             Controls how the assignment is done. Options are:
 
-            * 'add' (default)
-                If the physics does not presently have an associated
-                geometry, this will create associations, otherwise an
-                Exception is raised
-            * 'drop'
-                Associations with the current geometry will be removed
-            * 'move'
-                Associations will be made with the provided geometry, and
-                the pore and throat locations from the current geometry will
-                be transferred to the new one.
-
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'add'        If the physics does not presently have an associated
+                         geometry, this will create associations, otherwise an
+                         Exception is raised.(default)
+            'drop'       Associations with the current geometry will be removed
+            'move'       Associations will be made with the provided geometry,
+                         and the pore and throat locations from the current
+                         geometry will be transferred to the new one.
+            ===========  =====================================================
         See Also
         --------
         set_locations
diff --git a/openpnm/topotools/_perctools.py b/openpnm/topotools/_perctools.py
index 13deeb0c8..2bc69e36c 100644
--- a/openpnm/topotools/_perctools.py
+++ b/openpnm/topotools/_perctools.py
@@ -28,8 +28,14 @@ def ispercolating(am, inlets, outlets, mode='site'):
         An array of indices indicating which sites are part of the outlets
 
     mode : str
-        Indicates which type of percolation to apply, either `'site'` or
-        `'bond'`
+        Indicates which type of percolation to apply,. Options are:
+
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'site'       Applies site percolation
+            'bond'       Applies bond percolation
+            ===========  =====================================================
 
     """
     if am.format != 'coo':
diff --git a/openpnm/topotools/_topotools.py b/openpnm/topotools/_topotools.py
index 4fb876a86..e8d3b6d43 100644
--- a/openpnm/topotools/_topotools.py
+++ b/openpnm/topotools/_topotools.py
@@ -542,10 +542,14 @@ def clone_pores(network, pores, labels=['clone'], mode='parents'):
     mode : str
         Controls the connections between parents and clones.  Options are:
 
-        - 'parents': (Default) Each clone is connected only to its parent
-        - 'siblings': Clones are only connected to each other in the same
-                      manner as parents were connected
-        - 'isolated': No connections between parents or siblings
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'parents'    Each clone is connected only to its parent.(Default)
+            'siblings'   Clones are only connected to each other in the same
+                         manner as parents were connected.
+            'isolated'   No connections between parents or siblings
+            ===========  =====================================================
 
     """
     if isinstance(labels, str):
@@ -792,7 +796,14 @@ def stitch_pores(network, pores1, pores2, mode='gabriel'):
         The pore indices of the disconnected clusters to be joined
     mode : str
         Dictates which tesselation method is used to identify which pores to
-        stitch together.  Options are 'gabriel' (default) or 'delaunay'.
+        stitch together.  Options are:
+
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'gabriel'    Uses the gabriel tesselation method
+            'delaunay'   Uses the delaunay tesselation method
+            ===========  =====================================================
 
     Returns
     -------
diff --git a/openpnm/topotools/generators/_bcc.py b/openpnm/topotools/generators/_bcc.py
index de0368db3..d6282cb69 100644
--- a/openpnm/topotools/generators/_bcc.py
+++ b/openpnm/topotools/generators/_bcc.py
@@ -19,13 +19,13 @@ def bcc(shape, spacing=1, mode='kdtree'):
         applied in all 3 directions.
     mode : str
         Dictate how neighbors are found.  Options are:
-
-        'kdtree'
-            Uses ``scipy.spatial.KDTree`` to find all neighbors within the
-            unit cell.
-        'triangulation'
-            Uses ``scipy.spatial.Delaunay`` to find all neighbors.
-
+            ===============  =====================================================
+            mode             meaning
+            ===============  =====================================================
+            'kdtree'         Uses ``scipy.spatial.KDTree`` to find all neighbors
+                             within the unit cell.
+            'triangulation'  Uses ``scipy.spatial.Delaunay`` to find all neighbors.
+            ===============  =====================================================
     Returns
     -------
     network : dict
diff --git a/openpnm/topotools/generators/_fcc.py b/openpnm/topotools/generators/_fcc.py
index 4d1bf7c31..335069e3e 100644
--- a/openpnm/topotools/generators/_fcc.py
+++ b/openpnm/topotools/generators/_fcc.py
@@ -27,11 +27,13 @@ def fcc(shape, spacing=1, mode='kdtree'):
     mode : str
         Dictate how neighbors are found.  Options are:
 
-        'kdtree'
-            Uses ``scipy.spatial.KDTree`` to find all neighbors within the
-            unit cell.
-        'triangulation'
-            Uses ``scipy.spatial.Delaunay`` to find all neighbors.
+            ===============  =====================================================
+            mode             meaning
+            ===============  =====================================================
+            'kdtree'         Uses ``scipy.spatial.KDTree`` to find all neighbors
+                             within the unit cell.
+            'triangulation'  Uses ``scipy.spatial.Delaunay`` to find all neighbors.
+            ===============  =====================================================
 
     Returns
     -------
diff --git a/openpnm/topotools/generators/tools.py b/openpnm/topotools/generators/tools.py
index cd612a5ea..3ac8ea71d 100644
--- a/openpnm/topotools/generators/tools.py
+++ b/openpnm/topotools/generators/tools.py
@@ -239,11 +239,14 @@ def crop(network, shape, mode='full'):
     mode : str
         Controls how vertices to be trimmed is determined. Options are:
 
-            * 'full':
-                Any vertices lying outside the domain are trimmed
-            * 'mixed'
-                Vertices with at least one neighbor lying inside the domain
-                are kept
+            ===========  =====================================================
+            mode         meaning
+            ===========  =====================================================
+            'full'       Any vertices lying outside the domain are trimmed
+            'mixed'      Vertices with at least one neighbor lying inside
+                         the domain are kept
+            ===========  =====================================================
+
     """
     Pdrop = isoutside(network['vert.coords'], shape=shape, thresh=0)
     if mode == 'full':


Test patch:
diff --git a/tests/unit/algorithms/GenericTransportTest.py b/tests/unit/algorithms/GenericTransportTest.py
index 95c2c23d5..66bf320ef 100644
--- a/tests/unit/algorithms/GenericTransportTest.py
+++ b/tests/unit/algorithms/GenericTransportTest.py
@@ -54,12 +54,7 @@ class GenericTransportTest:
         alg.settings['quantity'] = 'pore.mole_fraction'
         alg.set_value_BC(pores=self.net.pores('top'), values=1)
         alg.set_value_BC(pores=self.net.pores('bottom'), values=0)
-        soln = alg.run()
-        # Check returned data type
-        from openpnm.algorithms._solution import SteadyStateSolution
-        assert isinstance(soln, SteadyStateSolution)
-        # Ensure solution object is attached to the algorithm
-        assert isinstance(alg.soln, SteadyStateSolution)
+        alg.run()
 
     def test_two_value_conditions(self):
         alg = op.algorithms.GenericTransport(network=self.net,
diff --git a/tests/unit/algorithms/ReactiveTransportTest.py b/tests/unit/algorithms/ReactiveTransportTest.py
index b1f6847d8..4646908c7 100644
--- a/tests/unit/algorithms/ReactiveTransportTest.py
+++ b/tests/unit/algorithms/ReactiveTransportTest.py
@@ -169,7 +169,7 @@ class ReactiveTransportTest:
         self.alg.settings._update({'relaxation_quantity': 20.0,
                                    'newton_maxiter': 25})
         self.alg.run()
-        assert not self.alg.soln.is_converged
+        assert not self.alg.is_converged
 
     # FIXME: we no longer want to throw exception when maxiter is reached
     def test_check_divergence_if_maxiter_reached(self):
diff --git a/tests/unit/algorithms/TransientReactiveTransportTest.py b/tests/unit/algorithms/TransientReactiveTransportTest.py
index 9c2dba520..ec3258e61 100644
--- a/tests/unit/algorithms/TransientReactiveTransportTest.py
+++ b/tests/unit/algorithms/TransientReactiveTransportTest.py
@@ -46,10 +46,9 @@ class TransientReactiveTransportTest:
     def test_transient_solution(self):
         out = self.alg.run(x0=0, tspan=(0, 1), saveat=0.1)
         # Test datatype
-        from openpnm.algorithms._solution import TransientSolution
-        assert isinstance(out, TransientSolution)
+        assert isinstance(out, op.integrators.TransientSolution)
         # Ensure solution object is attached to the algorithm
-        assert isinstance(self.alg.soln, TransientSolution)
+        assert isinstance(self.alg.soln, op.integrators.TransientSolution)
         # Test shape
         nt.assert_array_equal(self.alg.soln.shape, (self.alg.Np, 11))
         # Test stored time points