Mercury Intrusion Porosimetry#

Simulating capillary pressure curves using Porosimetry#

Start by importing OpenPNM.

import numpy as np
import openpnm as op
import matplotlib.pyplot as plt
%config InlineBackend.figure_formats = ['svg']
ws = op.Workspace()
ws.settings["loglevel"] = 40

Next, create a simple cubic network with 20 pores per side and a spacing of 50 um

pn =[20, 20, 20], spacing=0.00005)

The network object only contains topological and spatial information. We need to assign some pore and throat sizes, which can be conveniently done by creating a SpheresAndCylinders geometry object.

geo = op.geometry.SpheresAndCylinders(network=pn, pores=pn.Ps, throats=pn.Ts)

The SpheresAndCylinders class assigns pores as random values between 0.2 and 0.7 of the lattice spacing, then finds all other geometric information assuming the pores are spheres and the throats are cylinders.

Next we need to create a phase object which contains the thermo-physical properties of the invading fluid, such as surface tension:

hg = op.phases.Mercury(network=pn, name='mercury')
AttributeError                            Traceback (most recent call last)
Input In [4], in <cell line: 1>()
----> 1 hg = op.phases.Mercury(network=pn, name='mercury')

AttributeError: module 'openpnm' has no attribute 'phases'

Lastly, we need to compute the capillary entry pressure of the throats in the network. The OpenPNM models library has a few common capillary pressure models, including the Washburn equation:

\[P_C = \frac{-2\sigma cos(\theta)}{R_T}\]

To use this model we should create a physics object, and use the add_model method as follows:

phys = op.physics.GenericPhysics(network=pn, phase=hg, geometry=geo)
NameError                                 Traceback (most recent call last)
Input In [5], in <cell line: 1>()
----> 1 phys = op.physics.GenericPhysics(network=pn, phase=hg, geometry=geo)
      2 phys.add_model(propname='throat.entry_pressure',
      3                model=op.models.physics.capillary_pressure.washburn)

NameError: name 'hg' is not defined

Note that we can inspect our project to see the interrelationships between all the object with:

| net_01 |
| geo_01 |

The grid shows us that phys_01 is associated with the mercury phase, and geo_01. This means that when calculating the throat entry pressure using the Washburn equation above, it will fetch the throat radius (\(R_T\)) from geo_01 and the surface tension and contact angle from mercury.

Now that all the required objects are setup, we can perform the capillary pressure curve simulation. OpenPNM contains both InvasionPercolation and OrdinaryPercolation classes. The porosimetry experiment is ordinary percolation with access limitations. This means that a fixed pressure is applied to the invading fluid and all pores and throat that can be penetrated at that pressure are set as possibly invaded, then pores and throats are set to invaded only if they are physically connected to the source of invading fluid directly or though a path of already invading pores and throats. Since this process is simulated very frequently, OpenPNM includes a class called Porosimetry that is a subclass of OrdinaryPercolation, with several useful methods added. It’s use is illustrated below:

mip = op.algorithms.Porosimetry(network=pn, phase=hg)
AttributeError                            Traceback (most recent call last)
Input In [7], in <cell line: 1>()
----> 1 mip = op.algorithms.Porosimetry(network=pn, phase=hg)
      2 mip.set_inlets(pores=pn.pores('front'))

AttributeError: module 'openpnm.algorithms' has no attribute 'Porosimetry'

The meaning of this warning message will be analyzed below, but first let’s take a quick look at the result using the built-in plotting method:

NameError                                 Traceback (most recent call last)
Input In [8], in <cell line: 1>()
----> 1 mip.plot_intrusion_curve()

NameError: name 'mip' is not defined
Pc, Snwp = mip.get_intrusion_data()
print(Pc, Snwp)
NameError                                 Traceback (most recent call last)
Input In [9], in <cell line: 1>()
----> 1 Pc, Snwp = mip.get_intrusion_data()
      2 print(Pc, Snwp)

NameError: name 'mip' is not defined

With the above data in the form of arrays it’s possible to cut&paste into Excel, or to use a Python plotting package such as Matplotlib to make plots with your desired style:

fig, ax = plt.subplots()
ax.semilogx(Pc, Snwp, 'r*-')
ax.set_xlabel("Capillary pressure (Pa)")
NameError                                 Traceback (most recent call last)
Input In [10], in <cell line: 2>()
      1 fig, ax = plt.subplots()
----> 2 ax.semilogx(Pc, Snwp, 'r*-')
      3 ax.set_xlabel("Capillary pressure (Pa)")
      4 ax.set_ylabel("Saturation")

NameError: name 'Pc' is not defined