Skip to content

Commit

Permalink
adding 3 examples, and touchups to docstrings
Browse files Browse the repository at this point in the history
  • Loading branch information
jgostick committed Feb 4, 2024
1 parent 6440ad1 commit 9d7a9fe
Show file tree
Hide file tree
Showing 6 changed files with 1,063 additions and 18 deletions.
320 changes: 320 additions & 0 deletions examples/generators/reference/cylindrical_pillars_array.ipynb

Large diffs are not rendered by default.

311 changes: 311 additions & 0 deletions examples/generators/reference/cylindrical_pillars_mesh.ipynb

Large diffs are not rendered by default.

320 changes: 320 additions & 0 deletions examples/generators/reference/rectangular_pillars_array.ipynb

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions porespy/generators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@
from ._spheres_from_coords import *
from ._borders import *
from ._fractals import *
from ._micromodels import *
87 changes: 69 additions & 18 deletions porespy/generators/_micromodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@
import matplotlib.pyplot as plt
from nanomesh import Mesher2D
from porespy.generators import lattice_spheres, borders, spheres_from_coords
from porespy.tools import _insert_disks_at_points_parallel
from porespy.tools import extend_slice, extract_subsection
from porespy.tools import _insert_disks_at_points_parallel, extend_slice
import scipy.ndimage as spim
import scipy.stats as spst
from typing import List


__all__ = [
'rectangular_pillar_array',
'cylindrical_pillar_array',
'random_cylindrical_pillars',
'rectangular_pillars_array',
'cylindrical_pillars_array',
'cylindrical_pillars_mesh',
]


Expand Down Expand Up @@ -42,7 +41,7 @@ def _extract(im, shape, spacing, truncate, lattice):
return im


def rectangular_pillar_array(
def rectangular_pillars_array(
shape: List,
spacing: int = 40,
dist: str = 'uniform',
Expand All @@ -52,6 +51,14 @@ def rectangular_pillar_array(
seed: int = None,
):
r"""
A 2D micromodel with rectangular pillars positioned on a lattice
The model is generated by inserting rectangular sections of different widths
between each pair of points. The size of pillars is controlled indirectly by
the size of the openings.
Parameters
----------
shape : array_like
The X, Y size of the desired image in pixels
spacing : int
Expand Down Expand Up @@ -92,6 +99,12 @@ def rectangular_pillar_array(
-------
im : ndarray
An `ndarray` with `True` values indicating the void space.
Examples
--------
`Click here
<https://porespy.org/examples/generators/reference/rectangular_pillars_array.html>`_
to view online example.
"""
if seed is not None:
np.random.seed(seed)
Expand Down Expand Up @@ -124,7 +137,7 @@ def rectangular_pillar_array(
return tmp


def cylindrical_pillar_array(
def cylindrical_pillars_array(
shape: List,
spacing: int = 40,
dist: str = 'uniform',
Expand All @@ -134,6 +147,14 @@ def cylindrical_pillar_array(
seed: int = None,
):
r"""
A 2D micromodel with cylindrical pillars positioned on a lattice
The model is generated by inserting disks of different size at each point in
the lattice. The size of the openings between pillars is controlled indirectly
by the size of the pillars.
Parameters
----------
shape : array_like
The X, Y size of the desired image in pixels
spacing : int
Expand Down Expand Up @@ -174,6 +195,12 @@ def cylindrical_pillar_array(
-------
im : ndarray
An `ndarray` with `True` values indicating the void space.
Examples
--------
`Click here
<https://porespy.org/examples/generators/reference/cylindrical_pillars_array.html>`_
to view online example.
"""
if seed is not None:
np.random.seed(seed)
Expand Down Expand Up @@ -202,7 +229,7 @@ def cylindrical_pillar_array(
return tmp


def random_cylindrical_pillars(
def cylindrical_pillars_mesh(
shape: list,
f: float = 0.75,
a: int = 1000,
Expand All @@ -213,6 +240,10 @@ def random_cylindrical_pillars(
r"""
A 2D micromodel with randomly located cylindrical pillars of random radius
The model is generated by inserting disks at each corner of a triangular mesh
(generated using `nanomesh`). The size of the disks is a fraction of the
maximally inscribed disk at each location.
Parameter
---------
shape : array_like
Expand All @@ -227,7 +258,21 @@ def random_cylindrical_pillars(
minimum area for each triangle in the mesh.
n : scalar
Controls the distance between pillars on the edges. By default it uses
$\sqrt{a}/f$, but it can be adjusted as needed.
$\sqrt{a}/f$, but it can be overwritten using this argument if needed.
truncate : bool
A flag to indicate if the output should be truncated to the given `shape`
or if the returned image should be expanded to include the full boundary
pillars. The default is `True`.
seed : int
The value to initialize numpy's random number generator. The default is
`None` which results in a new realization each time this function is called.
Examples
--------
`Click here
<https://porespy.org/examples/generators/reference/cylindrical_pillars_mesh.html>`_
to view online example.
"""
if seed is not None:
np.random.seed(seed)
Expand All @@ -246,6 +291,12 @@ def random_cylindrical_pillars(
# mesh.plot_pyvista(jupyter_backend='static', show_edges=True)
tri = mesh.triangle_dict

# TODO: The corners contain 2 (say A and B) points very close to each other.
# The following if statement will ignore the connection between A and B when
# checking for the maximal size; however, sometimes the max size will be between
# A and some internal point when in fact B had an internal point as a neighbor
# that was much closer, so the sphere ends up being too big. The following code
# needs to handle this better.
r_max = np.inf*np.ones([tri['vertices'].shape[0], ])
for e in tri['edges']:
L = np.sqrt(np.sum(np.diff(tri['vertices'][e], axis=0)**2))
Expand Down Expand Up @@ -280,18 +331,18 @@ def random_cylindrical_pillars(
if rect_demo:
fig, ax = plt.subplots(2, 2)
np.random.seed(0)
im1 = rectangular_pillar_array(
im1 = rectangular_pillars_array(
shape=[400, 600], spacing=40, lattice='simple', truncate=True)
im2 = rectangular_pillar_array(
im2 = rectangular_pillars_array(
shape=[400, 600], spacing=40, lattice='tri', truncate=True)

ax[0][0].imshow(im1, origin='lower', interpolation='none')
ax[0][1].imshow(im2, origin='lower', interpolation='none')

np.random.seed(0)
im1 = rectangular_pillar_array(
im1 = rectangular_pillars_array(
shape=[400, 600], spacing=40, lattice='simple', truncate=False)
im2 = rectangular_pillar_array(
im2 = rectangular_pillars_array(
shape=[400, 600], spacing=40, lattice='tri', truncate=False)

ax[1][0].imshow(im1, origin='lower', interpolation='none')
Expand All @@ -300,23 +351,23 @@ def random_cylindrical_pillars(
if cyl_demo:
fig, ax = plt.subplots(2, 2)
np.random.seed(0)
im1 = cylindrical_pillar_array(
im1 = cylindrical_pillars_array(
shape=[400, 600], spacing=40, lattice='simple', truncate=True)
im2 = cylindrical_pillar_array(
im2 = cylindrical_pillars_array(
shape=[400, 600], spacing=40, lattice='tri', truncate=True)
ax[0][0].imshow(im1, origin='lower', interpolation='none')
ax[0][1].imshow(im2, origin='lower', interpolation='none')

np.random.seed(0)
im1 = cylindrical_pillar_array(
im1 = cylindrical_pillars_array(
shape=[400, 600], spacing=40, lattice='simple', truncate=False)
im2 = cylindrical_pillar_array(
im2 = cylindrical_pillars_array(
shape=[400, 600], spacing=40, lattice='tri', truncate=False)
ax[1][0].imshow(im1, origin='lower', interpolation='none')
ax[1][1].imshow(im2, origin='lower', interpolation='none')

if rand_cyl:
im = random_cylindrical_pillars(
im = cylindrical_pillars_mesh(
shape=[1000, 500],
n=40,
)
Expand Down
42 changes: 42 additions & 0 deletions porespy/tools/_sphere_insertions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,51 @@
'_insert_disks_at_points',
'_insert_disks_at_points_serial',
'_insert_disks_at_points_parallel',
'points_to_spheres',
]


def points_to_spheres(im):
r"""
Inserts disks/spheres into an image at locations indicated by non-zero values
Parameters
----------
im : ndarray
The image containing nonzeros indicating the locations to insert spheres.
If the non-zero values are `bool`, then the maximal size is found and used;
if the non-zeros are `int` then these values are used as the radii.
Returns
-------
spheres : ndarray
A `bool` array with disks/spheres inserted at each nonzero location in `im`.
"""
from scipy.spatial import distance_matrix
if im.ndim == 3:
x, y, z = np.where(im > 0)
coords = np.vstack((x, y, z)).T
else:
x, y = np.where(im > 0)
coords = np.vstack((x, y))
if im.dtype == bool:
dmap = distance_matrix(coords.T, coords.T)
mask = dmap < 1
dmap[mask] = np.inf
r = np.around(dmap.min(axis=0)/2, decimals=0).astype(int)
else:
r = im[x, y].flatten()
im_spheres = np.zeros_like(im, dtype=bool)
im_spheres = _insert_disks_at_points_parallel(
im_spheres,
coords=coords,
radii=r,
v=True,
smooth=False,
)
return im_spheres


@njit(parallel=True)
def _insert_disks_at_points_parallel(im, coords, radii, v, smooth=True,
overwrite=False): # pragma: no cover
Expand Down

0 comments on commit 9d7a9fe

Please sign in to comment.