The Cubic lattice network is easily the most commonly used pore network topology. When people first learn about pore network modeling they often insist on creating network that are topologically equivalent or representative of the real network (i.e. random networks extracted from tomography images). In reality, however, a simple cubic network provides a very passable representation of more complex topologies, and provides several additional benefits as well; namely they are much easier to visualize, and applying boundary conditions is easier since the faces of the network are flat.
The examples below will demonstrate how to create various cubic lattice networks in OpenPNM using the Cubic class, as well as illustrating a few topological manipulations that can be performed, such as adding boundary pores, and trimming throats to create a more random-like topology.
Let's start with the most basic cubic lattice:
In [1]:
import openpnm as op
wrk = op.Workspace()
wrk.logelevel=50
pn = op.network.Cubic(shape=[10, 10, 10], spacing=1)
In this case pn
will be a 10 x 10 x 10 cube with each pore spaced 1 unit away from it's neighbors in all directions. Each pore is connected to the 6 neighbors adjacent to each face of the cubic lattice site in which it sits. The image below illustrates the resulting network with pores shown as white spheres, along with a zoomed in view of the internals, showing the connectivity of the pores.
The Cubic network generator applies 6-connectivity by default, but different values can be specified. In a cubic lattice, each pore can have up to 26 neighbors: 6 on each face, 8 on each corner, and 12 on each edge. This is illustrated in the image below.
Cubic networks can have any combination of corners, edges, and faces, which is controlled with the connectivity
argument by specifying the total number of neighbors (6, 8, 12, 14, 18, 20, or 26):
In [2]:
pn = op.network.Cubic(shape=[10, 10, 10], spacing=1, connectivity=26)
This yields the following network, which clearly has a LOT of connections!
Often it is desired to create a distribution of coordination numbers on each pore, such that some pores have 2 neighbors and other have 8, while the overall average may be around 5. It is computationally very challenging to specify a specific distribution, so OpenPNM does not offer this feature (yet); however it can be approximated manually by creating a highly connected network, and then trimming random throats to reduced the coordination numbers. The following code block randomly selects 500 throats, then trims them from the network:
In [3]:
import scipy as sp
pn = op.network.Cubic(shape=[10, 10, 10], spacing=[1, 1, 1], connectivity=26)
print(pn.num_throats())
In [4]:
throats_to_trim = sp.random.randint(low=0, high=pn.Nt-1, size=500)
from openpnm import topotools as tt
tt.trim(network=pn, throats=throats_to_trim)
# randint returns some duplicate numbers so actual number of trimmed throats varies
assert pn.num_throats() < 10476
The following image shows histogram of the pore connectivity before and after trimming. Before trimming the coordination numbers fall into 4 distinct bins depending on where the pores lies (internal, face, edge or corner), while after trimming the coordination numbers show some distribution around their original values. If the trimming is too aggressive, OpenPNM might report an error message saying that isolated pores exist, which means that some regions of the network are now disconnected from the main network due to a lack of connected throats.
In [5]:
pn = op.network.Cubic(shape=[20, 20, 10], spacing=[0.003, 0.02, 0.01])
This results in the following network with is squished in the x-direction.
The Cubic class can generate networks of arbitrary shapes (i.e. spheres), but still with cubic connectivity. This is accomplished using the template
argument, which accepts a binary image of 1's and 0's. The network will have pores where the 1's are and 0's elsewhere. For instance, to make a spherical domain for a catalyst pellet, generate an image of a sphere using Scipy's NDimage module, the pass this image to Cubic as follows:
In [6]:
import scipy.ndimage as spim
im = sp.ones([21, 21, 21])
im[10, 10, 10] = 0
dt = spim.distance_transform_bf(input=im)
sphere = dt < 10
pn = op.network.CubicTemplate(template=sphere, spacing=0.1)
This results in the following:
All images of networks were made with paraview by exporting a VTK file with the following command:
In [7]:
op.io.VTK.save(network=pn, filename='my_network')