diffusive_size_factor_AI#

PoreSpy’s diffusive_size_factor_AI includes the steps for predicting the diffusive size factors of the conduit images decribed here. Note that the diffusive conductance of the conduits can be then calculated by multiplying the size factor by diffusivity of the phase. The function takes in the images of segmented porous medium and returns an array of diffusive size factors for all conduits in the image. Therefore, the framework can be applied to both one conduit image as well as a segmented image of porous medium.

PS_dl

Trained model and supplementary materials#

To use the diffusive_size_factor_AI, the trained model, and training data distribution are required. The AI model files and additional files used in this example are available here. The folder contains following files:

  • Trained model weights: This file includes only weights of the deep learning layers. To use this file, the Resnet50 model structure must be built first.

  • Trained data distribution: This file will be used in denormalizing predicted values based on normalized transform applied on training data. The denormalizing step is included in diffusive_size_factor_AI method.

  • Finite difference diffusive conductance: This file is used in this example to compare the prediction results with finite difference method for segmented regions

  • Pair of regions: This file is used in this example to compare the prediction results with finite difference method for a pair of regions

Let’s download the tensorflow files required to run this notebook:

from contextlib import contextmanager
import os
import sys

@contextmanager
def suppress_stdout():
    with open(os.devnull, "w") as devnull:
        old_stdout = sys.stdout
        sys.stdout = devnull
        try:  
            yield
        finally:
            sys.stdout = old_stdout

with suppress_stdout():
    !pip install -q tensorflow scikit-learn
    if not os.path.exists('sf-model-lib'):
        !git clone https://github.com/PMEAL/sf-model-lib

import tensorflow as tf
import sklearn
2023-01-09 16:08:54.895100: I tensorflow/core/platform/cpu_feature_guard.cc:193] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 AVX512F FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2023-01-09 16:08:55.033994: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /opt/hostedtoolcache/Python/3.8.15/x64/lib
2023-01-09 16:08:55.034015: I tensorflow/compiler/xla/stream_executor/cuda/cudart_stub.cc:29] Ignore above cudart dlerror if you do not have a GPU set up on your machine.
2023-01-09 16:08:55.741178: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /opt/hostedtoolcache/Python/3.8.15/x64/lib
2023-01-09 16:08:55.741265: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /opt/hostedtoolcache/Python/3.8.15/x64/lib
2023-01-09 16:08:55.741272: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.

Also, since the model weights have been stored in chunks, they need to be recombined first:

import importlib
h5tools = importlib.import_module("sf-model-lib.h5tools")
DIR_WEIGHTS = "sf-model-lib/diffusion"
fname_in = [f"{DIR_WEIGHTS}/model_weights_part{j}.h5" for j in [0, 1]]
h5tools.combine(fname_in, fname_out=f"{DIR_WEIGHTS}/model_weights.h5")

Note that to use diffusive_size_factor_AI, Scikit-learn and Tensorflow must be installed. Import necessary packages and the AI model:

import inspect
import os
import warnings

import h5py
import numpy as np
import porespy as ps
import scipy as sp
from matplotlib import pyplot as plt
from sklearn.metrics import r2_score

ps.visualization.set_mpl_style()
warnings.filterwarnings("ignore")
inspect.signature(ps.networks.diffusive_size_factor_AI)
<Signature (regions, throat_conns, model, g_train, voxel_size=1)>

model , g_train#

Import AI model and training data from the downloaded folder:

path = "./sf-model-lib/diffusion"
path_train = os.path.join(path, 'g_train_original.hdf5')
path_weights = os.path.join(path, 'model_weights.h5')
g_train = h5py.File(path_train, 'r')['g_train'][()]
model = ps.networks.create_model()
model.load_weights(path_weights)
2023-01-09 16:08:58.792155: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcuda.so.1'; dlerror: libcuda.so.1: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /opt/hostedtoolcache/Python/3.8.15/x64/lib
2023-01-09 16:08:58.792179: W tensorflow/compiler/xla/stream_executor/cuda/cuda_driver.cc:265] failed call to cuInit: UNKNOWN ERROR (303)
2023-01-09 16:08:58.792200: I tensorflow/compiler/xla/stream_executor/cuda/cuda_diagnostics.cc:156] kernel driver does not appear to be running on this host (fv-az551-888): /proc/driver/nvidia/version does not exist
2023-01-09 16:08:58.792450: I tensorflow/core/platform/cpu_feature_guard.cc:193] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 AVX512F FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
[16:09:00] WARNING  `lr` is deprecated, please use `learning_rate` instead, or use the legacy      optimizer.py:106
                    optimizer, e.g.,tf.keras.optimizers.legacy.Adam.                                               

regions#

We can create a 3D image using PoreSpy’s poly_disperese_spheres generator and segment the image using snow_partitioning method. Note that find_conns method returns the connections in the segmented region. The order of values in conns is similar to the network extraction conns. Therefore, the region with label=1 in the segmented image is mapped to indice 0 in conns.

np.random.seed(17)
shape = [120, 120, 120]
dist = sp.stats.norm(loc=7, scale=5)
im = ps.generators.polydisperse_spheres(shape=shape,
                                        porosity=0.7,
                                        dist=dist,
                                        r_min=7)
results = ps.filters.snow_partitioning(im=im.astype(bool))
regions = results['regions']
fig, ax = plt.subplots(1, 1, figsize=[4, 4])
ax.imshow(regions[:, :, 20], origin='lower', interpolation='none')
ax.axis(False);
../../../_images/e9e6242a693ee164272b3186829c060eec35be4b5ea3d7aa10ba64b57c880b39.png

throat_conns#

PoreSpy’s diffusive_size_factor_AI method takes in the segmented image, model, training ground truth values, and the conncetions of regions in the segmented image (throat conns). In this example we have created an image with voxel_size=1. For a different voxel size, the voxel_size argument needs to be passed to the method.

conns = ps.networks.find_conns(regions)
size_factors = ps.networks.diffusive_size_factor_AI(regions,
                                                    model=model,
                                                    g_train=g_train,
                                                    throat_conns=conns)
2023-01-09 16:09:21.153000: W tensorflow/tsl/framework/cpu_allocator_impl.cc:82] Allocation of 780140544 exceeds 10% of free system memory.
2023-01-09 16:09:21.581116: W tensorflow/tsl/framework/cpu_allocator_impl.cc:82] Allocation of 780140544 exceeds 10% of free system memory.
 1/47 [..............................] - ETA: 2:24

 2/47 [>.............................] - ETA: 1:19

 3/47 [>.............................] - ETA: 1:16

 4/47 [=>............................] - ETA: 1:14

 5/47 [==>...........................] - ETA: 1:12

 6/47 [==>...........................] - ETA: 1:10

 7/47 [===>..........................] - ETA: 1:08

 8/47 [====>.........................] - ETA: 1:07
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
Cell In [6], line 2
      1 conns = ps.networks.find_conns(regions)
----> 2 size_factors = ps.networks.diffusive_size_factor_AI(regions,
      3                                                     model=model,
      4                                                     g_train=g_train,
      5                                                     throat_conns=conns)

File ~/work/porespy/porespy/porespy/networks/_size_factors.py:73, in diffusive_size_factor_AI(regions, throat_conns, model, g_train, voxel_size)
     71     batch_size = 16
     72 test_steps = math.ceil(len(throat_conns) / batch_size)
---> 73 predictions = model.predict(test_data, steps=test_steps)
     74 predictions = np.squeeze(predictions)
     75 denorm_size_factor = _denorm_predict(predictions, g_train)

File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/keras/utils/traceback_utils.py:65, in filter_traceback.<locals>.error_handler(*args, **kwargs)
     63 filtered_tb = None
     64 try:
---> 65     return fn(*args, **kwargs)
     66 except Exception as e:
     67     filtered_tb = _process_traceback_frames(e.__traceback__)

File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/keras/engine/training.py:2350, in Model.predict(self, x, batch_size, verbose, steps, callbacks, max_queue_size, workers, use_multiprocessing)
   2348 for step in data_handler.steps():
   2349     callbacks.on_predict_batch_begin(step)
-> 2350     tmp_batch_outputs = self.predict_function(iterator)
   2351     if data_handler.should_sync:
   2352         context.async_wait()

File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/tensorflow/python/util/traceback_utils.py:150, in filter_traceback.<locals>.error_handler(*args, **kwargs)
    148 filtered_tb = None
    149 try:
--> 150   return fn(*args, **kwargs)
    151 except Exception as e:
    152   filtered_tb = _process_traceback_frames(e.__traceback__)

File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/tensorflow/python/eager/polymorphic_function/polymorphic_function.py:880, in Function.__call__(self, *args, **kwds)
    877 compiler = "xla" if self._jit_compile else "nonXla"
    879 with OptionalXlaContext(self._jit_compile):
--> 880   result = self._call(*args, **kwds)
    882 new_tracing_count = self.experimental_get_tracing_count()
    883 without_tracing = (tracing_count == new_tracing_count)

File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/tensorflow/python/eager/polymorphic_function/polymorphic_function.py:919, in Function._call(self, *args, **kwds)
    916 self._lock.release()
    917 # In this case we have not created variables on the first call. So we can
    918 # run the first trace but we should fail if variables are created.
--> 919 results = self._variable_creation_fn(*args, **kwds)
    920 if self._created_variables and not ALLOW_DYNAMIC_VARIABLE_CREATION:
    921   raise ValueError("Creating variables on a non-first call to a function"
    922                    " decorated with tf.function.")

File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/tensorflow/python/eager/polymorphic_function/tracing_compiler.py:134, in TracingCompiler.__call__(self, *args, **kwargs)
    131 with self._lock:
    132   (concrete_function,
    133    filtered_flat_args) = self._maybe_define_function(args, kwargs)
--> 134 return concrete_function._call_flat(
    135     filtered_flat_args, captured_inputs=concrete_function.captured_inputs)

File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/tensorflow/python/eager/polymorphic_function/monomorphic_function.py:1745, in ConcreteFunction._call_flat(self, args, captured_inputs, cancellation_manager)
   1741 possible_gradient_type = gradients_util.PossibleTapeGradientTypes(args)
   1742 if (possible_gradient_type == gradients_util.POSSIBLE_GRADIENT_TYPES_NONE
   1743     and executing_eagerly):
   1744   # No tape is watching; skip to running the function.
-> 1745   return self._build_call_outputs(self._inference_function.call(
   1746       ctx, args, cancellation_manager=cancellation_manager))
   1747 forward_backward = self._select_forward_and_backward_functions(
   1748     args,
   1749     possible_gradient_type,
   1750     executing_eagerly)
   1751 forward_function, args_with_tangents = forward_backward.forward()

File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/tensorflow/python/eager/polymorphic_function/monomorphic_function.py:378, in _EagerDefinedFunction.call(self, ctx, args, cancellation_manager)
    376 with _InterpolateFunctionError(self):
    377   if cancellation_manager is None:
--> 378     outputs = execute.execute(
    379         str(self.signature.name),
    380         num_outputs=self._num_outputs,
    381         inputs=args,
    382         attrs=attrs,
    383         ctx=ctx)
    384   else:
    385     outputs = execute.execute_with_cancellation(
    386         str(self.signature.name),
    387         num_outputs=self._num_outputs,
   (...)
    390         ctx=ctx,
    391         cancellation_manager=cancellation_manager)

File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/tensorflow/python/eager/execute.py:52, in quick_execute(op_name, num_outputs, inputs, attrs, ctx, name)
     50 try:
     51   ctx.ensure_initialized()
---> 52   tensors = pywrap_tfe.TFE_Py_Execute(ctx._handle, device_name, op_name,
     53                                       inputs, attrs, num_outputs)
     54 except core._NotOkStatusException as e:
     55   if name is not None:

KeyboardInterrupt: 

Compare with finite difference#

Assuming a diffusivity of 1, the diffusive conductance of the conduits will be equal to their diffusive size factors. Now let’s compare the AI-based diffusive conductances with the conductance values calculated by finite difference method. The finite difference method results are found using the steps explained here.

fname = os.path.join(path, 'g_finite_difference120-phi7.hdf5')
g_FD = h5py.File(fname, 'r')['g_finite_difference'][()]
g_AI = size_factors
plt.figure(figsize=[4, 4])
plt.xlim([0, 80])
plt.ylim([0, 80])
plt.plot(g_FD, g_AI, '*', [0, 80], [0, 80], 'r')
plt.xlabel('g reference')
plt.ylabel('g prediction')
r2 = r2_score(g_FD, g_AI)
print(f"The R^2 prediction accuracy is {r2:.3}")
The R^2 prediction accuracy is 0.952
../../../_images/5f62c8f7eb8460c94b75a2ce61ecd60fe9d2f97215101823ca4a0ce49bb8eb53.svg

Note on runtime: A larger part of AI_size_factors runtime is related to extracting the pairs of conduits, which is the common step required for both AI and finite difference method. Once the data is prepared, AI Prediction on the tensor takes a smaller amount of time in contrast to finite difference method, as it was shown here.

Apply on one conduit#

PoreSpy’s diffusive_size_factor_AI method can take in an image of a pair of regions. An image of a conduit and its diffusive size factor was saved for comparison:

fname = os.path.join(path, 'pair.hdf5')
pair_in = h5py.File(fname, 'r')
im_pair = pair_in['pair'][()]
conns = ps.networks.find_conns(im_pair)
sf_FD = pair_in['size_factor'][()]
sf_AI = ps.networks.diffusive_size_factor_AI(im_pair,
                                             model=model,
                                             g_train=g_train,
                                             throat_conns=conns)
print(f"Diffusive size factor from FD: {sf_FD[0]:.2f}, and AI: {sf_AI[0]:.2f}")
Diffusive size factor from FD: 9.87, and AI: 9.91