diff --git a/src/porespy/generators/_imgen.py b/src/porespy/generators/_imgen.py index af0bcecc7..3b90b6646 100644 --- a/src/porespy/generators/_imgen.py +++ b/src/porespy/generators/_imgen.py @@ -232,7 +232,7 @@ def random_spheres( edges: Literal['contained', 'extended'] = 'contained', smooth: bool = True, seed: int = None, - value: int = False, + value: int = True, ): r""" Generates a sphere or disk packing using random sequential addition as @@ -242,9 +242,9 @@ def random_spheres( ---------- shape : list The shape of the image to create. This is equivalent to passing an array - of `True` values of the desired size to `im`. + of `False` values of the desired size to `im`. im : ndarray - Image with values > 0 indicating the voxels where spheres should be + Image with `False` indicating the voxels where spheres should be inserted. This can be used to insert spheres into an image that already has some features (e.g. half filled with larger spheres, or a cylindrical plug). @@ -291,14 +291,14 @@ def random_spheres( function so it can be initialized the way ``numba`` requires. The default is ``None``, which means each call will produce a new realization. value : scalar - The value to set the inserted spheres to. Using `value < 0` is a handy + The value to set the inserted spheres to. Using `value > 1` is a handy way to repeatedly insert different sphere sizes into the same image while making them easy to identify. Returns ------- image : ndarray - An image with spheres of specified radius *added* to the foreground. + An image with spheres of specified radius *added* to the background. See Also -------- @@ -310,7 +310,7 @@ def random_spheres( This algorithm ensures that spheres do not overlap but does not guarantee they are tightly packed. - This function adds spheres to the foreground of the received ``im``, which + This function adds spheres to the background of the received ``im``, which allows iteratively adding spheres of different radii to the unfilled space by repeatedly passing in the result of previous calls to the function. @@ -333,10 +333,10 @@ def random_spheres( if (im is None) and (shape is not None): # If shape was given, generate empty im if np.ndim(shape) > 1: raise Exception('shape must be a list like [Nx, Ny] or [Nx, Ny, Nz]') - im = np.ones(shape, dtype=type(value)) + im = np.zeros(shape, dtype=type(value)) options_im = np.ones_like(im, dtype=bool) elif (shape is None) and (im is not None): # Otherwise use im - options_im = edt(im == 1) >= (r - protrusion) + options_im = edt(im == 0) >= (r - protrusion) im = np.copy(im).astype(type(value)) else: raise Exception('Must specify either im or shape') diff --git a/src/porespy/generators/_pseudo_packings.py b/src/porespy/generators/_pseudo_packings.py index ae4a610d0..cba9357cb 100644 --- a/src/porespy/generators/_pseudo_packings.py +++ b/src/porespy/generators/_pseudo_packings.py @@ -41,7 +41,7 @@ def _random_spheres2( phi: float = 1.0, seed: float = None, smooth: bool = True, - value: int = 0, + value: int = 1, ) -> np.ndarray: r""" This is an alternative implementation of random_spheres that uses the same @@ -54,14 +54,14 @@ def _random_spheres2( np.random.seed(seed) # Also initialize numpys rng if im is None: # If shape is given instead of im - im = np.ones(shape, dtype=bool) + im = np.zeros(shape, dtype=bool) im = np.swapaxes(im, 0, axis) # Move "axis" to x position for processing if smooth: # Smooth balls are 1 pixel smaller, so increase r r = r + 1 - mask = im == 1 # Find mask of valid points + mask = im == 0 # Find mask of valid points if protrusion > 0: # Dilate mask dt = edt(~mask) mask = dt <= protrusion @@ -111,7 +111,7 @@ def pseudo_gravity_packing( phi: float = 1.0, seed: int = None, smooth: bool = True, - value: int = 0, + value: int = 1, ) -> np.ndarray: r""" Iteratively inserts spheres at the lowest accessible point in an image, @@ -121,9 +121,9 @@ def pseudo_gravity_packing( ---------- shape : list The shape of the image to create. This is equivalent to passing an array - of `True` values of the desired size to `im`. + of `False` values of the desired size to `im`. im : ndarray - Image with values > 0 indicating the voxels where spheres should be + Image with `False` indicating the voxels where spheres should be inserted. This can be used to insert spheres into an image that already has some features (e.g. half filled with larger spheres, or a cylindrical plug). @@ -169,10 +169,10 @@ def pseudo_gravity_packing( is ``None``, which means each call will produce a new realization. smooth : bool, default = `True` Controls whether or not the spheres have the small pip each face. - value : int, default = 0 - The value of insert for the spheres. The default is 0, which puts solid - inclusions into to the foreground. Values other than 1 (Foreground) make - it easy to add spheres repeated and identify which were added on each step. + value : int, default = 1 + The value of insert for the spheres. The default is 1, which puts holes into + the background. Values other than 1 make it easy to add spheres repeatedly + and identify which were added on each step. Returns ------- @@ -195,7 +195,7 @@ def pseudo_gravity_packing( np.random.seed(seed) if im is None: # If shape was given, generate empty im - im = np.ones(shape, dtype=bool) + im = np.zeros(shape, dtype=bool) im = np.swapaxes(im, 0, axis) # Move specified axis to 0 position @@ -203,7 +203,7 @@ def pseudo_gravity_packing( r = r + 1 # Shrink or grow mask to allow for clearance - mask = im == 1 # Find mask of valid points + mask = im == 0 # Find mask of valid points if protrusion > 0: # Dilate mask dt = edt(~mask) mask = dt <= protrusion @@ -265,7 +265,7 @@ def pseudo_electrostatic_packing( seed: int = None, smooth: bool = True, compactness: float = 1.0, - value: int = 0, + value: int = 1, ): r""" Iterativley inserts spheres as close to the given sites as possible. @@ -274,9 +274,9 @@ def pseudo_electrostatic_packing( ---------- shape : list The shape of the image to create. This is equivalent to passing an array - of `True` values of the desired size to `im`. + of `False` values of the desired size to `im`. im : ndarray - Image with values > 0 indicating the voxels where spheres should be + Image with `False` indicating the voxels where spheres should be inserted. This can be used to insert spheres into an image that already has some features (e.g. half filled with larger spheres, or a cylindrical plug). @@ -325,9 +325,9 @@ def pseudo_electrostatic_packing( Controls how tightly the spheres are grouped together. A value of 1.0 (default) results in the tighest possible grouping while values < 1.0 give more loosely or imperfectly packed spheres. - value : int, default = 0 - The value of insert for the spheres. The default is 0, which puts solid - inclusions into to the foreground. Values other than 1 (Foreground) make + value : int, default = 1 + The value of insert for the spheres. The default is 1, which puts holes + into to the background. Values other than 1 (Foreground) make it easy to add spheres repeated and identify which were added on each step. Returns @@ -347,12 +347,12 @@ def pseudo_electrostatic_packing( np.random.seed(seed) if im is None: # If shape was given, generate empty im - im = np.ones(shape, dtype=bool) + im = np.zeros(shape, dtype=bool) if smooth: # If smooth spheres are used, increase r to compensate r = r + 1 - mask = im > 0 # Find mask of valid points + mask = im == 0 # Find mask of valid points if protrusion > 0: # Dilate mask dt = edt(~mask) mask = dt <= protrusion @@ -473,7 +473,7 @@ def _do_packing(im, mask, q, r, value, clearance, smooth, maxiter): if 1: blobs = ps.generators.blobs([400, 400], porosity=0.75, seed=0) im = ps.generators.pseudo_electrostatic_packing( - im=blobs, + im=~blobs, r=10, clearance=0, protrusion=0, @@ -484,7 +484,7 @@ def _do_packing(im, mask, q, r, value, clearance, smooth, maxiter): value=2, ) im2 = ps.generators.pseudo_electrostatic_packing( - im=im == 1, + im=im, sites=im == 2, r=5, clearance=1, @@ -564,6 +564,6 @@ def _do_packing(im, mask, q, r, value, clearance, smooth, maxiter): edges='contained', seed=0, smooth=True, - value=4 + value=1 ) ax[1][1].imshow(im, origin='lower') diff --git a/test/unit/test_generators.py b/test/unit/test_generators.py index e05795346..dd2d1bf25 100644 --- a/test/unit/test_generators.py +++ b/test/unit/test_generators.py @@ -265,59 +265,59 @@ def test_blobs_w_divs(self): assert np.all(im1 == im2) def test_random_spheres_2d_contained(self): - im = np.ones([100, 100], dtype=int) + im = np.zeros([100, 100], dtype=int) im = ps.generators.random_spheres( im=im, r=10, phi=0.5, edges='contained') - im = np.pad(im, pad_width=1, mode='constant', constant_values=True) - lt = ps.filters.local_thickness(~im, sizes=range(1, 20)) + im = np.pad(im, pad_width=1, mode='constant', constant_values=False) + lt = ps.filters.local_thickness(im > 0, sizes=range(1, 20)) assert len(np.unique(lt)) == 2 def test_random_spheres_2d_extended(self): - im = np.ones([100, 100], dtype=int) + im = np.zeros([100, 100], dtype=int) im = ps.generators.random_spheres( im=im, r=10, phi=0.5, edges='extended') - im = np.pad(im, pad_width=1, mode='constant', constant_values=True) - lt = ps.filters.local_thickness(~im, sizes=range(1, 20)) + im = np.pad(im, pad_width=1, mode='constant', constant_values=False) + lt = ps.filters.local_thickness(im > 0, sizes=range(1, 20)) assert len(np.unique(lt)) > 2 def test_random_spheres_2d_extended_with_clearance(self): - im = np.ones([100, 100], dtype=int) + im = np.zeros([100, 100], dtype=int) im = ps.generators.random_spheres( im=im, r=10, phi=0.5, clearance=2, edges='extended') - im = np.pad(im, pad_width=1, mode='constant', constant_values=True) - lt = ps.filters.local_thickness(~im, sizes=range(1, 20)) + im = np.pad(im, pad_width=1, mode='constant', constant_values=False) + lt = ps.filters.local_thickness(im > 0, sizes=range(1, 20)) assert len(np.unique(lt)) > 2 def test_random_spheres_3d_contained(self): - im = np.ones([100, 100, 100], dtype=int) + im = np.zeros([100, 100, 100], dtype=int) im = ps.generators.random_spheres( im=im, r=10, phi=0.5, edges='contained', smooth=False) - im = np.pad(im, pad_width=1, mode='constant', constant_values=True) - lt = ps.filters.local_thickness(~im, sizes=[10, 9, 8, 7, 6, 5]) + im = np.pad(im, pad_width=1, mode='constant', constant_values=False) + lt = ps.filters.local_thickness(im > 0, sizes=[10, 9, 8, 7, 6, 5]) assert len(np.unique(lt)) == 2 def test_random_spheres_3d_extended(self): - im = np.ones([100, 100, 100], dtype=int) + im = np.zeros([100, 100, 100], dtype=int) im = ps.generators.random_spheres( im=im, r=10, phi=0.5, edges='extended', smooth=False) - im = np.pad(im, pad_width=1, mode='constant', constant_values=True) - lt = ps.filters.local_thickness(~im, sizes=[10, 9, 8, 7, 6, 5]) + im = np.pad(im, pad_width=1, mode='constant', constant_values=False) + lt = ps.filters.local_thickness(im > 0, sizes=[10, 9, 8, 7, 6, 5]) assert len(np.unique(lt)) > 2 def test_random_spheres_2d_seqential_additions(self): - im = np.ones([100, 100], dtype=int) + im = np.zeros([100, 100], dtype=int) im = ps.generators.random_spheres(im=im, r=10) phi1 = ps.metrics.porosity(im) im = ps.generators.random_spheres(im=im, r=5) phi2 = ps.metrics.porosity(im) - assert phi2 < phi1 + assert phi2 > phi1 def test_random_spheres_preexisting_structure(self): im = ps.generators.blobs(shape=[200, 200, 200]) phi1 = im.sum()/im.size im = ps.generators.random_spheres(im=im, r=8, maxiter=200, edges='contained') phi2 = im.sum()/im.size - assert phi2 < phi1 + assert phi2 > phi1 # Ensure that 3 passes through random_spheres fills up image im = ps.generators.random_spheres(im=im, r=8, maxiter=200, edges='contained') im = ps.generators.random_spheres(im=im, r=8, maxiter=200, edges='contained') @@ -336,19 +336,17 @@ def test_random_spheres_clearance_large_spheres(self): shape=[200, 200], r=9, clearance=0, seed=0) random_spheres2p = ps.generators.random_spheres( shape=[200, 200], r=9, clearance=3, seed=0) - assert random_spheres0.sum() < random_spheres2p.sum() + assert random_spheres0.sum() > random_spheres2p.sum() random_spheres1n = ps.generators.random_spheres( shape=[200, 200], r=9, clearance=-3, seed=0) - assert random_spheres0.sum() > random_spheres1n.sum() + assert random_spheres0.sum() < random_spheres1n.sum() def test_random_spheres_clearance_small_spheres(self): - np.random.seed(0) random_spheres0 = ps.generators.random_spheres( shape=[200, 200], r=1, clearance=0) - np.random.seed(0) random_spheres2p = ps.generators.random_spheres( shape=[200, 200], r=1, clearance=2) - assert random_spheres0.sum() < random_spheres2p.sum() + assert random_spheres0.sum() > random_spheres2p.sum() def test_random_spheres_w_seed(self): im1 = ps.generators.random_spheres(shape=[50, 50], r=5, seed=0) @@ -372,40 +370,40 @@ def test_line_segment(self): assert np.all(L3 == [5, 6, 7, 8, 9, 10, 11, 12, 13]) def test_pseudo_gravity_packing_monodisperse(self): - im = np.ones([400, 400], dtype=bool) + im = np.zeros([400, 400], dtype=bool) im = ps.generators.pseudo_gravity_packing(im=im, r=20, clearance=0, seed=0) e1 = im.sum()/im.size - im = np.ones([400, 400], dtype=bool) + im = np.zeros([400, 400], dtype=bool) im = ps.generators.pseudo_gravity_packing(im=im, r=20, clearance=5, seed=0) e2 = im.sum()/im.size - assert e2 > e1 - im = np.ones([400, 400], dtype=bool) + assert e2 < e1 + im = np.zeros([400, 400], dtype=bool) im = ps.generators.pseudo_gravity_packing(im=im, r=20, maxiter=10, seed=0) e3 = im.sum()/im.size - im = np.ones([400, 400], dtype=bool) + im = np.zeros([400, 400], dtype=bool) im = ps.generators.pseudo_gravity_packing(im=im, r=50, maxiter=10, seed=0) e4 = im.sum()/im.size - assert e3 > e4 + assert e3 < e4 def test_pseudo_gravity_packing_2D(self): - im = np.ones([100, 100], dtype=bool) + im = np.zeros([100, 100], dtype=bool) im = ps.generators.pseudo_gravity_packing( im=im, r=8, clearance=1, seed=0) - assert im.sum() == 4024 + assert im.sum() == 5976 def test_pseudo_gravity_packing_3D(self): - im = np.ones([100, 100, 100], dtype=bool) + im = np.zeros([100, 100, 100], dtype=bool) im = ps.generators.pseudo_gravity_packing( im=im, r=8, clearance=1, seed=0) - assert im.sum() == 605123 # Used to be 279240 + assert im.sum() == 394877 def test_pseudo_gravity_packing_w_seed(self): im1 = ps.generators.pseudo_gravity_packing( - im=np.ones([50, 50], dtype=bool), r=5, seed=0) + shape=[50, 50], r=5, seed=0) im2 = ps.generators.pseudo_gravity_packing( - im=np.ones([50, 50], dtype=bool), r=5, seed=0) + shape=[50, 50], r=5, seed=0) im3 = ps.generators.pseudo_gravity_packing( - im=np.ones([50, 50], dtype=bool), r=5, seed=1) + shape=[50, 50], r=5, seed=1) assert np.all(im1 == im2) assert not np.all(im1 == im3) @@ -413,24 +411,24 @@ def test_pseudo_electrostatic_packing(self): im1 = ps.generators.blobs(shape=[100, 100]) im2 = ps.generators.pseudo_electrostatic_packing( im=im1, r=3, clearance=1, protrusion=1) - assert (im1.sum() > im2.sum()) + assert (im2.sum() > im1.sum()) assert im2.sum() > 0 def test_pseudo_electrostatic_packing_2D(self): - im = np.ones([100, 100], dtype=bool) + im = np.zeros([100, 100], dtype=bool) sites = np.zeros_like(im) sites[50, 50] = True im = ps.generators.pseudo_electrostatic_packing( im=im, r=8, sites=sites, maxiter=10, seed=0) - assert im.sum() == 7510 + assert im.sum() == 2490 def test_pseudo_electrostatic_packing_3D(self): - im = np.ones([100, 100, 100], dtype=bool) + im = np.zeros([100, 100, 100], dtype=bool) sites = np.zeros_like(im) sites[50, 50, 50] = True im = ps.generators.pseudo_electrostatic_packing( im=im, r=8, sites=sites, maxiter=10, seed=0) - assert im.sum() == 970310 # Used to be 21030 + assert im.sum() == 29690 def test_pseudo_electrostatic_packing_w_seed(self): im1 = ps.generators.pseudo_electrostatic_packing(