Repository: PMEAL/OpenPNM
Pull request #417 (2015-07-10 08:44:59)

Extra unit tests
I noticed that I have some unit tests in one of my private branches. I don't think they will change the overall coverage significantly, but at least they test different modes of solver/source_term thoroughly.


Patch:
diff --git a/OpenPNM/Algorithms/__GenericAlgorithm__.py b/OpenPNM/Algorithms/__GenericAlgorithm__.py
index b30f7eeb9..6589579dd 100644
--- a/OpenPNM/Algorithms/__GenericAlgorithm__.py
+++ b/OpenPNM/Algorithms/__GenericAlgorithm__.py
@@ -109,7 +109,8 @@ class GenericAlgorithm(Core):
 
             - 'merge': Inserts the specified values, leaving existing values \
               elsewhere
-            - 'overwrite': Inserts specified values, clearing all other values
+            - 'overwrite': Inserts specified values, clearing all other \
+              values for that specific bctype
             - 'remove': Removes boundary conditions from specified locations
 
         Notes
@@ -140,65 +141,52 @@ class GenericAlgorithm(Core):
             raise Exception('The mode (' + mode + ') cannot be applied to ' +
                             'the set_boundary_conditions!')
 
-        logger.debug('BC applies to the component: ' + component.name)
+        logger.debug('BC method applies to the component: ' + component.name)
+        # Validate bctype
+        if bctype == '':
+            raise Exception('bctype must be specified!')
         # If mode is 'remove', also bypass checks
         if mode == 'remove':
             if pores is None and throats is None:
-                if bctype == '':
-                    raise Exception('No bctype/pore/throat is specified')
-                else:
-                    for item in self.labels():
-                        item_spl = item.split('.')
-                        if bctype == (item_spl[-1]).replace(self._phase.name +
-                                                            '_', ''):
-                            element = item_spl[0]
-                            try:
-                                del self[element + '.' + component.name +
-                                         '_bcval_' + bctype]
-                            except KeyError:
-                                pass
-                            try:
-                                del self[element + '.' + component.name +
-                                         '_' + bctype]
-                            except KeyError:
-                                pass
-                    logger.debug('Removing ' + bctype + ' from all locations' +
-                                 ' for ' + component.name + ' in ' +
-                                 self.name)
-                    self._existing_BC.remove(bctype)
+                for item in self.labels():
+                    item_spl = item.split('.')
+                    if bctype == (item_spl[-1]).replace(self._phase.name +
+                                                        '_', ''):
+                        element = item_spl[0]
+                        try:
+                            del self[element + '.' + component.name +
+                                     '_bcval_' + bctype]
+                        except KeyError:
+                            pass
+                        try:
+                            del self[element + '.' + component.name +
+                                     '_' + bctype]
+                        except KeyError:
+                            pass
+                logger.debug('Removing ' + bctype + ' from all locations' +
+                             ' for ' + component.name + ' in ' +
+                             self.name)
+                self._existing_BC.remove(bctype)
             else:
                 if pores is not None:
-                    if bctype != '':
-                        prop_label = 'pore.' + component.name + '_bcval_'\
-                                     + bctype
-                        self[prop_label][pores] = sp.nan
-                        info_label = 'pore.' + component.name + '_' + bctype
-                        self[info_label][pores] = False
-                        logger.debug('Removing ' + bctype + ' from the ' +
-                                     'specified pores for ' + component.name +
-                                     ' in ' + self.name)
-                    else:
-                        raise Exception('Cannot remove BC from the pores ' +
-                                        'unless bctype is specified')
-
+                    prop_label = 'pore.' + component.name + '_bcval_'\
+                                 + bctype
+                    self[prop_label][pores] = sp.nan
+                    info_label = 'pore.' + component.name + '_' + bctype
+                    self[info_label][pores] = False
+                    logger.debug('Removing ' + bctype + ' from the ' +
+                                 'specified pores for ' + component.name +
+                                 ' in ' + self.name)
                 if throats is not None:
-                    if bctype != '':
-                        prop_label = 'throat.' + component.name + '_bcval_'\
-                                     + bctype
-                        self[prop_label][throats] = sp.nan
-                        info_label = 'throat.' + component.name + '_' + bctype
-                        self[info_label][throats] = False
-                        logger.debug('Removing ' + bctype + ' from the ' +
-                                     'specified throats for ' +
-                                     component.name + ' in ' + self.name)
-                    else:
-                        raise Exception('Cannot remove BC from the throats ' +
-                                        'unless bctype is specified')
-
+                    prop_label = 'throat.' + component.name + '_bcval_'\
+                                 + bctype
+                    self[prop_label][throats] = sp.nan
+                    info_label = 'throat.' + component.name + '_' + bctype
+                    self[info_label][throats] = False
+                    logger.debug('Removing ' + bctype + ' from the ' +
+                                 'specified throats for ' +
+                                 component.name + ' in ' + self.name)
             return
-        # Validate bctype
-        if bctype == '':
-            raise Exception('bctype must be specified')
         # Validate pores/throats
         if pores is None and throats is None:
             raise Exception('pores/throats must be specified')
@@ -246,7 +234,7 @@ class GenericAlgorithm(Core):
         for item in self.labels():
             bcname = (item.split('.')[-1]).replace(component.name + '_', "")
             if bcname in self._existing_BC and item.split('.')[0] == element:
-                if mode == 'merge':
+                if mode in ['merge', 'overwrite']:
                     try:
                         c1 = element + '.' + component.name
                         c2 = '_bcval_' + bcname
@@ -256,10 +244,19 @@ class GenericAlgorithm(Core):
                         c2_label = c1 + '_' + bcname
                         condition2 = sp.sum(self[c2_label][loc]) == 0
                         if not (condition1 and condition2):
-                            raise Exception('Because of the existing BCs, ' +
-                                            'the method cannot apply new BC ' +
-                                            'with the merge mode to the ' +
-                                            'specified pore/throat.')
+                            if mode == 'merge':
+                                raise Exception('Because of the existing ' +
+                                                'BCs, the method cannot ' +
+                                                'apply new BC with the merge' +
+                                                ' mode to the specified pore' +
+                                                '/throat.')
+                            elif (mode == 'overwrite' and bcname != bctype):
+                                raise Exception('Because of the existing ' +
+                                                'BCs, the method cannot ' +
+                                                'apply new BC with overwrite' +
+                                                ' mode. This mode only ' +
+                                                'overwrites this bctype, ' +
+                                                'not the other ones.')
                     except KeyError:
                         pass
         # Set boundary conditions based on supplied mode
diff --git a/OpenPNM/Algorithms/__GenericLinearTransport__.py b/OpenPNM/Algorithms/__GenericLinearTransport__.py
index 1152adad6..97166c3ad 100644
--- a/OpenPNM/Algorithms/__GenericLinearTransport__.py
+++ b/OpenPNM/Algorithms/__GenericLinearTransport__.py
@@ -160,83 +160,39 @@ class GenericLinearTransport(GenericAlgorithm):
 
                 if mode == 'remove':
                     s_mode = ['linear', 'nonlinear']
-                    if source_name is None:
-                        if pores is not None:
-                            if pores is 'all':
-                                for item in self.labels():
-                                    if 'pore.source_' in item:
-                                        temp_item = item.split('.')[-1]
-                                        prop = temp_item.replace('source_', '')
-                                        del self['pore.source_' +
-                                                 prop]
-                                        for s in s_mode:
-                                            try:
-                                                del self['pore.source_' +
-                                                         s + '_s1_' + prop]
-                                            except KeyError:
-                                                pass
-                                            try:
-                                                del self['pore.source_' +
-                                                         s + '_s2_' + prop]
-                                            except KeyError:
-                                                pass
-                            else:
-                                for item in self.labels():
-                                    if 'pore.source_' in item:
-                                        temp_item = item.split('.')[-1]
-                                        prop = temp_item.replace('source_', '')
-                                        self['pore.source_' +
-                                             prop][pores] = False
-                                        for s in s_mode:
-                                            try:
-                                                self['pore.source_' +
-                                                     s + '_s1_' +
-                                                     prop][pores] = sp.nan
-                                            except KeyError:
-                                                pass
-                                            try:
-                                                self['pore.source_' +
-                                                     s + '_s2_' +
-                                                     prop][pores] = sp.nan
-                                            except KeyError:
-                                                pass
-                        else:
-                            raise Exception('No pores/source_name are ' +
-                                            'sent to the set_term_method!')
+                    if pores is None:
+                        try:
+                            del self['pore.source_' + prop]
+                        except KeyError:
+                            pass
+                        for s in s_mode:
+                            try:
+                                del self['pore.source_' +
+                                         s + '_s1_' + prop]
+                            except KeyError:
+                                pass
+                            try:
+                                del self['pore.source_' +
+                                         s + '_s2_' + prop]
+                            except KeyError:
+                                pass
                     else:
-                        if pores is None:
+                        try:
+                            self['pore.source_' + prop][pores] = False
+                        except KeyError:
+                            pass
+                        for s in s_mode:
                             try:
-                                del self['pore.source_' + prop]
+                                self['pore.source_' +
+                                     s + '_s1_' +
+                                     prop][pores] = sp.nan
                             except KeyError:
                                 pass
-                            for s in s_mode:
-                                try:
-                                    del self['pore.source_' +
-                                             s + '_s1_' + prop]
-                                except KeyError:
-                                    pass
-                                try:
-                                    del self['pore.source_' +
-                                             s + '_s2_' + prop]
-                                except KeyError:
-                                    pass
-                        else:
                             try:
-                                self['pore.source_' + prop][pores] = False
+                                self['pore.source_' +
+                                     s + '_s2_' + prop][pores] = sp.nan
                             except KeyError:
                                 pass
-                            for s in s_mode:
-                                try:
-                                    self['pore.source_' +
-                                         s + '_s1_' +
-                                         prop][pores] = sp.nan
-                                except KeyError:
-                                    pass
-                                try:
-                                    self['pore.source_' +
-                                         s + '_s2_' + prop][pores] = sp.nan
-                                except KeyError:
-                                    pass
 
                 else:
                     # Handle tol, x0 and maxiter for the Picard algorithm
@@ -321,8 +277,8 @@ class GenericLinearTransport(GenericAlgorithm):
                             self['pore.source_maxiter'][loc] = maxiter
                             self['pore.source_tol'][loc] = tol
         else:
-            Exception('No source_name has been sent for set_source_term ' +
-                      'method in the algorithm ' + self.name)
+            raise Exception('No source_name has been sent for set_source_' +
+                            'term method in the algorithm ' + self.name)
 
     def run(self, **kwargs):
         r"""
@@ -389,6 +345,9 @@ class GenericLinearTransport(GenericAlgorithm):
         if self._iterative_solver is None:
             X = sprslin.spsolve(A, b)
         else:
+            if self._iterative_solver not in ['cg', 'gmres']:
+                raise Exception('GenericLinearTransport does not support the' +
+                                ' requested iterative solver!')
             params = kwargs.copy()
             solver_params = ['x0', 'tol', 'maxiter', 'xtype', 'M', 'callback']
             [params.pop(item, None) for item in kwargs.keys()
@@ -401,8 +360,6 @@ class GenericLinearTransport(GenericAlgorithm):
                 result = sprslin.cg(A, b, **params)
             elif self._iterative_solver == 'gmres':
                 result = sprslin.gmres(A, b, **params)
-            elif self._iterative_solver == 'bicgstab':
-                result = sprslin.bicgstab(A, b, **params)
             X = result[0]
             self._iterative_solver_info = result[1]
         return X
diff --git a/OpenPNM/Physics/models/generic_source_term.py b/OpenPNM/Physics/models/generic_source_term.py
index cabe3d3ac..675610e20 100644
--- a/OpenPNM/Physics/models/generic_source_term.py
+++ b/OpenPNM/Physics/models/generic_source_term.py
@@ -79,7 +79,7 @@ def linear(physics, phase, A1='', A2='', x='', return_rate=True, **kwargs):
                                 'type!')
 
     if return_rate:
-        return(a['1'] * X ** a['2'])
+        return(a['1'] * X + a['2'])
     else:
         S1 = a['1']
         S2 = a['2']


Test patch:
diff --git a/test/integration/script_test.py b/test/integration/script_test.py
index b0e2075dd..bee4993bc 100644
--- a/test/integration/script_test.py
+++ b/test/integration/script_test.py
@@ -25,7 +25,7 @@ def test_linear_solvers():
     alg_1.set_boundary_conditions(bctype='Dirichlet',
                                   bcvalue=0,
                                   pores=BC2_pores)
-    alg_1.run()
+    alg_1.run(iterative_solver='gmres')
 
     alg_2 = OpenPNM.Algorithms.FickianDiffusion(network=pn, phase=air)
     alg_2.set_boundary_conditions(bctype='Neumann',
@@ -34,7 +34,7 @@ def test_linear_solvers():
     alg_2.set_boundary_conditions(bctype='Dirichlet',
                                   bcvalue=0,
                                   pores=BC2_pores)
-    alg_2.run()
+    alg_2.run(iterative_solver='cg')
 
     alg_3 = OpenPNM.Algorithms.FickianDiffusion(network=pn, phase=air)
     alg_3.set_boundary_conditions(bctype='Neumann_group',
@@ -55,10 +55,6 @@ def test_linear_solvers():
     alg_4.setup()
     alg_4.solve()
 
-    print(alg_1['pore.'+air.name+'_mole_fraction'][BC1_pores])
-    print(alg_2['pore.'+air.name+'_mole_fraction'][BC1_pores])
-    print(alg_3['pore.'+air.name+'_mole_fraction'][BC1_pores])
-
     assert round(sp.absolute(alg_1.rate(BC1_pores))[0], 16) == round(sp.absolute(alg_1.rate(BC2_pores))[0], 16)
     assert round(sp.absolute(alg_2.rate(BC2_pores))[0], 16) == round(sp.absolute(sp.unique(alg_2['pore.'+air.name+'_bcval_Neumann']))[0]*len(BC1_pores), 16)
     assert round(sp.absolute(alg_3.rate(BC2_pores))[0], 16) == round(sp.absolute(sp.unique(alg_3['pore.'+air.name+'_bcval_Neumann_group']))[0], 16)
diff --git a/test/unit/Algorithms/GenericAlgorithmTest.py b/test/unit/Algorithms/GenericAlgorithmTest.py
index 334ca0c21..d277203b5 100644
--- a/test/unit/Algorithms/GenericAlgorithmTest.py
+++ b/test/unit/Algorithms/GenericAlgorithmTest.py
@@ -1,3 +1,55 @@
+import OpenPNM
+import numpy as np
+
+
 class GenericAlgorithmTest:
-    def test_set_boundary_conditions(self):
-        pass
+    def setup_class(self):
+        self.net = OpenPNM.Network.Cubic(shape=[5, 5, 5])
+        self.phase = OpenPNM.Phases.GenericPhase(network=self.net)
+        self.alg = OpenPNM.Algorithms.GenericLinearTransport(network=self.net,
+                                                             phase=self.phase)
+
+    def test_set_BC_modes(self):
+        BC1_pores = np.arange(25, 35)
+        BC1_pores = np.arange(25, 35)
+        self.alg.set_boundary_conditions(component=self.phase,
+                                         bctype='Dirichlet',
+                                         bcvalue=0.8,
+                                         pores=BC1_pores)
+        ptest = self.alg.pores('pore.' + self.phase.name + '_Dirichlet')
+        assert np.all(ptest == BC1_pores)
+        BC2_pores = np.arange(43, 50)
+        self.alg.set_boundary_conditions(component=self.phase,
+                                         bctype='Dirichlet',
+                                         bcvalue=0.8,
+                                         pores=BC2_pores,
+                                         mode='merge')
+        ptest = self.alg.pores('pore.' + self.phase.name + '_Dirichlet')
+        assert np.all(ptest == np.concatenate((BC1_pores, BC2_pores)))
+        BC3_pores = np.arange(4, 9)
+        self.alg.set_boundary_conditions(bctype='Dirichlet',
+                                         bcvalue=0.8,
+                                         pores=BC3_pores,
+                                         mode='overwrite')
+        ptest = self.alg.pores('pore.' + self.phase.name + '_Dirichlet')
+        assert np.all(ptest == BC3_pores)
+        BC4_pores = [11, 90]
+        self.alg.set_boundary_conditions(bctype='Neumann',
+                                         bcvalue=0.5,
+                                         pores=BC4_pores,
+                                         mode='overwrite')
+        ptest = self.alg.pores('pore.' + self.phase.name + '_Neumann')
+        assert np.all(ptest == BC4_pores)
+        self.alg.set_boundary_conditions(bctype='Dirichlet',
+                                         pores=BC1_pores,
+                                         bcvalue=0.3)
+        ptest = self.alg.pores('pore.' + self.phase.name + '_Dirichlet')
+        self.alg.set_boundary_conditions(bctype='Dirichlet',
+                                         pores=self.alg.Ps,
+                                         mode='remove')
+        Dp = np.sum(self.alg['pore.' + self.phase.name + '_Dirichlet'])
+        assert Dp == 0
+        self.alg.set_boundary_conditions(bctype='Neumann',
+                                         mode='remove')
+        label = 'pore.' + self.phase.name + '_Neumann'
+        assert (label not in self.alg.labels())
diff --git a/test/unit/Algorithms/GenericLinearTransportTest.py b/test/unit/Algorithms/GenericLinearTransportTest.py
index 7f8576104..c35e180ef 100644
--- a/test/unit/Algorithms/GenericLinearTransportTest.py
+++ b/test/unit/Algorithms/GenericLinearTransportTest.py
@@ -1,3 +1,102 @@
+import OpenPNM
+import numpy as np
+import OpenPNM.Physics.models as pm
+
+
 class GenericLinearTransportTest:
-    def test_setup(self):
-        pass
+    def setup_class(self):
+        self.net = OpenPNM.Network.Cubic(shape=[5, 5, 5])
+        self.phase = OpenPNM.Phases.GenericPhase(network=self.net)
+        Ps = self.net.Ps
+        Ts = self.net.Ts
+        self.phys = OpenPNM.Physics.GenericPhysics(network=self.net,
+                                                   phase=self.phase,
+                                                   pores=Ps, throats=Ts)
+        self.phys['throat.cond'] = 5e-8
+        self.alg = OpenPNM.Algorithms.GenericLinearTransport(network=self.net,
+                                                             phase=self.phase)
+
+    def test_super_pore_conductance(self):
+        g_super = []
+        BC1_pores = np.arange(20, 30)
+        self.alg.set_boundary_conditions(bctype='Dirichlet',
+                                         bcvalue=0.4,
+                                         pores=BC1_pores)
+        BC2_pores = np.arange(45, 66)
+        self.alg.set_boundary_conditions(bctype='Neumann_group',
+                                         bcvalue=1.4e-10,
+                                         pores=BC2_pores)
+        g_super.append(2e-12)
+        BC3_pores = np.arange(87, 94)
+        self.alg.set_boundary_conditions(bctype='Neumann_group',
+                                         bcvalue=-0.9e-10,
+                                         pores=BC3_pores)
+        g_super.append(np.ones(len(BC3_pores)) * 1.5e-12)
+        BC4_pores = np.arange(3, 7)
+        self.alg.set_boundary_conditions(bctype='Neumann_group',
+                                         bcvalue=0.1e-10,
+                                         pores=BC4_pores)
+        g_super.append(np.array([6.42e-13]))
+        self.alg.run(conductance='throat.cond',
+                     quantity='pore.mole_fraction',
+                     super_pore_conductance=g_super)
+        self.alg.return_results()
+        r1 = self.alg.rate(BC1_pores)[0]
+        r2 = self.alg.rate(BC2_pores)[0]
+        r3 = self.alg.rate(BC3_pores)[0]
+        r4 = self.alg.rate(BC4_pores)[0]
+        assert np.absolute(r1 + r2 + r3 + r4) < 1e-20
+        assert np.size(self.alg.super_pore_conductance[0]) == 1
+        assert np.size(self.alg.super_pore_conductance[1]) == 7
+        assert np.size(self.alg.super_pore_conductance[2]) == 1
+
+    def test_source_term_modes(self):
+        self.phys['pore.item1'] = 0.5e-12
+        self.phys['pore.item2'] = 2.5
+        self.phys['pore.item3'] = -1.4e-11
+        self.phys.models.add(propname='pore.A',
+                             model=pm.generic_source_term.power_law,
+                             A1='pore.item1',
+                             A2='pore.item2',
+                             A3='pore.item3',
+                             x='mole_fraction',
+                             return_rate=False,
+                             regen_mode='on_demand')
+        self.phys.models.add(propname='pore.B',
+                             model=pm.generic_source_term.linear,
+                             A1='pore.item1',
+                             A2='pore.item3',
+                             x='mole_fraction',
+                             return_rate=False,
+                             regen_mode='on_demand')
+        S1_pores = np.arange(25, 35)
+        self.alg.set_source_term(source_name=['pore.A', 'pore.B'],
+                                 pores=S1_pores)
+        mask1 = ~np.isnan(self.alg['pore.source_nonlinear_s1_A'])
+        mask2 = ~np.isnan(self.alg['pore.source_nonlinear_s2_A'])
+        assert np.all(self.alg.Ps[mask1] == S1_pores)
+        assert np.all(self.alg.Ps[mask2] == S1_pores)
+        self.alg.set_source_term(source_name='pore.A',
+                                 pores=[26], x0=np.ones(self.phys.Np),
+                                 mode='update')
+        assert self.alg['pore.source_nonlinear_s1_A'][26] == 1.25e-12
+        S2_pores = np.array([30, 31])
+        self.alg.set_source_term(source_name='pore.A',
+                                 pores=S2_pores,
+                                 mode='overwrite')
+        mask1 = ~np.isnan(self.alg['pore.source_nonlinear_s1_A'])
+        assert np.all(self.alg.Ps[mask1] == S2_pores)
+        self.alg.set_source_term(source_name='pore.B',
+                                 pores=S1_pores,
+                                 mode='remove')
+        mask1 = np.isnan(self.alg['pore.source_nonlinear_s1_B'])
+        assert np.all(self.alg.Ps[mask1] == self.alg.Ps)
+        self.alg.set_source_term(source_name=['pore.A', 'pore.B'],
+                                 pores=self.alg.Ps,
+                                 mode='remove')
+        assert ('pore.source_B' in self.alg.labels())
+        assert ('pore.source_A' in self.alg.labels())
+        self.alg.set_source_term(source_name=['pore.A', 'pore.B'],
+                                 mode='remove')
+        assert ('pore.source_B' not in self.alg.labels())
+        assert ('pore.source_A' not in self.alg.labels())
diff --git a/test/unit/Physics/models/GenericSourceTerm.py b/test/unit/Physics/models/GenericSourceTerm.py
index c195b4a36..2fdbcaed7 100644
--- a/test/unit/Physics/models/GenericSourceTerm.py
+++ b/test/unit/Physics/models/GenericSourceTerm.py
@@ -1,3 +1,270 @@
+import OpenPNM
+import numpy as np
+import OpenPNM.Physics.models as pm
+
+
 class GenericSourceTermTest:
+    def setup_class(self):
+        self.net = OpenPNM.Network.Cubic(shape=[5, 5, 5])
+        self.phase = OpenPNM.Phases.GenericPhase(network=self.net)
+        Ps = self.net.Ps
+        Ts = self.net.Ts
+        self.phys = OpenPNM.Physics.GenericPhysics(network=self.net,
+                                                   phase=self.phase,
+                                                   pores=Ps, throats=Ts)
+        self.phys['throat.diffusive_conductance'] = 5e-8
+        self.phase['pore.mole_fraction'] = 0.
+        self.alg = OpenPNM.Algorithms.GenericLinearTransport(network=self.net,
+                                                             phase=self.phase)
+        BC_pores = np.arange(20, 30)
+        self.S_pores = np.arange(55, 85)
+        self.alg.set_boundary_conditions(bctype='Dirichlet',
+                                         bcvalue=0.4,
+                                         pores=BC_pores)
+
     def test_linear(self):
-        pass
+        self.phys['pore.item1'] = 0.5e-11
+        self.phys['pore.item2'] = 1.5e-12
+        self.phys.models.add(propname='pore.source1',
+                             model=pm.generic_source_term.linear,
+                             A1='pore.item1',
+                             A2='pore.item2',
+                             x='mole_fraction',
+                             return_rate=False,
+                             regen_mode='on_demand')
+        self.phys.models.add(propname='pore.source2',
+                             model=pm.generic_source_term.linear,
+                             A1='pore.item1',
+                             A2='pore.item2',
+                             x='mole_fraction',
+                             return_rate=True,
+                             regen_mode='on_demand')
+        self.alg.set_source_term(source_name='pore.source1',
+                                 pores=self.S_pores,
+                                 mode='overwrite')
+        self.alg.run(conductance='throat.diffusive_conductance',
+                     quantity='pore.mole_fraction',
+                     super_pore_conductance=None)
+        self.alg.return_results()
+        self.phys.regenerate(props='pore.source1')
+        self.phys.regenerate(props='pore.source2')
+        X = self.phase['pore.mole_fraction']
+        r1 = np.round(np.sum(0.5e-11 * X[self.S_pores] + 1.5e-12), 20)
+        r2 = np.round(np.sum(self.phys['pore.source2'][self.S_pores]), 20)
+        r3 = np.round(self.alg.rate(pores=self.S_pores)[0], 20)
+        assert r1 == r2
+        assert r2 == -r3
+
+    def test_power_law(self):
+        self.phys['pore.item1'] = 0.5e-12
+        self.phys['pore.item2'] = 2.5
+        self.phys['pore.item3'] = -1.4e-11
+        self.phys.models.add(propname='pore.source1',
+                             model=pm.generic_source_term.power_law,
+                             A1='pore.item1',
+                             A2='pore.item2',
+                             A3='pore.item3',
+                             x='mole_fraction',
+                             return_rate=False,
+                             regen_mode='on_demand')
+        self.phys.models.add(propname='pore.source2',
+                             model=pm.generic_source_term.power_law,
+                             A1='pore.item1',
+                             A2='pore.item2',
+                             A3='pore.item3',
+                             x='mole_fraction',
+                             return_rate=True,
+                             regen_mode='on_demand')
+        self.alg.set_source_term(source_name='pore.source1',
+                                 pores=self.S_pores,
+                                 mode='overwrite')
+        self.alg.run(conductance='throat.diffusive_conductance',
+                     quantity='pore.mole_fraction',
+                     super_pore_conductance=None)
+        self.alg.return_results()
+        self.phys.regenerate(props='pore.source1')
+        self.phys.regenerate(props='pore.source2')
+        X = self.phase['pore.mole_fraction']
+        r1 = np.round(np.sum(0.5e-12 * X[self.S_pores] ** 2.5 - 1.4e-11), 20)
+        r2 = np.round(np.sum(self.phys['pore.source2'][self.S_pores]), 20)
+        r3 = np.round(self.alg.rate(pores=self.S_pores)[0], 20)
+        assert r1 == r2
+        assert r2 == -r3
+
+    def test_exponential(self):
+        self.phys['pore.item1'] = 0.8e-11
+        self.phys['pore.item2'] = 3
+        self.phys['pore.item3'] = 0.5
+        self.phys['pore.item4'] = 2
+        self.phys['pore.item5'] = -0.34
+        self.phys['pore.item6'] = 2e-14
+        self.phys.models.add(propname='pore.source1',
+                             model=pm.generic_source_term.exponential,
+                             A1='pore.item1',
+                             A2='pore.item2',
+                             A3='pore.item3',
+                             A4='pore.item4',
+                             A5='pore.item5',
+                             A6='pore.item6',
+                             x='mole_fraction',
+                             return_rate=False,
+                             regen_mode='on_demand')
+        self.phys.models.add(propname='pore.source2',
+                             model=pm.generic_source_term.exponential,
+                             A1='pore.item1',
+                             A2='pore.item2',
+                             A3='pore.item3',
+                             A4='pore.item4',
+                             A5='pore.item5',
+                             A6='pore.item6',
+                             x='mole_fraction',
+                             return_rate=True,
+                             regen_mode='on_demand')
+        self.alg.set_source_term(source_name='pore.source1',
+                                 pores=self.S_pores,
+                                 mode='overwrite')
+        self.alg.run(conductance='throat.diffusive_conductance',
+                     quantity='pore.mole_fraction',
+                     super_pore_conductance=None)
+        self.alg.return_results()
+        self.phys.regenerate(props='pore.source1')
+        self.phys.regenerate(props='pore.source2')
+        X = self.phase['pore.mole_fraction']
+        r1 = np.round(np.sum(0.8e-11 * 3 ** (0.5 * X[self.S_pores] ** 2 -
+                      0.34) + 2e-14), 20)
+        r2 = np.round(np.sum(self.phys['pore.source2'][self.S_pores]), 20)
+        r3 = np.round(self.alg.rate(pores=self.S_pores)[0], 20)
+        assert r1 == r2
+        assert r2 == -r3
+
+    def test_natural_exponential(self):
+        self.phys['pore.item1'] = 0.8e-11
+        self.phys['pore.item2'] = 0.5
+        self.phys['pore.item3'] = 2
+        self.phys['pore.item4'] = -0.34
+        self.phys['pore.item5'] = 2e-14
+        self.phys.models.add(propname='pore.source1',
+                             model=pm.generic_source_term.natural_exponential,
+                             A1='pore.item1',
+                             A2='pore.item2',
+                             A3='pore.item3',
+                             A4='pore.item4',
+                             A5='pore.item5',
+                             x='mole_fraction',
+                             return_rate=False,
+                             regen_mode='on_demand')
+        self.phys.models.add(propname='pore.source2',
+                             model=pm.generic_source_term.natural_exponential,
+                             A1='pore.item1',
+                             A2='pore.item2',
+                             A3='pore.item3',
+                             A4='pore.item4',
+                             A5='pore.item5',
+                             x='mole_fraction',
+                             return_rate=True,
+                             regen_mode='on_demand')
+        self.alg.set_source_term(source_name='pore.source1',
+                                 pores=self.S_pores,
+                                 mode='overwrite')
+        self.alg.run(conductance='throat.diffusive_conductance',
+                     quantity='pore.mole_fraction',
+                     super_pore_conductance=None)
+        self.alg.return_results()
+        self.phys.regenerate(props='pore.source1')
+        self.phys.regenerate(props='pore.source2')
+        X = self.phase['pore.mole_fraction']
+        r1 = np.round(np.sum(0.8e-11 * np.exp(0.5 * X[self.S_pores] ** 2 -
+                      0.34) + 2e-14), 20)
+        r2 = np.round(np.sum(self.phys['pore.source2'][self.S_pores]), 20)
+        r3 = np.round(self.alg.rate(pores=self.S_pores)[0], 20)
+        assert r1 == r2
+        assert r2 == -r3
+
+    def test_logarithm(self):
+        self.phys['pore.item1'] = 0.16e-13
+        self.phys['pore.item2'] = 10
+        self.phys['pore.item3'] = 4
+        self.phys['pore.item4'] = 1.4
+        self.phys['pore.item5'] = 0.133
+        self.phys['pore.item6'] = -5.1e-13
+        self.phys.models.add(propname='pore.source1',
+                             model=pm.generic_source_term.logarithm,
+                             A1='pore.item1',
+                             A2='pore.item2',
+                             A3='pore.item3',
+                             A4='pore.item4',
+                             A5='pore.item5',
+                             A6='pore.item6',
+                             x='mole_fraction',
+                             return_rate=False,
+                             regen_mode='on_demand')
+        self.phys.models.add(propname='pore.source2',
+                             model=pm.generic_source_term.logarithm,
+                             A1='pore.item1',
+                             A2='pore.item2',
+                             A3='pore.item3',
+                             A4='pore.item4',
+                             A5='pore.item5',
+                             A6='pore.item6',
+                             x='mole_fraction',
+                             return_rate=True,
+                             regen_mode='on_demand')
+        self.alg.set_source_term(source_name='pore.source1',
+                                 pores=self.S_pores,
+                                 mode='overwrite')
+        self.alg.run(conductance='throat.diffusive_conductance',
+                     quantity='pore.mole_fraction',
+                     super_pore_conductance=None)
+        self.alg.return_results()
+        self.phys.regenerate(props='pore.source1')
+        self.phys.regenerate(props='pore.source2')
+        X = self.phase['pore.mole_fraction']
+        r1 = np.round(np.sum(0.16e-13 * np.log(4 * X[self.S_pores] ** (1.4) +
+                             0.133) / np.log(10) - 5.1e-13), 20)
+        r2 = np.round(np.sum(self.phys['pore.source2'][self.S_pores]), 20)
+        r3 = np.round(self.alg.rate(pores=self.S_pores)[0], 20)
+        assert r1 == r2
+        assert r2 == -r3
+
+    def test_natural_logarithm(self):
+        self.phys['pore.item1'] = 0.16e-14
+        self.phys['pore.item2'] = 4
+        self.phys['pore.item3'] = 1.4
+        self.phys['pore.item4'] = 0.133
+        self.phys['pore.item5'] = -5.1e-14
+        self.phys.models.add(propname='pore.source1',
+                             model=pm.generic_source_term.natural_logarithm,
+                             A1='pore.item1',
+                             A2='pore.item2',
+                             A3='pore.item3',
+                             A4='pore.item4',
+                             A5='pore.item5',
+                             x='mole_fraction',
+                             return_rate=False,
+                             regen_mode='on_demand')
+        self.phys.models.add(propname='pore.source2',
+                             model=pm.generic_source_term.natural_logarithm,
+                             A1='pore.item1',
+                             A2='pore.item2',
+                             A3='pore.item3',
+                             A4='pore.item4',
+                             A5='pore.item5',
+                             x='mole_fraction',
+                             return_rate=True,
+                             regen_mode='on_demand')
+        self.alg.set_source_term(source_name='pore.source1',
+                                 pores=self.S_pores,
+                                 mode='overwrite')
+        self.alg.run(conductance='throat.diffusive_conductance',
+                     quantity='pore.mole_fraction',
+                     super_pore_conductance=None)
+        self.alg.return_results()
+        self.phys.regenerate(props='pore.source1')
+        self.phys.regenerate(props='pore.source2')
+        X = self.phase['pore.mole_fraction']
+        r1 = np.round(np.sum(0.16e-14 * np.log(4 * X[self.S_pores] ** (1.4) +
+                             0.133) - 5.1e-14), 20)
+        r2 = np.round(np.sum(self.phys['pore.source2'][self.S_pores]), 20)
+        r3 = np.round(self.alg.rate(pores=self.S_pores)[0], 20)
+        assert r1 == r2
+        assert r2 == -r3