Repository: PMEAL/OpenPNM
Pull request #1288 (2019-05-30 02:35:47)

Adding imported geometry
This addresses issue #1115 


Patch:
diff --git a/openpnm/geometry/GenericGeometry.py b/openpnm/geometry/GenericGeometry.py
index a6c3aaee1..d3709a8b4 100644
--- a/openpnm/geometry/GenericGeometry.py
+++ b/openpnm/geometry/GenericGeometry.py
@@ -93,4 +93,8 @@ class GenericGeometry(Subdomain, ModelsMixin):
                             self.name + ' to entire domain')
                 pores = network.Ps
                 throats = network.Ts
-            self._add_locations(pores=pores, throats=throats)
+            try:
+                self._add_locations(pores=pores, throats=throats)
+            except Exception as e:
+                network.project.purge_object(self)
+                logger.error(str(e) +  ', instantiation cancelled')
diff --git a/openpnm/geometry/Imported.py b/openpnm/geometry/Imported.py
new file mode 100644
index 000000000..4dfdf26b0
--- /dev/null
+++ b/openpnm/geometry/Imported.py
@@ -0,0 +1,61 @@
+import openpnm.models as mods
+from openpnm.geometry import GenericGeometry
+
+
+class Imported(GenericGeometry):
+    r"""
+    This geometry class extracts all numerical properites from the received
+    network object and moves them to itself.
+
+    This class is intended for use with networks imported from network
+    extraction codes, where the geometry properties are included on the
+    network itself.  In these cases, an error occurs when adding other
+    geometries to the project, such as adding boundary pores or in more
+    elaborate scenarios such as stitching networks together.  The issue
+    arises since OpenPNM prevents a property, such as 'pore.volume', from
+    existing on both the network and also a geometry.  Thus it is necessary
+    to move the extracted network properties to this ``Imported`` class,
+    then create new geometry objects for any added pores as needed.
+
+    Parameters
+    ----------
+    network : OpenPNM Network object
+        The network with which this Geometry should be associated
+
+    exclude : list of strings
+        A list of which network properties should *not* be transferred to
+        new geometry object.  'pore.coords' and 'throat.conns' are *always*
+        excluded.  Note that labels are not transferred, only properties.
+
+    project : OpenPNM Project object, optional
+        Can be supplied in addition to ``network`` but is inferred from the
+        network's project if not given.
+
+    name : string
+        The name of the object, which is also used as the label where this
+        geometry is defined.
+
+    """
+
+    def __init__(self, network, exclude=[], **kwargs):
+        super().__init__(network=network, **kwargs)
+        exclude.extend(['pore.coords', 'throat.conns'])
+        for item in network.props():
+            if item not in exclude:
+                self[item] = network.pop(item)
+
+        if 'throat.endpoints' not in self.keys():
+            self.add_model(propname='throat.endpoints',
+                           model=mods.geometry.throat_endpoints.spherical_pores,
+                           pore_diameter='pore.diameter',
+                           throat_diameter='throat.diameter')
+
+        if 'throat.length' not in self.keys():
+            self.add_model(propname='throat.length',
+                           model=mods.geometry.throat_length.piecewise,
+                           throat_endpoints='throat.endpoints')
+
+        self.add_model(propname='throat.conduit_lengths',
+                       model=mods.geometry.throat_length.conduit_lengths,
+                       throat_endpoints='throat.endpoints',
+                       throat_length='throat.length')
diff --git a/openpnm/geometry/__init__.py b/openpnm/geometry/__init__.py
index 2bb205056..81c155093 100644
--- a/openpnm/geometry/__init__.py
+++ b/openpnm/geometry/__init__.py
@@ -93,3 +93,4 @@ from .StickAndBall import StickAndBall
 from .StickAndBall_2D import StickAndBall_2D
 from .Boundary import Boundary
 from .Boundary_2D import Boundary_2D
+from .Imported import Imported


Test patch:
diff --git a/tests/unit/geometry/ImportedTest.py b/tests/unit/geometry/ImportedTest.py
new file mode 100644
index 000000000..6083b7725
--- /dev/null
+++ b/tests/unit/geometry/ImportedTest.py
@@ -0,0 +1,62 @@
+import openpnm as op
+import numpy as np
+import pytest
+ws = op.Workspace()
+ws.settings['loglevel'] = 50
+
+
+class ImportedTest:
+
+    def setup_class(self):
+        pass
+
+    def test_init(self):
+        net = op.network.Cubic(shape=[3, 3, 3])
+        net['pore.diameter'] = 2.0
+        net['throat.diameter'] = 1.0
+        geo = op.geometry.Imported(network=net)
+        assert 'pore.diameter' in geo.keys()
+        assert 'pore.diameter' not in net.keys()
+        assert 'throat.diameter' in geo.keys()
+        assert 'throat.diameter' not in net.keys()
+
+    def test_init_no_props_on_network(self):
+        net = op.network.Cubic(shape=[3, 3, 3])
+        geo = op.geometry.Imported(network=net)
+        assert 'throat.length' not in geo.keys()
+        assert 'throat.endpoints' not in geo.keys()
+        assert 'throat.conduit_lengths' not in geo.keys()
+        assert 'throat.length' in geo.models.keys()
+        assert 'throat.endpoints' in geo.models.keys()
+        assert 'throat.conduit_lengths' in geo.models.keys()
+
+    def test_with_added_pores(self):
+        net = op.network.Cubic(shape=[3, 3, 3])
+        net['pore.diameter'] = 2.0
+        net['throat.diameter'] = 1.0
+        geo = op.geometry.Imported(network=net)
+        op.topotools.extend(network=net,
+                            pore_coords=[[1, 1, 1]],
+                            throat_conns=[[0, 27]])
+        h = net.project.check_geometry_health()
+        assert h.health is False
+        geo2 = op.geometry.GenericGeometry(network=net, pores=27, throats=54)
+        h = net.project.check_geometry_health()
+        assert h.health is True
+        assert np.any(np.isnan(net['pore.diameter']))
+        assert np.any(np.isnan(net['throat.diameter']))
+
+    def teardown_class(self):
+        mgr = op.Workspace()
+        mgr.clear()
+
+
+if __name__ == '__main__':
+
+    t = ImportedTest()
+    self = t
+    t.setup_class()
+    for item in t.__dir__():
+        if item.startswith('test'):
+            print('running test: '+item)
+            t.__getattribute__(item)()