2020-11-16 05:42:23 +05:30
|
|
|
|
"""Functionality for generation of seed points for Voronoi or Laguerre tessellation."""
|
2020-11-16 03:44:46 +05:30
|
|
|
|
|
2022-11-23 02:56:15 +05:30
|
|
|
|
from typing import Optional as _Optional, Tuple as _Tuple
|
2021-11-02 22:31:32 +05:30
|
|
|
|
|
2021-11-03 12:23:38 +05:30
|
|
|
|
from scipy import spatial as _spatial
|
2020-09-23 13:15:36 +05:30
|
|
|
|
import numpy as _np
|
|
|
|
|
|
2022-02-27 20:46:09 +05:30
|
|
|
|
from ._typehints import FloatSequence as _FloatSequence, IntSequence as _IntSequence, \
|
2022-12-14 00:02:19 +05:30
|
|
|
|
NumpyRngSeed as _NumpyRngSeed
|
2021-05-06 18:30:03 +05:30
|
|
|
|
from . import util as _util
|
|
|
|
|
from . import grid_filters as _grid_filters
|
2020-09-23 13:15:36 +05:30
|
|
|
|
|
|
|
|
|
|
2022-01-27 15:15:14 +05:30
|
|
|
|
def from_random(size: _FloatSequence,
|
|
|
|
|
N_seeds: int,
|
2022-11-23 02:56:15 +05:30
|
|
|
|
cells: _Optional[_IntSequence] = None,
|
|
|
|
|
rng_seed: _Optional[_NumpyRngSeed] = None) -> _np.ndarray:
|
2020-09-23 13:15:36 +05:30
|
|
|
|
"""
|
2021-06-15 20:32:02 +05:30
|
|
|
|
Place seeds randomly in space.
|
2020-09-25 00:56:16 +05:30
|
|
|
|
|
2020-09-23 13:15:36 +05:30
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2022-01-12 18:48:38 +05:30
|
|
|
|
size : sequence of float, len (3)
|
2023-02-21 20:57:06 +05:30
|
|
|
|
Edge lengths of the seeding domain.
|
2020-09-23 13:15:36 +05:30
|
|
|
|
N_seeds : int
|
|
|
|
|
Number of seeds.
|
2022-01-12 18:48:38 +05:30
|
|
|
|
cells : sequence of int, len (3), optional.
|
2020-12-08 05:06:41 +05:30
|
|
|
|
If given, ensures that each seed results in a grain when a standard Voronoi
|
|
|
|
|
tessellation is performed using the given grid resolution (i.e. size/cells).
|
2020-11-15 17:36:26 +05:30
|
|
|
|
rng_seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional
|
2020-09-23 13:15:36 +05:30
|
|
|
|
A seed to initialize the BitGenerator. Defaults to None.
|
|
|
|
|
If None, then fresh, unpredictable entropy will be pulled from the OS.
|
2020-09-25 00:56:16 +05:30
|
|
|
|
|
2021-04-23 22:50:07 +05:30
|
|
|
|
Returns
|
|
|
|
|
-------
|
2022-01-12 18:48:38 +05:30
|
|
|
|
coords : numpy.ndarray, shape (N_seeds,3)
|
2021-04-24 21:30:57 +05:30
|
|
|
|
Seed coordinates in 3D space.
|
2021-04-23 22:50:07 +05:30
|
|
|
|
|
2020-09-23 13:15:36 +05:30
|
|
|
|
"""
|
2022-01-12 18:48:38 +05:30
|
|
|
|
size_ = _np.array(size,float)
|
2020-11-15 17:36:26 +05:30
|
|
|
|
rng = _np.random.default_rng(rng_seed)
|
2020-12-04 02:28:24 +05:30
|
|
|
|
if cells is None:
|
2022-01-12 18:48:38 +05:30
|
|
|
|
coords = rng.random((N_seeds,3)) * size_
|
2020-09-23 13:15:36 +05:30
|
|
|
|
else:
|
2021-05-06 18:30:03 +05:30
|
|
|
|
grid_coords = _grid_filters.coordinates0_point(cells,size).reshape(-1,3,order='F')
|
2020-12-04 02:28:24 +05:30
|
|
|
|
coords = grid_coords[rng.choice(_np.prod(cells),N_seeds, replace=False)] \
|
2022-06-02 23:10:18 +05:30
|
|
|
|
+ _np.broadcast_to(size_/_np.array(cells,_np.int64),(N_seeds,3))*(rng.random((N_seeds,3))*.5-.25) # wobble w/o leaving grid
|
2020-09-23 13:15:36 +05:30
|
|
|
|
|
2020-09-25 00:56:16 +05:30
|
|
|
|
return coords
|
2020-09-23 13:15:36 +05:30
|
|
|
|
|
|
|
|
|
|
2022-01-27 15:15:14 +05:30
|
|
|
|
def from_Poisson_disc(size: _FloatSequence,
|
|
|
|
|
N_seeds: int,
|
|
|
|
|
N_candidates: int,
|
|
|
|
|
distance: float,
|
|
|
|
|
periodic: bool = True,
|
2022-11-23 02:56:15 +05:30
|
|
|
|
rng_seed: _Optional[_NumpyRngSeed] = None) -> _np.ndarray:
|
2020-09-23 13:15:36 +05:30
|
|
|
|
"""
|
2023-02-21 20:57:06 +05:30
|
|
|
|
Place seeds following a Poisson disc distribution.
|
2020-09-25 00:56:16 +05:30
|
|
|
|
|
2020-09-23 13:15:36 +05:30
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2022-01-12 18:48:38 +05:30
|
|
|
|
size : sequence of float, len (3)
|
2023-02-21 20:57:06 +05:30
|
|
|
|
Edge lengths of the seeding domain.
|
2020-09-23 13:15:36 +05:30
|
|
|
|
N_seeds : int
|
|
|
|
|
Number of seeds.
|
|
|
|
|
N_candidates : int
|
|
|
|
|
Number of candidates to consider for finding best candidate.
|
|
|
|
|
distance : float
|
|
|
|
|
Minimum acceptable distance to other seeds.
|
2022-01-13 03:43:38 +05:30
|
|
|
|
periodic : bool, optional
|
2020-12-07 22:19:37 +05:30
|
|
|
|
Calculate minimum distance for periodically repeated grid.
|
2023-02-21 20:57:06 +05:30
|
|
|
|
Defaults to True.
|
2020-11-15 17:36:26 +05:30
|
|
|
|
rng_seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional
|
2020-09-23 13:15:36 +05:30
|
|
|
|
A seed to initialize the BitGenerator. Defaults to None.
|
|
|
|
|
If None, then fresh, unpredictable entropy will be pulled from the OS.
|
2020-09-25 00:56:16 +05:30
|
|
|
|
|
2021-04-23 22:50:07 +05:30
|
|
|
|
Returns
|
|
|
|
|
-------
|
2022-01-12 18:48:38 +05:30
|
|
|
|
coords : numpy.ndarray, shape (N_seeds,3)
|
2021-04-24 21:30:57 +05:30
|
|
|
|
Seed coordinates in 3D space.
|
2021-04-23 22:50:07 +05:30
|
|
|
|
|
2020-09-23 13:15:36 +05:30
|
|
|
|
"""
|
2020-11-15 17:36:26 +05:30
|
|
|
|
rng = _np.random.default_rng(rng_seed)
|
2020-09-23 13:15:36 +05:30
|
|
|
|
coords = _np.empty((N_seeds,3))
|
2022-01-12 18:48:38 +05:30
|
|
|
|
coords[0] = rng.random(3) * _np.array(size,float)
|
2020-09-23 13:15:36 +05:30
|
|
|
|
|
2021-04-28 11:27:20 +05:30
|
|
|
|
s = 1
|
|
|
|
|
i = 0
|
2022-01-22 12:20:52 +05:30
|
|
|
|
progress = _util.ProgressBar(N_seeds+1,'',50)
|
2021-04-28 11:27:20 +05:30
|
|
|
|
while s < N_seeds:
|
2021-11-02 22:31:32 +05:30
|
|
|
|
i += 1
|
2020-09-23 13:15:36 +05:30
|
|
|
|
candidates = rng.random((N_candidates,3))*_np.broadcast_to(size,(N_candidates,3))
|
2021-04-28 11:27:20 +05:30
|
|
|
|
tree = _spatial.cKDTree(coords[:s],boxsize=size) if periodic else \
|
|
|
|
|
_spatial.cKDTree(coords[:s])
|
2021-04-29 12:26:40 +05:30
|
|
|
|
distances = tree.query(candidates)[0]
|
2022-01-29 22:46:19 +05:30
|
|
|
|
if distances.max() > distance: # require minimum separation
|
2021-11-02 22:31:32 +05:30
|
|
|
|
i = 0
|
2022-01-29 22:46:19 +05:30
|
|
|
|
coords[s] = candidates[distances.argmax()] # maximum separation to existing point cloud
|
2021-04-28 11:27:20 +05:30
|
|
|
|
s += 1
|
|
|
|
|
progress.update(s)
|
|
|
|
|
|
2022-02-22 21:12:05 +05:30
|
|
|
|
if i >= 100:
|
|
|
|
|
raise ValueError('seeding not possible')
|
2020-09-23 13:15:36 +05:30
|
|
|
|
|
2020-09-24 00:18:34 +05:30
|
|
|
|
return coords
|
2020-09-23 13:15:36 +05:30
|
|
|
|
|
|
|
|
|
|
2022-01-27 15:15:14 +05:30
|
|
|
|
def from_grid(grid,
|
2022-12-14 00:02:19 +05:30
|
|
|
|
selection: _Optional[_IntSequence] = None,
|
2022-01-27 15:15:14 +05:30
|
|
|
|
invert_selection: bool = False,
|
|
|
|
|
average: bool = False,
|
|
|
|
|
periodic: bool = True) -> _Tuple[_np.ndarray, _np.ndarray]:
|
2020-09-23 13:15:36 +05:30
|
|
|
|
"""
|
2021-06-15 20:32:02 +05:30
|
|
|
|
Create seeds from grid description.
|
2020-09-25 00:56:16 +05:30
|
|
|
|
|
2020-09-23 13:15:36 +05:30
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2020-12-04 11:42:18 +05:30
|
|
|
|
grid : damask.Grid
|
2021-11-02 23:11:05 +05:30
|
|
|
|
Grid from which the material IDs are used as seeds.
|
2022-12-14 00:02:19 +05:30
|
|
|
|
selection : (sequence of) int, optional
|
2020-09-25 07:29:48 +05:30
|
|
|
|
Material IDs to consider.
|
2022-01-13 03:43:38 +05:30
|
|
|
|
invert_selection : bool, optional
|
2021-11-02 23:11:05 +05:30
|
|
|
|
Consider all material IDs except those in selection. Defaults to False.
|
2022-01-13 03:43:38 +05:30
|
|
|
|
average : bool, optional
|
2020-09-25 07:29:48 +05:30
|
|
|
|
Seed corresponds to center of gravity of material ID cloud.
|
2023-02-21 20:57:06 +05:30
|
|
|
|
Defaults to False.
|
2022-01-13 03:43:38 +05:30
|
|
|
|
periodic : bool, optional
|
2021-11-02 23:11:05 +05:30
|
|
|
|
Center of gravity accounts for periodic boundaries.
|
2023-02-21 20:57:06 +05:30
|
|
|
|
Defaults to True.
|
2020-09-25 00:56:16 +05:30
|
|
|
|
|
2021-04-23 22:50:07 +05:30
|
|
|
|
Returns
|
|
|
|
|
-------
|
2022-01-12 18:48:38 +05:30
|
|
|
|
coords, materials : numpy.ndarray, shape (:,3); numpy.ndarray, shape (:)
|
2021-04-24 21:30:57 +05:30
|
|
|
|
Seed coordinates in 3D space, material IDs.
|
2021-04-23 22:50:07 +05:30
|
|
|
|
|
2023-10-13 02:51:40 +05:30
|
|
|
|
Notes
|
|
|
|
|
-----
|
|
|
|
|
The origin is not considered in order to obtain coordinates
|
|
|
|
|
in a coordinate system located at the origin. This is expected
|
|
|
|
|
by damask.Grid.from_Voronoi_tessellation.
|
|
|
|
|
|
2023-02-21 20:57:06 +05:30
|
|
|
|
Examples
|
|
|
|
|
--------
|
|
|
|
|
Recreate seeds from Voronoi tessellation.
|
|
|
|
|
|
|
|
|
|
>>> import numpy as np
|
|
|
|
|
>>> import scipy.spatial
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> seeds = damask.seeds.from_random(np.ones(3),29,[128]*3)
|
|
|
|
|
>>> (g := damask.Grid.from_Voronoi_tessellation([128]*3,np.ones(3),seeds))
|
|
|
|
|
cells: 128 × 128 × 128
|
|
|
|
|
size: 1.0 × 1.0 × 1.0 m³
|
|
|
|
|
origin: 0.0 0.0 0.0 m
|
|
|
|
|
# materials: 29
|
|
|
|
|
>>> COG,matID = damask.seeds.from_grid(g,average=True)
|
|
|
|
|
>>> distance,ID = scipy.spatial.KDTree(COG,boxsize=g.size).query(seeds)
|
|
|
|
|
>>> np.max(distance) / np.linalg.norm(g.size/g.cells)
|
|
|
|
|
7.8057356746350415
|
|
|
|
|
>>> (ID == matID).all()
|
|
|
|
|
True
|
|
|
|
|
|
2020-09-23 13:15:36 +05:30
|
|
|
|
"""
|
2020-12-04 11:42:18 +05:30
|
|
|
|
material = grid.material.reshape((-1,1),order='F')
|
2022-03-10 04:54:05 +05:30
|
|
|
|
mask = _np.full(grid.cells.prod(),True,dtype=bool) if selection is None else \
|
2022-12-14 00:02:19 +05:30
|
|
|
|
_np.isin(material,selection,invert=invert_selection).flatten()
|
2021-05-06 18:30:03 +05:30
|
|
|
|
coords = _grid_filters.coordinates0_point(grid.cells,grid.size).reshape(-1,3,order='F')
|
2020-09-23 13:15:36 +05:30
|
|
|
|
|
2020-09-25 07:29:48 +05:30
|
|
|
|
if not average:
|
|
|
|
|
return (coords[mask],material[mask])
|
|
|
|
|
else:
|
|
|
|
|
materials = _np.unique(material[mask])
|
|
|
|
|
coords_ = _np.zeros((materials.size,3),dtype=float)
|
|
|
|
|
for i,mat in enumerate(materials):
|
2023-10-13 02:51:40 +05:30
|
|
|
|
pc = 2*_np.pi*coords[material[:,0]==mat,:]/grid.size
|
|
|
|
|
coords_[i] = grid.size / 2 / _np.pi * (_np.pi +
|
2020-09-25 07:29:48 +05:30
|
|
|
|
_np.arctan2(-_np.average(_np.sin(pc),axis=0),
|
|
|
|
|
-_np.average(_np.cos(pc),axis=0))) \
|
|
|
|
|
if periodic else \
|
|
|
|
|
_np.average(coords[material[:,0]==mat,:],axis=0)
|
|
|
|
|
return (coords_,materials)
|