cylinders

This creates a pile of overlapping cylinders, resembling fibrous mats.

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

The arguments and defaults for this function can be listed as follows:

inspect.signature(ps.generators.cylinders)
<Signature (shape: List[int], r: int, ncylinders: int = None, porosity: float = None, phi_max: float = 0, theta_max: float = 90, length: float = None, maxiter: int = 3, seed=None)>

shape

The dimension of the image to create. This must be 3D since 2D overlapping cylinders don’t make much physical sense.

im = ps.generators.cylinders(shape=[250, 250, 250], r=8, ncylinders=100)

fig, ax = plt.subplots(1, 1, figsize=[6, 6])
ax.imshow(ps.visualization.show_planes(~im))
ax.axis(False);
../../../_images/2d797271db8c0d694bc5cd600c672ba5c6841c2fa33cde09da0f7f7b6ae84942.png

r

The radius of the cylinders to add

fig, ax = plt.subplots(1, 2, figsize=[12, 6])

r = 8
im = ps.generators.cylinders(shape=[200, 200, 200], r=r, ncylinders=100)
ax[0].imshow(ps.visualization.sem(im, axis=2), cmap=plt.cm.bone)
ax[0].axis(False)

r = 16
im = ps.generators.cylinders(shape=[200, 200, 200], r=r, ncylinders=100)

ax[1].imshow(ps.visualization.sem(im, axis=2), cmap=plt.cm.bone)
ax[1].axis(False);
../../../_images/59edfa94f8a256ac0b6209601bbd8ee8779856c17f4c089e37988b89cb81aa20.png

ncylinders

Directly controls the number of cylinders that are added:

fig, ax = plt.subplots(1, 2, figsize=[12, 6])

r = 8
ncylinders = 25
im = ps.generators.cylinders(shape=[200, 200, 200], r=r, ncylinders=ncylinders)
ax[0].imshow(ps.visualization.sem(im, axis=2), cmap=plt.cm.bone)
ax[0].axis(False)

ncylinders = 100
im = ps.generators.cylinders(shape=[200, 200, 200], r=r, ncylinders=ncylinders)

ax[1].imshow(ps.visualization.sem(im, axis=2), cmap=plt.cm.bone)
ax[1].axis(False);
../../../_images/207737ab98e7858046aa20af74a653949b8e88f06837b6a1aa046b296cb943ae.png

porosity

Instead of specifying the number of cylinders, you can optional request a porosity which the function attempts to match iteratively:

fig, ax = plt.subplots(1, 2, figsize=[12, 6])

r = 8
porosity = 0.4
im = ps.generators.cylinders(shape=[200, 200, 200], r=r, porosity=porosity)
ax[0].imshow(ps.visualization.sem(im, axis=2), cmap=plt.cm.bone)
ax[0].axis(False)
ax[0].set_title(f'Actual porosity is {im.sum()/im.size}')

porosity = 0.8
im = ps.generators.cylinders(shape=[200, 200, 200], r=r, porosity=porosity)

ax[1].imshow(ps.visualization.sem(im, axis=2), cmap=plt.cm.bone)
ax[1].axis(False)
ax[1].set_title(f'Actual porosity is {im.sum()/im.size}');
../../../_images/85cdfa017e2421b31fff398a24f47a0adebe967838f167ebe58c81f0a4bc3823.png

phi_max and theta_max

Controls the amount of random rotation of the fibers. Orientations will be chosen randomly from between 0 and the given values.

fig, ax = plt.subplots(1, 2, figsize=[12, 6])

im = ps.generators.cylinders(shape=[100, 100, 100], r=5, ncylinders=100, phi_max=0)
ax[0].imshow(ps.visualization.sem(im, axis=0))
ax[0].axis(False)

im = ps.generators.cylinders(shape=[100, 100, 100], r=5, ncylinders=100, phi_max=90)

ax[1].imshow(ps.visualization.sem(im, axis=0))
ax[1].axis(False);
../../../_images/5a8d83373264a7fd647a656605d1441b37523f47053f9399dda21cbc0fc580d3.png
fig, ax = plt.subplots(1, 2, figsize=[12, 6])

im = ps.generators.cylinders(shape=[100, 100, 100], r=5, ncylinders=100, theta_max=10)
ax[0].imshow(ps.visualization.sem(im, axis=2))
ax[0].axis(False)

im = ps.generators.cylinders(shape=[100, 100, 100], r=5, ncylinders=100, theta_max=90)

ax[1].imshow(ps.visualization.sem(im, axis=2))
ax[1].axis(False);
../../../_images/b3add4f09c68674a52cb049103a408f122aba78d218be9f1ad48ab036bf63c7a.png

length

Controls the length of the fibers. By default they always extend to the image edges, but they can be shortened.

fig, ax = plt.subplots(1, 2, figsize=[12, 6])

im = ps.generators.cylinders(shape=[100, 100, 100], r=5, ncylinders=100, length=10)
ax[0].imshow(ps.visualization.sem(im, axis=2))
ax[0].axis(False)

im = ps.generators.cylinders(shape=[100, 100, 100], r=5, ncylinders=100, length=50)

ax[1].imshow(ps.visualization.sem(im, axis=2))
ax[1].axis(False);
../../../_images/f7b1ed8091aa486d112dbc12ee8a5d0d4806600a2189f6df899533078e9b77cb.png

maxiter

Controls how many iterations the function uses to match the requested porosity. If ncylinders is given instead of porosity, then maxiter is ignored. The default is 3, which is usually fine.

fig, ax = plt.subplots(1, 2, figsize=[12, 6])

porosity = 0.5
im = ps.generators.cylinders(shape=[200, 200, 200], r=8, porosity=porosity, maxiter=2)
ax[0].imshow(ps.visualization.sem(im, axis=2), cmap=plt.cm.bone)
ax[0].axis(False)
ax[0].set_title(f'Actual porosity is {im.sum()/im.size}')

im = ps.generators.cylinders(shape=[200, 200, 200], r=8, porosity=porosity, maxiter=5)

ax[1].imshow(ps.visualization.sem(im, axis=2), cmap=plt.cm.bone)
ax[1].axis(False)
ax[1].set_title(f'Actual porosity is {im.sum()/im.size}');
../../../_images/946dd8825b586af8a95909a666ec1b385732aae148a68af852fd78d25ed34e4a.png