Repository: PMEAL/OpenPNM
Pull request #1261 (2019-04-27 04:36:04)

Enhance generic physics
Adding a ``set_phase`` and ``set_geometry`` to the GenericPhysics class, and allowing empty geometry and phase arguments upon instantiation.


Patch:
diff --git a/openpnm/physics/GenericPhysics.py b/openpnm/physics/GenericPhysics.py
index a004ee3ce..ab25880cb 100644
--- a/openpnm/physics/GenericPhysics.py
+++ b/openpnm/physics/GenericPhysics.py
@@ -53,12 +53,37 @@ class GenericPhysics(Subdomain, ModelsMixin):
                 logger.warning('No Phase provided, ' + self.name +
                                ' will not be associated with a phase')
             else:
-                phase['pore.'+self.name] = False
-                phase['throat.'+self.name] = False
+                self.set_phase(phase=phase)
             if geometry is None:
                 logger.warning('No Geometry provided, ' + self.name +
-                               'will not be associated with any locations')
+                               ' will not be associated with any locations')
             else:
-                Ps = network.pores(geometry.name)
-                Ts = network.throats(geometry.name)
-                self._add_locations(pores=Ps, throats=Ts)
+                if (phase is None):
+                    logger.warning('Cannot associate with a geometry unless ' +
+                                   'a phase is also given')
+                else:
+                    self.set_geometry(geometry=geometry)
+
+    def set_phase(self, phase, mode='add'):
+        r"""
+        """
+        if phase not in self.project:
+            raise Exception(self.name + ' not in same project as given phase')
+        if mode == 'add':
+            phase['pore.'+self.name] = False
+            phase['throat.'+self.name] = False
+        elif mode == 'remove':
+            phase.pop('pore.'+self.name, None)
+            phase.pop('throat.'+self.name, None)
+        else:
+            raise Exception('mode ' + mode + ' not understood')
+
+    def set_geometry(self, geometry):
+        r"""
+        """
+        if geometry not in self.project:
+            raise Exception(self.name + ' not in same project as given geometry')
+        network = self.network
+        Ps = network.pores(geometry.name)
+        Ts = network.throats(geometry.name)
+        self._add_locations(pores=Ps, throats=Ts)


Test patch:
diff --git a/tests/unit/physics/GenericPhysicsTest.py b/tests/unit/physics/GenericPhysicsTest.py
new file mode 100644
index 000000000..768007b4f
--- /dev/null
+++ b/tests/unit/physics/GenericPhysicsTest.py
@@ -0,0 +1,88 @@
+import openpnm as op
+import pytest
+
+
+class GenericPhysicsTest:
+
+    def setup_class(self):
+        self.net = op.network.Cubic(shape=[3, 3, 3])
+        self.geo = op.geometry.StickAndBall(network=self.net,
+                                            pores=self.net.Ps,
+                                            throats=self.net.Ts)
+
+    def teardown_class(self):
+        mgr = op.Workspace()
+        mgr.clear()
+
+    def test_instantiate_normally(self):
+        phase = op.phases.GenericPhase(network=self.net)
+        phys = op.physics.GenericPhysics(network=self.net,
+                                         phase=phase,
+                                         geometry=self.geo)
+
+    def test_instantiate_with_phase_only(self):
+        phase = op.phases.GenericPhase(network=self.net)
+        phys = op.physics.GenericPhysics(network=self.net,
+                                         phase=phase)
+        assert phys.project is not None
+        assert phys.project.find_phase(phys) is phase
+        with pytest.raises(Exception):
+            phys.project.find_geometry(phys)
+
+    def test_instantiate_with_geometry_only(self):
+        phase = op.phases.GenericPhase(network=self.net)
+        phys = op.physics.GenericPhysics(network=self.net,
+                                         geometry=self.geo)
+        assert phys.project is not None
+        with pytest.raises(Exception):
+            phys.project.find_phase(phys) is phase
+        with pytest.raises(Exception):
+            phys.project.find_geometry(phys)
+
+    def test_instantiate_with_only_network(self):
+        phase = op.phases.GenericPhase(network=self.net)
+        phys = op.physics.GenericPhysics(network=self.net)
+        assert phys.project is not None
+        with pytest.raises(Exception):
+            phys.project.find_phase(phys) is phase
+        with pytest.raises(Exception):
+            phys.project.find_geometry(phys)
+
+    def test_set_phase_afer_instantiation(self):
+        phys = op.physics.GenericPhysics(network=self.net)
+        phase = op.phases.GenericPhase(network=self.net)
+        assert 'pore.' + phys.name not in phase.keys()
+        phys.set_phase(phase=phase, mode='add')
+        assert 'pore.' + phys.name in phase.keys()
+        phys.set_phase(phase=phase, mode='remove')
+        assert 'pore.' + phys.name not in phase.keys()
+
+    def test_set_geom_after_instantiation(self):
+        phase = op.phases.GenericPhase(network=self.net)
+        phys = op.physics.GenericPhysics(network=self.net, phase=phase)
+        assert phase['pore.'+phys.name].sum() == 0
+        phys.set_geometry(geometry=self.geo)
+        assert phase['pore.'+phys.name].sum() == phase.Np
+
+    def test_set_phase_and_geometry_from_different_project(self):
+        net2 = op.network.Cubic(shape=[5, 5, 5])
+        geo2 = op.geometry.GenericGeometry(network=net2)
+        phase2 = op.phases.GenericPhase(network=net2)
+        phase = op.phases.GenericPhase(network=self.net)
+        phys = op.physics.GenericPhysics(network=self.net)
+        with pytest.raises(Exception):
+            phys.set_phase(phase=phase2)
+        phys.set_phase(phase=phase)
+        with pytest.raises(Exception):
+            phys.set_geometry(geometry=geo2)
+
+
+if __name__ == '__main__':
+
+    t = GenericPhysicsTest()
+    self = t
+    t.setup_class()
+    for item in t.__dir__():
+        if item.startswith('test'):
+            print('running test: '+item)
+            t.__getattribute__(item)()