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>
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>
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>
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>
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>
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>
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>