Poisson disc for periodic situation

This commit is contained in:
Martin Diehl 2020-09-24 21:26:16 +02:00
parent ec23ab8b61
commit e5b414419a
2 changed files with 17 additions and 12 deletions

View File

@ -8,7 +8,7 @@ from . import grid_filters
def from_random(size,N_seeds,grid=None,seed=None): def from_random(size,N_seeds,grid=None,seed=None):
""" """
Random seeding in space. Random seeding in space.
Parameters Parameters
---------- ----------
size : numpy.ndarray of shape (3) size : numpy.ndarray of shape (3)
@ -21,7 +21,7 @@ def from_random(size,N_seeds,grid=None,seed=None):
seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional
A seed to initialize the BitGenerator. Defaults to None. A seed to initialize the BitGenerator. Defaults to None.
If None, then fresh, unpredictable entropy will be pulled from the OS. If None, then fresh, unpredictable entropy will be pulled from the OS.
""" """
rng = _np.random.default_rng(seed) rng = _np.random.default_rng(seed)
if grid is None: if grid is None:
@ -31,13 +31,13 @@ def from_random(size,N_seeds,grid=None,seed=None):
coords = grid_coords[rng.choice(_np.prod(grid),N_seeds, replace=False)] \ coords = grid_coords[rng.choice(_np.prod(grid),N_seeds, replace=False)] \
+ _np.broadcast_to(size/grid,(N_seeds,3))*(rng.random((N_seeds,3))*.5-.25) # wobble without leaving grid + _np.broadcast_to(size/grid,(N_seeds,3))*(rng.random((N_seeds,3))*.5-.25) # wobble without leaving grid
coords return coords
def from_Poisson_disc(size,N_seeds,N_candidates,distance,seed=None): def from_Poisson_disc(size,N_seeds,N_candidates,distance,periodic=True,seed=None):
""" """
Seeding in space according to a Poisson disc distribution. Seeding in space according to a Poisson disc distribution.
Parameters Parameters
---------- ----------
size : numpy.ndarray of shape (3) size : numpy.ndarray of shape (3)
@ -48,10 +48,12 @@ def from_Poisson_disc(size,N_seeds,N_candidates,distance,seed=None):
Number of candidates to consider for finding best candidate. Number of candidates to consider for finding best candidate.
distance : float distance : float
Minimum acceptable distance to other seeds. Minimum acceptable distance to other seeds.
periodic : boolean, optional
Calculate minimum distance for periodically repeated grid.
seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional
A seed to initialize the BitGenerator. Defaults to None. A seed to initialize the BitGenerator. Defaults to None.
If None, then fresh, unpredictable entropy will be pulled from the OS. If None, then fresh, unpredictable entropy will be pulled from the OS.
""" """
rng = _np.random.default_rng(seed) rng = _np.random.default_rng(seed)
coords = _np.empty((N_seeds,3)) coords = _np.empty((N_seeds,3))
@ -61,7 +63,8 @@ def from_Poisson_disc(size,N_seeds,N_candidates,distance,seed=None):
progress = util._ProgressBar(N_seeds+1,'',50) progress = util._ProgressBar(N_seeds+1,'',50)
while i < N_seeds: while i < N_seeds:
candidates = rng.random((N_candidates,3))*_np.broadcast_to(size,(N_candidates,3)) candidates = rng.random((N_candidates,3))*_np.broadcast_to(size,(N_candidates,3))
tree = _spatial.cKDTree(coords[:i]) tree = _spatial.cKDTree(coords[:i],boxsize=size) if periodic else \
_spatial.cKDTree(coords[:i])
distances, dev_null = tree.query(candidates) distances, dev_null = tree.query(candidates)
best = distances.argmax() best = distances.argmax()
if distances[best] > distance: # require minimum separation if distances[best] > distance: # require minimum separation
@ -75,12 +78,12 @@ def from_Poisson_disc(size,N_seeds,N_candidates,distance,seed=None):
def from_geom(geom,selection=None,invert=False): def from_geom(geom,selection=None,invert=False):
""" """
Create seed from existing geometry description. Create seed from existing geometry description.
Parameters Parameters
---------- ----------
geom : damask.Geom geom : damask.Geom
... ...
""" """
material = geom.materials.reshape((-1,1),order='F') material = geom.materials.reshape((-1,1),order='F')
mask = _np.full(geom.grid.prod(),True,dtype=bool) if selection is None else \ mask = _np.full(geom.grid.prod(),True,dtype=bool) if selection is None else \

View File

@ -13,11 +13,13 @@ class TestSeeds:
coords = seeds.from_random(size,N_seeds,grid) coords = seeds.from_random(size,N_seeds,grid)
assert (0<=coords).all() and (coords<size).all() assert (0<=coords).all() and (coords<size).all()
def test_from_Poisson_disc(self): @pytest.mark.parametrize('periodic',[True,False])
def test_from_Poisson_disc(self,periodic):
N_seeds = np.random.randint(30,300) N_seeds = np.random.randint(30,300)
N_candidates = N_seeds//15 N_candidates = N_seeds//15
distance = np.random.random() distance = np.random.random()
size = np.ones(3)*distance*N_seeds size = np.ones(3)*distance*N_seeds
coords = seeds.from_Poisson_disc(size,N_seeds,N_candidates,distance) coords = seeds.from_Poisson_disc(size,N_seeds,N_candidates,distance,periodic=periodic)
min_dists, _ = cKDTree(coords).query(coords, 2) min_dists, _ = cKDTree(coords,boxsize=size).query(coords, 2) if periodic else \
cKDTree(coords).query(coords, 2)
assert (0<= coords).all() and (coords<size).all() and np.min(min_dists[:,1])>=distance assert (0<= coords).all() and (coords<size).all() and np.min(min_dists[:,1])>=distance