This single method can convert either (a) images containing invasion size or (b) invasion pressure results, such as those produced by porosimetry and drainage, respectively.

import porespy as ps
import numpy as np
import matplotlib.pyplot as plt
[03:06:45] ERROR    PARDISO solver not installed, run `pip install pypardiso`. Otherwise,          _workspace.py:56
                    simulations will be slow. Apple M chips not supported.                                         

Generate an image and apply the porosimetry function:

im = ps.generators.blobs(shape=[200, 200], porosity=0.6)
drainage = ps.filters.porosimetry(im)


The porosimetry function returns an image indicating the radius of the capillary meniscus that at which is was invaded. In this case the Washburn equation is used to convert sizes to capillary pressures, surface tension (sigma) and contact (theta) must be provided, as well as voxel size (voxel_size) if the sizes array is in units of voxels.

data = ps.metrics.pc_curve(im=im, sizes=drainage, voxel_size=1e-5)

The function results a Results object, whose attributes can be inspected by printing:

Results of pc_curve generated at Mon Jun 10 03:06:47 2024
pc                        [407.8608703277795, 815.721740655559, 924.1694802519894, 1047.0350190039558, 1186.2351597260008, 1343.9415383726086, 1522.6145033338332, 1725.041498880861, 1954.3805515746424, 2214.2095380610917, 2508.582002870727, 2842.090397026047, 3219.9377240305935, 3648.018710975678, 4133.011771100392, 4682.483192495964, 5305.005178383415, 6809.339411869592, 7714.620502905222, 11218.720341000993, 14399.999999999998]
snwp                      [0.0, 0.028208333333333332, 0.07691666666666666, 0.08808333333333333, 0.09191666666666666, 0.10758333333333334, 0.27570833333333333, 0.35275, 0.36525, 0.44633333333333336, 0.8315, 0.840625, 0.939625, 0.9485833333333333, 0.9569166666666666, 0.9709583333333334, 0.987, 0.9874583333333333, 0.9885, 0.988875, 0.99025]

Matplotlib can be used to plot the curve:

plt.plot(data.pc, data.snwp, 'b-o')
plt.xlabel('Capillary Pressure [Pa]')
plt.ylabel('Non-wetting Phase Saturation');

Or, other options:

fig, ax = plt.subplots(1, 2, figsize=[12, 6])
ax[0].step(data.pc, data.snwp, 'b-o', where='post')
ax[0].set_xlabel('Capillary Pressure [Pa]')
ax[0].set_ylabel('Non-wetting Phase Saturation')
ax[1].semilogx(data.pc, data.snwp, 'r-o')
ax[1].set_xlabel('Capillary Pressure [Pa]')
ax[1].set_ylabel('Non-wetting Phase Saturation');


In this case no additional information is needed since the sizes have been converted to capillary pressure already, either by a previous function, or by hand.

sigma = 0.072
theta = 180
voxel_size = 1e-5
pc = -2*sigma*np.cos(np.deg2rad(theta))/(drainage*voxel_size)
data = ps.metrics.pc_curve(im=im, pc=pc)
plt.plot(data.pc, data.snwp, 'b-o')
plt.xlabel('Capillary Pressure [Pa]')
plt.ylabel('Non-wetting Phase Saturation');

Note that some voxels in pc had a value of np.inf since they were uninvaded. The pc_curve function replaces these with 2*pressure.max() to improve the plotting.