Adding Boundary Pores#

[1]:
import numpy as np
import openpnm as op
%config InlineBackend.figure_formats = ['svg']
import matplotlib.pyplot as plt
ws = op.Workspace()
ws.settings['loglevel'] = 40
np.random.seed(10)
%matplotlib inline

Start by creating a Delaunay network. Because it uses random base points it will better illustrate the process of adding boundary pores to arbitrary networks:

[2]:
pn = op.network.Delaunay(points=200, shape=[1, 1, 0])
print(pn)
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
openpnm.network.Delaunay : net_01
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
#     Properties                                    Valid Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
1     pore.coords                                     248 / 248
2     throat.conns                                    601 / 601
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
#     Labels                                        Assigned Locations
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
1     pore.all                                      248
2     pore.back                                     9
3     pore.bottom                                   248
4     pore.boundary                                 48
5     pore.front                                    12
6     pore.internal                                 200
7     pore.left                                     14
8     pore.right                                    13
9     pore.surface                                  44
10    pore.top                                      248
11    throat.all                                    601
12    throat.boundary                               0
13    throat.internal                               553
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
Parameters                          Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――

As can be seen in the above printout, the Delaunay class predefines many labels including boundaries and sides. In fact, as can be seen in the plot below, the Delaunay class also adds boundary pores to the topology. (Note that the Delaunay network is generated randomly so your’s will not look the same, nor have the same number of total pores and throats). In this case, the location of the boundary pores is determined from the Voronoi cell that surrounds each Delaunay point, so the boundary cells apper to be randomly oriented relative to the internal pore they are connected with. In the example that follows, we’ll be removing these pores, then adding boundary pores in a manual way.

[3]:
fig, ax = plt.subplots()
op.topotools.plot_connections(network=pn, ax=ax)
op.topotools.plot_coordinates(network=pn, ax=ax,
                              markersize=50, c='r')
[3]:
<matplotlib.collections.PathCollection at 0x7fb803f59490>
../../../_images/examples_tutorials_network_adding_boundary_pores_5_1.svg

For the purpose of this tutorial, we will trim these boundary pores from the network since we’ll be adding our own.

[4]:
op.topotools.trim(network=pn, pores=pn.pores('boundary'))
print(pn)
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
openpnm.network.Delaunay : net_01
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
#     Properties                                    Valid Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
1     pore.coords                                     200 / 200
2     throat.conns                                    553 / 553
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
#     Labels                                        Assigned Locations
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
1     pore.all                                      200
2     pore.back                                     0
3     pore.bottom                                   200
4     pore.boundary                                 0
5     pore.front                                    0
6     pore.internal                                 200
7     pore.left                                     0
8     pore.right                                    0
9     pore.surface                                  44
10    pore.top                                      200
11    throat.all                                    553
12    throat.boundary                               0
13    throat.internal                               553
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
Parameters                          Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――

Plotting the network now shows the missing pores. Our goal will be re-add boundary pores to each face.

[5]:
fig, ax = plt.subplots(figsize=(7, 7))
op.topotools.plot_connections(network=pn, ax=ax)
op.topotools.plot_coordinates(network=pn, ax=ax,
                              markersize=50, c='r')
[5]:
<matplotlib.collections.PathCollection at 0x7fb804094580>
../../../_images/examples_tutorials_network_adding_boundary_pores_9_1.svg

Find Surface Pores: The Basic Way#

The easiest way to find pores on one surface of a domain is using coordinates. In the code below we’ll find all pores on the right surface based on their x-coordinate.

[6]:
Ps = pn['pore.coords'][:, 0] > 0.9 * pn['pore.coords'][:, 0].max()

We can now label these pores are ‘right’ for later use, such applying boundary conditions:

[7]:
pn.set_label(pores=Ps, label='pore.right_surface')

Now let’s visualize the network to see which pores we’ve actually labelled:

[8]:
fig, ax = plt.subplots(figsize=(7, 7))
op.topotools.plot_connections(network=pn, ax=ax)
op.topotools.plot_coordinates(network=pn, pores=pn.pores('right_surface'),
                                    markersize=50, ax=ax, c='r')
[8]:
<matplotlib.collections.PathCollection at 0x7fb800e88580>
../../../_images/examples_tutorials_network_adding_boundary_pores_15_1.svg

Find Surface Pores: The Proper Way#

The topotools module in OpenPNM provides many handy helper functions for dealing with topology. We’ll first use the find_surface_pores function. It works be specifying the location of a set of marker points outside the domain, then performing a Delaunay tessellation between these markers and the network pores. Any pores that form a simplex with the marker points are considered to be on the surface. By default OpenPNM will place one marker on each edge of the domain in an attempt to find all the surfaces. In our case, we will specify them manually to only find one face.

Specifying the markers can be a challenge. If we only specify a single marker, we will only find a limited number of surface pores due to the way the triangulation works.

[9]:
markers = np.array([[-0.1, 0.5]])
op.topotools.find_surface_pores(network=pn, markers=markers, label='left_surface')
fig, ax = plt.subplots()
op.topotools.plot_connections(network=pn, ax=ax)
op.topotools.plot_coordinates(network=pn, pores=pn.pores('left_surface'),
                              markersize=50, ax=ax, c='r')
[9]:
<matplotlib.collections.PathCollection at 0x7fb800e2c370>
../../../_images/examples_tutorials_network_adding_boundary_pores_18_1.svg

As can be seen, some of the pores in deeper recesses of the surface were not found by this method. If we want to be certain of finding all the surface pores on the left side of the domain we can add more markers:

[10]:
markers = np.array([[-0.1, 0.2], [-0.1, 0.4], [-0.1, 0.6], [-0.1, 0.8]])
op.topotools.find_surface_pores(network=pn, markers=markers,
                                label='left_surface')
fig, ax = plt.subplots(figsize=(7, 7))
op.topotools.plot_connections(network=pn, ax=ax)
op.topotools.plot_coordinates(network=pn, pores=pn.pores('left_surface'),
                              markersize=50, ax=ax, c='r')
[10]:
<matplotlib.collections.PathCollection at 0x7fb800d03940>
../../../_images/examples_tutorials_network_adding_boundary_pores_20_1.svg

Now we’ve captured several more pores. In some cases we may actually get more than we wanted, including some that are more correctly on the bottom of the domain. This is why finding surfaces requires a careful touch, although this problem becomes less important in domains with more pores.

Cloning Surface Pores to Make Proper Boundary Pores#

Next we want to take the newly labeled surface pores and ‘clone’ them. This creates new pores in the network that are physically located in the same place as their ‘parents’. They are also connected only to their ‘parents’ by default which is what we want, though this can be changed using the mode argument. In the following code, we tell the function to clone the ‘left_surface’ pores and to give them a new label of ‘left_boundary’.

[11]:
op.topotools.clone_pores(network=pn, pores=pn.pores('left_surface'),
                         labels=['left_boundary'])

Now that we’ve cloned the pores, we need to move them. In this case we want them to all site on the x=0 boundary face. We can do this by directly altering the ‘pore.coords’ array:

[12]:
Ps = pn.pores('left_boundary')
coords = pn['pore.coords'][Ps]
coords *= [0, 1, 1]
pn['pore.coords'][Ps] = coords
print(pn)
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
openpnm.network.Delaunay : net_01
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
#     Properties                                    Valid Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
1     pore.coords                                     212 / 212
2     throat.conns                                    565 / 565
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
#     Labels                                        Assigned Locations
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
1     pore.all                                      212
2     pore.back                                     0
3     pore.bottom                                   200
4     pore.boundary                                 0
5     pore.front                                    0
6     pore.internal                                 200
7     pore.left                                     0
8     pore.left_boundary                            12
9     pore.left_surface                             12
10    pore.right                                    0
11    pore.right_surface                            13
12    pore.surface                                  44
13    pore.top                                      200
14    throat.all                                    565
15    throat.boundary                               0
16    throat.internal                               553
17    throat.left_boundary                          12
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
Parameters                          Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――

The above code will set the x-coordinate of each of the cloned pores to 0, while maintaining the other coordinates the same. The result is:

[13]:
fig, ax = plt.subplots(figsize=(7, 7))
op.topotools.plot_connections(network=pn, ax=ax)
op.topotools.plot_coordinates(network=pn, pores=pn.pores('left_surface'),
                              markersize=50, ax=ax, c='r')
op.topotools.plot_coordinates(network=pn, pores=pn.pores('left_boundary'),
                              markersize=50, ax=ax, c='g')
[13]:
<matplotlib.collections.PathCollection at 0x7fb800d28d30>
../../../_images/examples_tutorials_network_adding_boundary_pores_27_1.svg

Let’s also make clones of the ‘right_surface’ pores as well:

[14]:
op.topotools.clone_pores(network=pn, pores=pn.pores('right_surface'), labels=['right_boundary'])

We need to set their x-coords to all 1.0:

[15]:
Ps = pn.pores('right_boundary')
coords = pn['pore.coords'][Ps]
coords[:, 0] = 1
pn['pore.coords'][Ps] = coords

And finally, let’s plot the whole network together:

[16]:
fig, ax = plt.subplots(figsize=(7, 7))
op.topotools.plot_connections(network=pn, ax=ax)
op.topotools.plot_coordinates(network=pn, c='r',
                              markersize=50, ax=ax)
[16]:
<matplotlib.collections.PathCollection at 0x7fb800c4d610>
../../../_images/examples_tutorials_network_adding_boundary_pores_33_1.svg