random_spheres#

Generates randomly located, non-overlapping spheres using the ‘random sequential addition’ technique.

import porespy as ps
import matplotlib.pyplot as plt
import numpy as np
ps.visualization.set_mpl_style()
[17:46:58] ERROR    PARDISO solver not installed, run `pip install pypardiso`. Otherwise,          _workspace.py:56
                    simulations will be slow. Apple M chips not supported.                                         

im or shape#

The function can either add spheres an existing image, or create a new image of the given shape and add spheres to that. Let’s start with an empty image, and set the void fraction to a low value:

shape = [300, 300]
r = 15
im = ps.generators.random_spheres(shape=shape, r=r)
fig, ax = plt.subplots(1, 1, figsize=[4, 4])
ax.axis(False)
ax.imshow(im, origin='lower', interpolation='none');
../../../_images/cd07cdf6251bf00cc0a84ed79513868c73af93559e0970727ba26b8ecd93154f.png

Now let’s fill up the remaining space with as many smaller spheres as possible:

r = 5
im = ps.generators.random_spheres(im=im, r=r)
fig, ax = plt.subplots(1, 1, figsize=[4, 4])
ax.axis(False)
ax.imshow(im, origin='lower', interpolation='none');
../../../_images/64d572824113650a1d94d0683c5c183dcd7af3d4d1e682965a527e07b1bc77ea.png

edges#

Spheres can either be fully contained within the image or be truncated at the edges:

fig, ax = plt.subplots(1, 2, figsize=[8, 4])

r = 20
mode = 'contained'
im1 = ps.generators.random_spheres(shape=shape, r=r, edges=mode)
ax[0].imshow(im1, origin='lower', interpolation='none')
ax[0].axis(False)
ax[0].set_title(f'edges = {mode}')

mode = 'extended'
im2 = ps.generators.random_spheres(shape=shape, r=r, edges=mode)
ax[1].imshow(im2, origin='lower', interpolation='none')
ax[1].axis(False)
ax[1].set_title(f'edges = {mode}');
../../../_images/56c6d9440bd98b4a0b4fd541905dfdf80f641ad89332ef3690b20bb10850761d.png

clearance#

Spheres can be made to partially overlap, the so called ‘cherry pit’ model, or to have some clearance:

fig, ax = plt.subplots(1, 2, figsize=[8, 4])

c = -4
im1 = ps.generators.random_spheres(shape=shape, r=r, clearance=c)
ax[0].imshow(im1, origin='lower', interpolation='none')
ax[0].axis(False)
ax[0].set_title(f'clearance = {c}')

c = 4
im2 = ps.generators.random_spheres(shape=shape, r=r, clearance=c)
ax[1].imshow(im2, origin='lower', interpolation='none')
ax[1].axis(False)
ax[1].set_title(f'clearance = {c}');
../../../_images/3a864edb1cd00bca61966bce2be41d570b6fc2867728e258388ace3f3d898d99.png

protrusion#

This controls how much the added spheres are allowed to protrude into the background.

im1 = ps.generators.blobs(shape=[300, 300], porosity=0.5)
im2 = ps.generators.random_spheres(im=im1, r=8, protrusion=-5)
im3 = ps.generators.random_spheres(im=im1, r=8, protrusion=5)

fig, ax = plt.subplots(1, 3, figsize=[10, 4])
ax[0].imshow(im1, origin='lower', interpolation='none')
ax[1].imshow(im2, origin='lower', interpolation='none')
ax[2].imshow(im3, origin='lower', interpolation='none');
../../../_images/4e04911a61cd9f22ea3c0672b74b79445ee5302fa58f28d5432db79287963c48.png

In the case of pre-existing spheres this can be used to give some clearance between existing spheres and new ones by using a negative value:

im1 = np.zeros([300, 300])
im2 = ps.generators.random_spheres(im=im1, r=15, clearance=5)
im3 = ps.generators.random_spheres(im=im2, r=5, protrusion=-5)
im4 = ps.generators.random_spheres(im=im2, r=5, protrusion=5)

fig, ax = plt.subplots(1, 3, figsize=[10, 4])
ax[0].imshow(im2, origin='lower', interpolation='none')
ax[1].imshow(im3, origin='lower', interpolation='none')
ax[2].imshow(im4, origin='lower', interpolation='none');
../../../_images/d657b772cbd2d23924db6c4a67adaa7365dfbd889c37c048d3f8cace42c43a58.png

phi and maxiter#

The number of spheres to add can be controlled directly by specifying maxiter, or implicitly by specifying phi (the solid volume fraction). Setting phi will attempt to compute the correct value of maxiter internally so will not give the exact phi requested:

fig, ax = plt.subplots(1, 2, figsize=[8, 4])

maxiter = 5
im1 = ps.generators.random_spheres(shape=shape, r=r, clearance=c, maxiter=maxiter)
ax[0].imshow(im1, origin='lower', interpolation='none')
ax[0].axis(False)
ax[0].set_title(f'maxiter = {maxiter}')

vf = 0.2
im2 = ps.generators.random_spheres(shape=shape, r=r, clearance=c, phi=vf)
ax[1].axis(False)
ax[1].set_title(f'phi = {vf}')
ax[1].imshow(im2, origin='lower', interpolation='none');
../../../_images/0e18bb75eb342c53940b9c72882356d639eef9922f5066650c961898e676e6bc.png

smooth#

Controls whether or not the inserted spheres have the little nibs or pips on each face. Note that if smooth=True, then r is increased by 1 to ensure the radius of the inserted spheres matches the requested value.

fig, ax = plt.subplots(1, 2, figsize=[8, 4])

smooth = True
im1 = ps.generators.random_spheres(shape=[100, 100], r=10, smooth=smooth)
ax[0].imshow(im1, origin='lower', interpolation='none')
ax[0].axis(False)
ax[0].set_title(f'smooth = {smooth}')

smooth = False
im2 = ps.generators.random_spheres(shape=[100, 100], r=10, smooth=smooth)
ax[1].axis(False)
ax[1].set_title(f'smooth = {smooth}')
ax[1].imshow(im2, origin='lower', interpolation='none');
../../../_images/35fd4d383d0523bf39c74b6383c64f986b8bcd48c57dae4de069b4de9506807a.png

seed#

This function uses numba to accelerate the insertion process, which means that the numpy random number generator is not used. To generate a repeatable realization of the spheres, pass an integer value to the seed argument:

fig, ax = plt.subplots(1, 3, figsize=[12, 4])

seed = 1
im1 = ps.generators.random_spheres(shape=[100, 100], r=10, seed=seed)
ax[0].imshow(im1, origin='lower', interpolation='none')
ax[0].axis(False)
ax[0].set_title(f'seed = {seed}')

seed = 1
im2 = ps.generators.random_spheres(shape=[100, 100], r=10, seed=seed)
ax[1].axis(False)
ax[1].set_title(f'seed = {seed}')
ax[1].imshow(im2, origin='lower', interpolation='none')

seed = 2
im2 = ps.generators.random_spheres(shape=[100, 100], r=10, seed=seed)
ax[2].axis(False)
ax[2].set_title(f'seed = {seed}')
ax[2].imshow(im2, origin='lower', interpolation='none');
../../../_images/6a439a279b5039ac993477a317df0fb0204b183ab1e40871c6003d6c8dca90a8.png

value#

The default behavior is to insert spheres with values of 0 or False, but this can be specified using the value argument. This is useful when inserting several different sizes of spheres sequentially:

fig, ax = plt.subplots(1, 2, figsize=[8, 4])

value = 2
im1 = ps.generators.random_spheres(shape=[100, 100], r=10, value=value)
im1 = ps.generators.random_spheres(im=im1, r=5, value=value+2, protrusion=-2)
h = ax[0].imshow(im1, origin='lower', interpolation='none')
ax[0].axis(False)
ax[0].set_title(f'value = {value} and {value + 2}')
fig.colorbar(h)

value = -2
im2 = ps.generators.random_spheres(shape=[100, 100], r=10, value=value)
im2 = ps.generators.random_spheres(im=im2, r=5, value=value-2, protrusion=-2)
h = ax[1].imshow(im2, origin='lower', interpolation='none')
ax[1].axis(False)
ax[1].set_title(f'value = {value} and {value - 2}')
fig.colorbar(h);
../../../_images/1ea7cb48d4f8a7bee4349c3d3b659949653f0ad0a8d8a9c8ecb37881c1b592e6.png