more systematic name

This commit is contained in:
Martin Diehl 2023-11-27 22:41:28 +01:00
parent 558ab1c98b
commit 4c5f980d00
67 changed files with 243 additions and 242 deletions

@ -1 +1 @@
Subproject commit 2cfe059d411b00e62e005107c3031ec988b2cd84
Subproject commit bc2c8e3b8f405fdda0d69a2900d9b94c7cc0936f

View File

@ -193,7 +193,7 @@ if filenames == []: filenames = [None]
for name in filenames:
print(script_name+': '+name)
geom = damask.Grid.load(StringIO(''.join(sys.stdin.read())) if name is None else name)
geom = damask.GeomGrid.load(StringIO(''.join(sys.stdin.read())) if name is None else name)
material = geom.material.flatten(order='F')
cmds = [\

View File

@ -26,5 +26,5 @@ from ._vtk import VTK # noqa
from ._config import Config # noqa
from ._configmaterial import ConfigMaterial # noqa
from ._loadcasegrid import LoadcaseGrid # noqa
from ._grid import Grid # noqa
from ._geomgrid import GeomGrid # noqa
from ._result import Result # noqa

View File

@ -144,7 +144,7 @@ class ConfigMaterial(Config):
Notes
-----
damask.Grid.load_DREAM3D gives the corresponding geometry for
damask.GeomGrid.load_DREAM3D gives the corresponding geometry for
the grid solver.
For cell-wise data, only unique combinations of

View File

@ -28,7 +28,7 @@ def numba_njit_wrapper(**kwargs):
return (lambda function: nb.njit(function) if nb else function)
class Grid:
class GeomGrid:
"""
Geometry definition for grid solvers.
@ -89,7 +89,7 @@ class Grid:
]+(['initial_conditions:']+[f' - {f}' for f in self.initial_conditions] if self.initial_conditions else []))
def __copy__(self) -> 'Grid':
def __copy__(self) -> 'GeomGrid':
"""
Return deepcopy(self).
@ -110,11 +110,11 @@ class Grid:
Parameters
----------
other : damask.Grid
Grid to compare self against.
other : damask.GeomGrid
GeomGrid to compare self against.
"""
if not isinstance(other, Grid):
if not isinstance(other, GeomGrid):
return NotImplemented
return bool( np.allclose(other.size,self.size)
and np.allclose(other.origin,self.origin)
@ -197,20 +197,20 @@ class Grid:
@staticmethod
def load(fname: Union[str, Path]) -> 'Grid':
def load(fname: Union[str, Path]) -> 'GeomGrid':
"""
Load from VTK ImageData file.
Parameters
----------
fname : str or pathlib.Path
Grid file to read.
GeomGrid file to read.
Valid extension is .vti, which will be appended if not given.
Returns
-------
loaded : damask.Grid
Grid-based geometry from file.
loaded : damask.GeomGrid
GeomGrid-based geometry from file.
"""
v = VTK.load(fname if str(fname).endswith('.vti') else str(fname)+'.vti')
@ -218,17 +218,17 @@ class Grid:
bbox = np.array(v.vtk_data.GetBounds()).reshape(3,2).T
ic = {label:v.get(label).reshape(cells,order='F') for label in set(v.labels['Cell Data']) - {'material'}}
return Grid(material = v.get('material').reshape(cells,order='F'),
size = bbox[1] - bbox[0],
origin = bbox[0],
initial_conditions = ic,
comments = v.comments,
)
return GeomGrid(material = v.get('material').reshape(cells,order='F'),
size = bbox[1] - bbox[0],
origin = bbox[0],
initial_conditions = ic,
comments = v.comments,
)
@typing.no_type_check
@staticmethod
def load_ASCII(fname)-> 'Grid':
def load_ASCII(fname)-> 'GeomGrid':
"""
Load from geom file.
@ -242,8 +242,8 @@ class Grid:
Returns
-------
loaded : damask.Grid
Grid-based geometry from file.
loaded : damask.GeomGrid
GeomGrid-based geometry from file.
"""
warnings.warn('Support for ASCII-based geom format will be removed in DAMASK 3.0.0', DeprecationWarning,2)
@ -296,14 +296,15 @@ class Grid:
if not np.any(np.mod(material,1) != 0.0): # no float present
material = material.astype(np.int64) - (1 if material.min() > 0 else 0)
return Grid(material = material.reshape(cells,order='F'),
size = size,
origin = origin,
comments = comments)
return GeomGrid(material = material.reshape(cells,order='F'),
size = size,
origin = origin,
comments = comments,
)
@staticmethod
def load_Neper(fname: Union[str, Path]) -> 'Grid':
def load_Neper(fname: Union[str, Path]) -> 'GeomGrid':
"""
Load from Neper VTK file.
@ -314,8 +315,8 @@ class Grid:
Returns
-------
loaded : damask.Grid
Grid-based geometry from file.
loaded : damask.GeomGrid
GeomGrid-based geometry from file.
Notes
-----
@ -330,7 +331,7 @@ class Grid:
>>> N_grains = 20
>>> cells = (32,32,32)
>>> damask.util.run(f'neper -T -n {N_grains} -tesrsize {cells[0]}:{cells[1]}:{cells[2]} -periodicity all -format vtk')
>>> damask.Grid.load_Neper(f'n{N_grains}-id1.vtk').renumber()
>>> damask.GeomGrid.load_Neper(f'n{N_grains}-id1.vtk').renumber()
cells: 32 × 32 × 32
size: 1.0 × 1.0 × 1.0
origin: 0.0 0.0 0.0 m
@ -341,11 +342,11 @@ class Grid:
cells = np.array(v.vtk_data.GetDimensions())-1
bbox = np.array(v.vtk_data.GetBounds()).reshape(3,2).T
return Grid(material = v.get('MaterialId').reshape(cells,order='F').astype('int32',casting='unsafe'),
size = bbox[1] - bbox[0],
origin = bbox[0],
comments = util.execution_stamp('Grid','load_Neper'),
)
return GeomGrid(material = v.get('MaterialId').reshape(cells,order='F').astype('int32',casting='unsafe'),
size = bbox[1] - bbox[0],
origin = bbox[0],
comments = util.execution_stamp('GeomGrid','load_Neper'),
)
@staticmethod
@ -354,7 +355,7 @@ class Grid:
cell_data: Optional[str] = None,
phases: str = 'Phases',
Euler_angles: str = 'EulerAngles',
base_group: Optional[str] = None) -> 'Grid':
base_group: Optional[str] = None) -> 'GeomGrid':
"""
Load DREAM.3D (HDF5) file.
@ -389,8 +390,8 @@ class Grid:
Returns
-------
loaded : damask.Grid
Grid-based geometry from file.
loaded : damask.GeomGrid
GeomGrid-based geometry from file.
Notes
-----
@ -418,17 +419,17 @@ class Grid:
else:
ma = f['/'.join([b,c,feature_IDs])][()].flatten()
return Grid(material = ma.reshape(cells,order='F'),
size = size,
origin = origin,
comments = util.execution_stamp('Grid','load_DREAM3D'),
)
return GeomGrid(material = ma.reshape(cells,order='F'),
size = size,
origin = origin,
comments = util.execution_stamp('GeomGrid','load_DREAM3D'),
)
@staticmethod
def from_table(table: Table,
coordinates: str,
labels: Union[str, Sequence[str]]) -> 'Grid':
labels: Union[str, Sequence[str]]) -> 'GeomGrid':
"""
Create grid from ASCII table.
@ -445,8 +446,8 @@ class Grid:
Returns
-------
new : damask.Grid
Grid-based geometry from values in table.
new : damask.GeomGrid
GeomGrid-based geometry from values in table.
"""
cells,size,origin = grid_filters.cellsSizeOrigin_coordinates0_point(table.get(coordinates))
@ -457,11 +458,11 @@ class Grid:
ma = np.arange(cells.prod()) if len(unique) == cells.prod() else \
np.arange(unique.size)[np.argsort(pd.unique(unique_inverse))][unique_inverse]
return Grid(material = ma.reshape(cells,order='F'),
size = size,
origin = origin,
comments = util.execution_stamp('Grid','from_table'),
)
return GeomGrid(material = ma.reshape(cells,order='F'),
size = size,
origin = origin,
comments = util.execution_stamp('GeomGrid','from_table'),
)
@staticmethod
@ -500,8 +501,8 @@ class Grid:
Returns
-------
new : damask.Grid
Grid-based geometry from tessellation.
new : damask.GeomGrid
GeomGrid-based geometry from tessellation.
"""
weights_p: FloatSequence
@ -517,17 +518,17 @@ class Grid:
coords = grid_filters.coordinates0_point(cells,size).reshape(-1,3)
pool = mp.Pool(int(os.environ.get('OMP_NUM_THREADS',4)))
result = pool.map_async(partial(Grid._find_closest_seed,seeds_p,weights_p), coords)
result = pool.map_async(partial(GeomGrid._find_closest_seed,seeds_p,weights_p), coords)
pool.close()
pool.join()
material_ = np.array(result.get()).reshape(cells)
if periodic: material_ %= len(weights)
return Grid(material = material_ if material is None else np.array(material)[material_],
size = size,
comments = util.execution_stamp('Grid','from_Laguerre_tessellation'),
)
return GeomGrid(material = material_ if material is None else np.array(material)[material_],
size = size,
comments = util.execution_stamp('GeomGrid','from_Laguerre_tessellation'),
)
@staticmethod
@ -535,7 +536,7 @@ class Grid:
size: FloatSequence,
seeds: np.ndarray,
material: Optional[IntSequence] = None,
periodic: bool = True) -> 'Grid':
periodic: bool = True) -> 'GeomGrid':
"""
Create grid from Voronoi tessellation.
@ -556,8 +557,8 @@ class Grid:
Returns
-------
new : damask.Grid
Grid-based geometry from tessellation.
new : damask.GeomGrid
GeomGrid-based geometry from tessellation.
"""
coords = grid_filters.coordinates0_point(cells,size).reshape(-1,3)
@ -568,10 +569,10 @@ class Grid:
except TypeError:
material_ = tree.query(coords, n_jobs = int(os.environ.get('OMP_NUM_THREADS',4)))[1] # scipy <1.6
return Grid(material = (material_ if material is None else np.array(material)[material_]).reshape(cells),
size = size,
comments = util.execution_stamp('Grid','from_Voronoi_tessellation'),
)
return GeomGrid(material = (material_ if material is None else np.array(material)[material_]).reshape(cells),
size = size,
comments = util.execution_stamp('GeomGrid','from_Voronoi_tessellation'),
)
_minimal_surface = \
@ -622,7 +623,7 @@ class Grid:
surface: str,
threshold: float = 0.0,
periods: int = 1,
materials: IntSequence = (0,1)) -> 'Grid':
materials: IntSequence = (0,1)) -> 'GeomGrid':
"""
Create grid from definition of triply-periodic minimal surface.
@ -643,8 +644,8 @@ class Grid:
Returns
-------
new : damask.Grid
Grid-based geometry from definition of minimal surface.
new : damask.GeomGrid
GeomGrid-based geometry from definition of minimal surface.
Notes
-----
@ -679,7 +680,7 @@ class Grid:
>>> import numpy as np
>>> import damask
>>> damask.Grid.from_minimal_surface([64]*3,np.ones(3)*1.e-4,'Gyroid')
>>> damask.GeomGrid.from_minimal_surface([64]*3,np.ones(3)*1.e-4,'Gyroid')
cells : 64 × 64 × 64
size : 0.0001 × 0.0001 × 0.0001
origin: 0.0 0.0 0.0 m
@ -689,7 +690,7 @@ class Grid:
>>> import numpy as np
>>> import damask
>>> damask.Grid.from_minimal_surface([80]*3,np.ones(3)*5.e-4,
>>> damask.GeomGrid.from_minimal_surface([80]*3,np.ones(3)*5.e-4,
... 'Neovius',materials=(1,5))
cells : 80 × 80 × 80
size : 0.0005 × 0.0005 × 0.0005
@ -701,10 +702,10 @@ class Grid:
periods*2.0*np.pi*(np.arange(cells[1])+0.5)/cells[1],
periods*2.0*np.pi*(np.arange(cells[2])+0.5)/cells[2],
indexing='ij',sparse=True)
return Grid(material = np.where(threshold < Grid._minimal_surface[surface](x,y,z),materials[1],materials[0]),
size = size,
comments = util.execution_stamp('Grid','from_minimal_surface'),
)
return GeomGrid(material = np.where(threshold < GeomGrid._minimal_surface[surface](x,y,z),materials[1],materials[0]),
size = size,
comments = util.execution_stamp('GeomGrid','from_minimal_surface'),
)
def save(self,
@ -781,7 +782,7 @@ class Grid:
def canvas(self,
cells: Optional[IntSequence] = None,
offset: Optional[IntSequence] = None,
fill: Optional[int] = None) -> 'Grid':
fill: Optional[int] = None) -> 'GeomGrid':
"""
Crop or enlarge/pad grid.
@ -798,7 +799,7 @@ class Grid:
Returns
-------
updated : damask.Grid
updated : damask.GeomGrid
Updated grid-based geometry.
Examples
@ -807,7 +808,7 @@ class Grid:
>>> import numpy as np
>>> import damask
>>> g = damask.Grid(np.zeros([32]*3,int),np.ones(3)*1e-3)
>>> g = damask.GeomGrid(np.zeros([32]*3,int),np.ones(3)*1e-3)
>>> g.canvas([32,32,16],[0,0,16])
cells: 32 × 32 × 16
size: 0.001 × 0.001 × 0.0005
@ -827,16 +828,16 @@ class Grid:
canvas[ll[0]:ur[0],ll[1]:ur[1],ll[2]:ur[2]] = self.material[LL[0]:UR[0],LL[1]:UR[1],LL[2]:UR[2]]
return Grid(material = canvas,
size = self.size/self.cells*np.asarray(canvas.shape),
origin = self.origin+offset_*self.size/self.cells,
comments = self.comments+[util.execution_stamp('Grid','canvas')],
)
return GeomGrid(material = canvas,
size = self.size/self.cells*np.asarray(canvas.shape),
origin = self.origin+offset_*self.size/self.cells,
comments = self.comments+[util.execution_stamp('GeomGrid','canvas')],
)
def mirror(self,
directions: Sequence[str],
reflect: bool = False) -> 'Grid':
reflect: bool = False) -> 'GeomGrid':
"""
Mirror grid along given directions.
@ -849,7 +850,7 @@ class Grid:
Returns
-------
updated : damask.Grid
updated : damask.GeomGrid
Updated grid-based geometry.
Examples
@ -858,7 +859,7 @@ class Grid:
>>> import numpy as np
>>> import damask
>>> (g := damask.Grid(np.arange(4*5*6).reshape([4,5,6]),np.ones(3)))
>>> (g := damask.GeomGrid(np.arange(4*5*6).reshape([4,5,6]),np.ones(3)))
cells: 4 × 5 × 6
size: 1.0 × 1.0 × 1.0
origin: 0.0 0.0 0.0 m
@ -896,15 +897,15 @@ class Grid:
if 'z' in directions:
mat = np.concatenate([mat,mat[:,:,limits[0]:limits[1]:-1]],2)
return Grid(material = mat,
size = self.size/self.cells*np.asarray(mat.shape),
origin = self.origin,
comments = self.comments+[util.execution_stamp('Grid','mirror')],
)
return GeomGrid(material = mat,
size = self.size/self.cells*np.asarray(mat.shape),
origin = self.origin,
comments = self.comments+[util.execution_stamp('GeomGrid','mirror')],
)
def flip(self,
directions: Sequence[str]) -> 'Grid':
directions: Sequence[str]) -> 'GeomGrid':
"""
Flip grid along given directions.
@ -915,7 +916,7 @@ class Grid:
Returns
-------
updated : damask.Grid
updated : damask.GeomGrid
Updated grid-based geometry.
Examples
@ -924,7 +925,7 @@ class Grid:
>>> import numpy as np
>>> import damask
>>> (g := damask.Grid(np.arange(4*5*6).reshape([4,5,6]),np.ones(3)))
>>> (g := damask.GeomGrid(np.arange(4*5*6).reshape([4,5,6]),np.ones(3)))
cells: 4 × 5 × 6
size: 1.0 × 1.0 × 1.0
origin: 0.0 0.0 0.0 m
@ -943,16 +944,16 @@ class Grid:
mat = np.flip(self.material, [valid.index(d) for d in directions if d in valid])
return Grid(material = mat,
size = self.size,
origin = self.origin,
comments = self.comments+[util.execution_stamp('Grid','flip')],
)
return GeomGrid(material = mat,
size = self.size,
origin = self.origin,
comments = self.comments+[util.execution_stamp('GeomGrid','flip')],
)
def rotate(self,
R: Rotation,
fill: Optional[int] = None) -> 'Grid':
fill: Optional[int] = None) -> 'GeomGrid':
"""
Rotate grid (possibly extending its bounding box).
@ -966,7 +967,7 @@ class Grid:
Returns
-------
updated : damask.Grid
updated : damask.GeomGrid
Updated grid-based geometry.
Examples
@ -975,7 +976,7 @@ class Grid:
>>> import numpy as np
>>> import damask
>>> (g := damask.Grid(np.arange(4*5*6).reshape([4,5,6]),np.ones(3)))
>>> (g := damask.GeomGrid(np.arange(4*5*6).reshape([4,5,6]),np.ones(3)))
cells: 4 × 5 × 6
size: 1.0 × 1.0 × 1.0
origin: 0.0 0.0 0.0 m
@ -997,15 +998,15 @@ class Grid:
origin = self.origin-(np.asarray(material.shape)-self.cells)*.5 * self.size/self.cells
return Grid(material = material,
size = self.size/self.cells*np.asarray(material.shape),
origin = origin,
comments = self.comments+[util.execution_stamp('Grid','rotate')],
)
return GeomGrid(material = material,
size = self.size/self.cells*np.asarray(material.shape),
origin = origin,
comments = self.comments+[util.execution_stamp('GeomGrid','rotate')],
)
def scale(self,
cells: IntSequence) -> 'Grid':
cells: IntSequence) -> 'GeomGrid':
"""
Scale grid to new cell counts.
@ -1016,7 +1017,7 @@ class Grid:
Returns
-------
updated : damask.Grid
updated : damask.GeomGrid
Updated grid-based geometry.
Examples
@ -1025,7 +1026,7 @@ class Grid:
>>> import numpy as np
>>> import damask
>>> (g := damask.Grid(np.zeros([32]*3,int),np.ones(3)*1e-4))
>>> (g := damask.GeomGrid(np.zeros([32]*3,int),np.ones(3)*1e-4))
cells: 32 × 32 × 32
size: 0.0001 × 0.0001 × 0.0001
origin: 0.0 0.0 0.0 m
@ -1043,28 +1044,28 @@ class Grid:
points=orig,method='nearest',bounds_error=False,fill_value=None)
new = grid_filters.coordinates0_point(cells,self.size,self.origin)
return Grid(material = interpolator(values=self.material)(new).astype(int),
size = self.size,
origin = self.origin,
initial_conditions = {k: interpolator(values=v)(new)
for k,v in self.initial_conditions.items()},
comments = self.comments+[util.execution_stamp('Grid','scale')],
)
return GeomGrid(material = interpolator(values=self.material)(new).astype(int),
size = self.size,
origin = self.origin,
initial_conditions = {k: interpolator(values=v)(new)
for k,v in self.initial_conditions.items()},
comments = self.comments+[util.execution_stamp('GeomGrid','scale')],
)
def assemble(self,
idx: np.ndarray) -> 'Grid':
idx: np.ndarray) -> 'GeomGrid':
"""
Assemble new grid from index map.
Parameters
----------
idx : numpy.ndarray of int, shape (:,:,:) or (:,:,:,3)
Grid of flat indices or coordinate indices.
GeomGrid of flat indices or coordinate indices.
Returns
-------
updated : damask.Grid
updated : damask.GeomGrid
Updated grid-based geometry.
Cell count of resulting grid matches shape of index map.
@ -1073,37 +1074,37 @@ class Grid:
flat = (idx if len(idx.shape)==3 else grid_filters.ravel_index(idx)).flatten(order='F')
ic = {k: v.flatten(order='F')[flat].reshape(cells,order='F') for k,v in self.initial_conditions.items()}
return Grid(material = self.material.flatten(order='F')[flat].reshape(cells,order='F'),
size = self.size,
origin = self.origin,
initial_conditions = ic,
comments = self.comments+[util.execution_stamp('Grid','assemble')],
)
return GeomGrid(material = self.material.flatten(order='F')[flat].reshape(cells,order='F'),
size = self.size,
origin = self.origin,
initial_conditions = ic,
comments = self.comments+[util.execution_stamp('GeomGrid','assemble')],
)
def renumber(self) -> 'Grid':
def renumber(self) -> 'GeomGrid':
"""
Renumber sorted material indices as 0,...,N-1.
Returns
-------
updated : damask.Grid
updated : damask.GeomGrid
Updated grid-based geometry.
"""
_,renumbered = np.unique(self.material,return_inverse=True)
return Grid(material = renumbered.reshape(self.cells),
size = self.size,
origin = self.origin,
initial_conditions = self.initial_conditions,
comments = self.comments+[util.execution_stamp('Grid','renumber')],
)
return GeomGrid(material = renumbered.reshape(self.cells),
size = self.size,
origin = self.origin,
initial_conditions = self.initial_conditions,
comments = self.comments+[util.execution_stamp('GeomGrid','renumber')],
)
def substitute(self,
from_material: Union[int,IntSequence],
to_material: Union[int,IntSequence]) -> 'Grid':
to_material: Union[int,IntSequence]) -> 'GeomGrid':
"""
Substitute material indices.
@ -1116,7 +1117,7 @@ class Grid:
Returns
-------
updated : damask.Grid
updated : damask.GeomGrid
Updated grid-based geometry.
"""
@ -1125,21 +1126,21 @@ class Grid:
to_material if isinstance(to_material,(Sequence,np.ndarray)) else [to_material]): # ToDo Python 3.10 has strict mode for zip
material[self.material==f] = t
return Grid(material = material,
size = self.size,
origin = self.origin,
initial_conditions = self.initial_conditions,
comments = self.comments+[util.execution_stamp('Grid','substitute')],
)
return GeomGrid(material = material,
size = self.size,
origin = self.origin,
initial_conditions = self.initial_conditions,
comments = self.comments+[util.execution_stamp('GeomGrid','substitute')],
)
def sort(self) -> 'Grid':
def sort(self) -> 'GeomGrid':
"""
Sort material indices such that min(material ID) is located at (0,0,0).
Returns
-------
updated : damask.Grid
updated : damask.GeomGrid
Updated grid-based geometry.
"""
@ -1148,12 +1149,12 @@ class Grid:
sort_idx = np.argsort(from_ma)
ma = np.unique(a)[sort_idx][np.searchsorted(from_ma,a,sorter = sort_idx)]
return Grid(material = ma.reshape(self.cells,order='F'),
size = self.size,
origin = self.origin,
initial_conditions = self.initial_conditions,
comments = self.comments+[util.execution_stamp('Grid','sort')],
)
return GeomGrid(material = ma.reshape(self.cells,order='F'),
size = self.size,
origin = self.origin,
initial_conditions = self.initial_conditions,
comments = self.comments+[util.execution_stamp('GeomGrid','sort')],
)
def clean(self,
@ -1161,7 +1162,7 @@ class Grid:
selection: Optional[IntSequence] = None,
invert_selection: bool = False,
periodic: bool = True,
rng_seed: Optional[NumpyRngSeed] = None) -> 'Grid':
rng_seed: Optional[NumpyRngSeed] = None) -> 'GeomGrid':
"""
Smooth grid by selecting most frequent material ID within given stencil at each location.
@ -1182,7 +1183,7 @@ class Grid:
Returns
-------
updated : damask.Grid
updated : damask.GeomGrid
Updated grid-based geometry.
Notes
@ -1216,12 +1217,12 @@ class Grid:
mode='wrap' if periodic else 'nearest',
extra_keywords=dict(selection=selection_,rng=rng),
).astype(self.material.dtype)
return Grid(material = material,
size = self.size,
origin = self.origin,
initial_conditions = self.initial_conditions,
comments = self.comments+[util.execution_stamp('Grid','clean')],
)
return GeomGrid(material = material,
size = self.size,
origin = self.origin,
initial_conditions = self.initial_conditions,
comments = self.comments+[util.execution_stamp('GeomGrid','clean')],
)
def add_primitive(self,
@ -1231,7 +1232,7 @@ class Grid:
fill: Optional[int] = None,
R: Rotation = Rotation(),
inverse: bool = False,
periodic: bool = True) -> 'Grid':
periodic: bool = True) -> 'GeomGrid':
"""
Insert a primitive geometric object at a given position.
@ -1261,7 +1262,7 @@ class Grid:
Returns
-------
updated : damask.Grid
updated : damask.GeomGrid
Updated grid-based geometry.
Examples
@ -1270,7 +1271,7 @@ class Grid:
>>> import numpy as np
>>> import damask
>>> g = damask.Grid(np.zeros([64]*3,int), np.ones(3)*1e-4)
>>> g = damask.GeomGrid(np.zeros([64]*3,int), np.ones(3)*1e-4)
>>> g.add_primitive(np.ones(3)*5e-5,np.ones(3)*5e-5,1)
cells : 64 × 64 × 64
size : 0.0001 × 0.0001 × 0.0001
@ -1281,7 +1282,7 @@ class Grid:
>>> import numpy as np
>>> import damask
>>> g = damask.Grid(np.zeros([64]*3,int), np.ones(3)*1e-4)
>>> g = damask.GeomGrid(np.zeros([64]*3,int), np.ones(3)*1e-4)
>>> g.add_primitive(np.ones(3,int)*32,np.zeros(3),np.inf)
cells : 64 × 64 × 64
size : 0.0001 × 0.0001 × 0.0001
@ -1307,14 +1308,14 @@ class Grid:
if periodic: # translate back to center
mask = np.roll(mask,((c/self.size-0.5)*self.cells).round().astype(np.int64),(0,1,2))
return Grid(material = np.where(np.logical_not(mask) if inverse else mask,
return GeomGrid(material = np.where(np.logical_not(mask) if inverse else mask,
self.material,
np.nanmax(self.material)+1 if fill is None else fill),
size = self.size,
origin = self.origin,
initial_conditions = self.initial_conditions,
comments = self.comments+[util.execution_stamp('Grid','add_primitive')],
)
size = self.size,
origin = self.origin,
initial_conditions = self.initial_conditions,
comments = self.comments+[util.execution_stamp('GeomGrid','add_primitive')],
)
def vicinity_offset(self,
@ -1322,7 +1323,7 @@ class Grid:
offset: Optional[int] = None,
selection: Optional[IntSequence] = None,
invert_selection: bool = False,
periodic: bool = True) -> 'Grid':
periodic: bool = True) -> 'GeomGrid':
"""
Offset material ID of points in the vicinity of selected (or just other) material IDs.
@ -1348,7 +1349,7 @@ class Grid:
Returns
-------
updated : damask.Grid
updated : damask.GeomGrid
Updated grid-based geometry.
"""
@ -1380,12 +1381,12 @@ class Grid:
extra_keywords=dict(selection=selection_),
)
return Grid(material = np.where(mask, self.material + offset_,self.material),
size = self.size,
origin = self.origin,
initial_conditions = self.initial_conditions,
comments = self.comments+[util.execution_stamp('Grid','vicinity_offset')],
)
return GeomGrid(material = np.where(mask, self.material + offset_,self.material),
size = self.size,
origin = self.origin,
initial_conditions = self.initial_conditions,
comments = self.comments+[util.execution_stamp('GeomGrid','vicinity_offset')],
)
def get_grain_boundaries(self,

View File

@ -116,7 +116,7 @@ def from_grid(grid,
Parameters
----------
grid : damask.Grid
grid : damask.GeomGrid
Grid from which the material IDs are used as seeds.
selection : (sequence of) int, optional
Material IDs to consider.
@ -138,7 +138,7 @@ def from_grid(grid,
-----
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.
by damask.GeomGrid.from_Voronoi_tessellation.
Examples
--------
@ -148,7 +148,7 @@ def from_grid(grid,
>>> 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))
>>> (g := damask.GeomGrid.from_Voronoi_tessellation([128]*3,np.ones(3),seeds))
cells: 128 × 128 × 128
size: 1.0 × 1.0 × 1.0
origin: 0.0 0.0 0.0 m

View File

@ -1 +1 @@
../Grid/2phase_irregularGrid.dream3d
../GeomGrid/2phase_irregularGrid.dream3d

View File

@ -1 +1 @@
../Grid/2phase_irregularGrid.json
../GeomGrid/2phase_irregularGrid.json

View File

@ -1 +1 @@
../Grid/2phase_irregularGrid.xdmf
../GeomGrid/2phase_irregularGrid.xdmf

View File

@ -1 +1 @@
../Grid/measured.dream3d
../GeomGrid/measured.dream3d

View File

@ -1 +1 @@
../Grid/measured.xdmf
../GeomGrid/measured.xdmf

View File

@ -6,7 +6,7 @@ import numpy as np
from damask import ConfigMaterial
from damask import Table
from damask import Rotation
from damask import Grid
from damask import GeomGrid
@pytest.fixture
def res_path(res_path_base):
@ -182,8 +182,8 @@ class TestConfigMaterial:
assert point_c.is_valid and grain_c.is_valid and \
len(point_c['material'])+1 == len(grain_c['material'])
grain_m = Grid.load_DREAM3D(res_path/'2phase_irregularGrid.dream3d','FeatureIds').material.flatten()
point_m = Grid.load_DREAM3D(res_path/'2phase_irregularGrid.dream3d').material.flatten()
grain_m = GeomGrid.load_DREAM3D(res_path/'2phase_irregularGrid.dream3d','FeatureIds').material.flatten()
point_m = GeomGrid.load_DREAM3D(res_path/'2phase_irregularGrid.dream3d').material.flatten()
for i in np.unique(point_m):
j = int(grain_m[(point_m==i).nonzero()[0][0]])

View File

@ -5,7 +5,7 @@ import numpy as np
from vtkmodules.vtkCommonCore import vtkVersion
from damask import VTK
from damask import Grid
from damask import GeomGrid
from damask import Table
from damask import Rotation
from damask import Colormap
@ -19,7 +19,7 @@ def default():
"""Simple geometry."""
g = np.array([8,5,4])
l = np.prod(g[:2])
return Grid(np.concatenate((np.ones (l,dtype=int),
return GeomGrid(np.concatenate((np.ones (l,dtype=int),
np.arange(l,dtype=int)+2,
np.ones (l,dtype=int)*2,
np.arange(l,dtype=int)+1)).reshape(g,order='F'),
@ -31,15 +31,15 @@ def random():
size = (1+np.random.rand(3))*1e-5
cells = np.random.randint(10,20,3)
s = seeds.from_random(size,np.random.randint(5,25),cells)
return Grid.from_Voronoi_tessellation(cells,size,s)
return GeomGrid.from_Voronoi_tessellation(cells,size,s)
@pytest.fixture
def res_path(res_path_base):
"""Directory containing testing resources."""
return res_path_base/'Grid'
return res_path_base/'GeomGrid'
class TestGrid:
class TestGeomGrid:
@pytest.fixture(autouse=True)
def _patch_execution_stamp(self, patch_execution_stamp):
@ -65,49 +65,49 @@ class TestGrid:
def test_read_write_vti(self,default,tmp_path):
default.save(tmp_path/'default')
new = Grid.load(tmp_path/'default.vti')
new = GeomGrid.load(tmp_path/'default.vti')
assert new == default
def test_invalid_no_material(self,tmp_path):
v = VTK.from_image_data(np.random.randint(5,10,3)*2,np.random.random(3) + 1.0)
v.save(tmp_path/'no_materialpoint.vti',parallel=False)
with pytest.raises(KeyError):
Grid.load(tmp_path/'no_materialpoint.vti')
GeomGrid.load(tmp_path/'no_materialpoint.vti')
def test_invalid_material_type(self):
with pytest.raises(TypeError):
Grid(np.zeros((3,3,3),dtype='complex'),np.ones(3))
GeomGrid(np.zeros((3,3,3),dtype='complex'),np.ones(3))
def test_cast_to_int(self):
g = Grid(np.zeros((3,3,3)),np.ones(3))
g = GeomGrid(np.zeros((3,3,3)),np.ones(3))
assert g.material.dtype in np.sctypes['int']
def test_invalid_size(self,default):
with pytest.raises(ValueError):
Grid(default.material[1:,1:,1:],
GeomGrid(default.material[1:,1:,1:],
size=np.ones(2))
def test_save_load_ASCII(self,default,tmp_path):
default.save_ASCII(tmp_path/'ASCII')
default.material -= 1
assert Grid.load_ASCII(tmp_path/'ASCII') == default
assert GeomGrid.load_ASCII(tmp_path/'ASCII') == default
def test_invalid_origin(self,default):
with pytest.raises(ValueError):
Grid(default.material[1:,1:,1:],
GeomGrid(default.material[1:,1:,1:],
size=np.ones(3),
origin=np.ones(4))
def test_invalid_materials_shape(self,default):
material = np.ones((3,3))
with pytest.raises(ValueError):
Grid(material,
GeomGrid(material,
size=np.ones(3))
def test_invalid_materials_type(self,default):
material = np.random.randint(1,300,(3,4,5))==1
with pytest.raises(TypeError):
Grid(material)
GeomGrid(material)
@pytest.mark.parametrize('directions,reflect',[
(['x'], False),
@ -121,7 +121,7 @@ class TestGrid:
tag = f'directions_{"-".join(directions)}+reflect_{reflect}'
reference = res_path/f'mirror_{tag}.vti'
if update: modified.save(reference)
assert Grid.load(reference) == modified
assert GeomGrid.load(reference) == modified
@pytest.mark.parametrize('directions',[(1,2,'y'),('a','b','x'),[1]])
def test_mirror_invalid(self,default,directions):
@ -146,7 +146,7 @@ class TestGrid:
tag = f'directions_{"-".join(directions)}'
reference = res_path/f'flip_{tag}.vti'
if update: modified.save(reference)
assert Grid.load(reference) == modified
assert GeomGrid.load(reference) == modified
def test_flip_order_invariant(self,default):
direction = np.array(['x','y','z'])
@ -180,7 +180,7 @@ class TestGrid:
reference = res_path/f'clean_{distance}_{util.srepr(selection,"+")}_{periodic}.vti'
if update:
current.save(reference)
assert Grid.load(reference) == current
assert GeomGrid.load(reference) == current
@pytest.mark.parametrize('selection',[list(np.random.randint(1,20,6)),np.random.randint(1,20,6)])
@pytest.mark.parametrize('invert',[True,False])
@ -207,14 +207,14 @@ class TestGrid:
tag = f'grid_{util.srepr(cells,"-")}'
reference = res_path/f'scale_{tag}.vti'
if update: modified.save(reference)
assert Grid.load(reference) == modified
assert GeomGrid.load(reference) == modified
def test_renumber(self,default):
material = default.material.copy()
for m in np.unique(material):
material[material==m] = material.max() + np.random.randint(1,30)
default.material -= 1
modified = Grid(material,
modified = GeomGrid(material,
default.size,
default.origin)
assert not default == modified
@ -223,13 +223,13 @@ class TestGrid:
def test_assemble(self):
cells = np.random.randint(8,16,3)
N = cells.prod()
g = Grid(np.arange(N).reshape(cells),np.ones(3))
g = GeomGrid(np.arange(N).reshape(cells),np.ones(3))
idx = np.random.randint(0,N,N).reshape(cells)
assert (idx == g.assemble(idx).material).all
def test_substitute(self,default):
offset = np.random.randint(1,500)
modified = Grid(default.material + offset,
modified = GeomGrid(default.material + offset,
default.size,
default.origin)
assert not default == modified
@ -250,7 +250,7 @@ class TestGrid:
def test_sort(self):
cells = np.random.randint(5,20,3)
m = Grid(np.random.randint(1,20,cells)*3,np.ones(3)).sort().material.flatten(order='F')
m = GeomGrid(np.random.randint(1,20,cells)*3,np.ones(3)).sort().material.flatten(order='F')
for i,v in enumerate(m):
assert i==0 or v > m[:i].max() or v in m[:i]
@ -269,7 +269,7 @@ class TestGrid:
tag = f'Eulers_{util.srepr(Eulers,"-")}'
reference = res_path/f'rotate_{tag}.vti'
if update: modified.save(reference)
assert Grid.load(reference) == modified
assert GeomGrid.load(reference) == modified
def test_canvas_extend(self,default):
cells = default.cells
@ -280,7 +280,7 @@ class TestGrid:
@pytest.mark.parametrize('sign',[+1,-1])
@pytest.mark.parametrize('extra_offset',[0,-1])
def test_canvas_move_out(self,sign,extra_offset):
g = Grid(np.zeros(np.random.randint(3,30,(3)),int),np.ones(3))
g = GeomGrid(np.zeros(np.random.randint(3,30,(3)),int),np.ones(3))
o = sign*np.ones(3)*g.cells.min() +extra_offset*sign
if extra_offset == 0:
assert np.all(g.canvas(offset=o).material == 1)
@ -288,7 +288,7 @@ class TestGrid:
assert np.all(np.unique(g.canvas(offset=o).material) == (0,1))
def test_canvas_cells(self,default):
g = Grid(np.zeros(np.random.randint(3,30,(3)),int),np.ones(3))
g = GeomGrid(np.zeros(np.random.randint(3,30,(3)),int),np.ones(3))
cells = np.random.randint(1,30,(3))
offset = np.random.randint(-30,30,(3))
assert np.all(g.canvas(cells,offset).cells == cells)
@ -308,8 +308,8 @@ class TestGrid:
o = np.random.random(3)-.5
g = np.random.randint(8,32,(3))
s = np.random.random(3)+.5
G_1 = Grid(np.ones(g,'i'),s,o).add_primitive(diameter,center1,exponent)
G_2 = Grid(np.ones(g,'i'),s,o).add_primitive(diameter,center2,exponent)
G_1 = GeomGrid(np.ones(g,'i'),s,o).add_primitive(diameter,center1,exponent)
G_2 = GeomGrid(np.ones(g,'i'),s,o).add_primitive(diameter,center2,exponent)
assert np.count_nonzero(G_1.material!=2) == np.count_nonzero(G_2.material!=2)
@pytest.mark.parametrize('center',[np.random.randint(4,10,(3)),
@ -323,8 +323,8 @@ class TestGrid:
g = np.random.randint(8,32,(3))
s = np.random.random(3)+.5
fill = np.random.randint(10)+2
G_1 = Grid(np.ones(g,'i'),s).add_primitive(.3,center,1,fill,inverse=inverse,periodic=periodic)
G_2 = Grid(np.ones(g,'i'),s).add_primitive(.3,center,1,fill,Rotation.from_random(),inverse,periodic=periodic)
G_1 = GeomGrid(np.ones(g,'i'),s).add_primitive(.3,center,1,fill,inverse=inverse,periodic=periodic)
G_2 = GeomGrid(np.ones(g,'i'),s).add_primitive(.3,center,1,fill,Rotation.from_random(),inverse,periodic=periodic)
assert G_1 == G_2
@pytest.mark.parametrize('exponent',[1,np.inf,np.random.random(3)*2.])
@ -332,7 +332,7 @@ class TestGrid:
"""Shapes defined in the center should always produce a grid with reflection symmetry along the coordinate axis."""
o = np.random.random(3)-.5
s = np.random.random(3)*5.
grid = Grid(np.zeros(np.random.randint(8,32,3),'i'),s,o).add_primitive(np.random.random(3)*3.,o+s/2.,exponent)
grid = GeomGrid(np.zeros(np.random.randint(8,32,3),'i'),s,o).add_primitive(np.random.random(3)*3.,o+s/2.,exponent)
for axis in [0,1,2]:
assert np.all(grid.material==np.flip(grid.material,axis=axis))
@ -352,7 +352,7 @@ class TestGrid:
if selection == 1:
m2[m==1] = 1
grid = Grid(m,np.random.rand(3)).vicinity_offset(distance,offset,selection=selection)
grid = GeomGrid(m,np.random.rand(3)).vicinity_offset(distance,offset,selection=selection)
assert np.all(m2==grid.material)
@ -379,8 +379,8 @@ class TestGrid:
size = np.random.random(3) + 1.0
N_seeds= np.random.randint(10,30)
seeds = np.random.rand(N_seeds,3) * np.broadcast_to(size,(N_seeds,3))
Voronoi = Grid.from_Voronoi_tessellation( cells,size,seeds, np.arange(N_seeds)+5,periodic)
Laguerre = Grid.from_Laguerre_tessellation(cells,size,seeds,np.ones(N_seeds),np.arange(N_seeds)+5,periodic)
Voronoi = GeomGrid.from_Voronoi_tessellation( cells,size,seeds, np.arange(N_seeds)+5,periodic)
Laguerre = GeomGrid.from_Laguerre_tessellation(cells,size,seeds,np.ones(N_seeds),np.arange(N_seeds)+5,periodic)
assert Laguerre == Voronoi
def test_Laguerre_weights(self):
@ -391,7 +391,7 @@ class TestGrid:
weights= np.full((N_seeds),-np.inf)
ms = np.random.randint(N_seeds)
weights[ms] = np.random.random()
Laguerre = Grid.from_Laguerre_tessellation(cells,size,seeds,weights,periodic=np.random.random()>0.5)
Laguerre = GeomGrid.from_Laguerre_tessellation(cells,size,seeds,weights,periodic=np.random.random()>0.5)
assert np.all(Laguerre.material == ms)
@pytest.mark.parametrize('approach',['Laguerre','Voronoi'])
@ -402,9 +402,9 @@ class TestGrid:
material = np.zeros(cells)
material[:,cells[1]//2:,:] = 1
if approach == 'Laguerre':
grid = Grid.from_Laguerre_tessellation(cells,size,seeds,np.ones(2),periodic=np.random.random()>0.5)
grid = GeomGrid.from_Laguerre_tessellation(cells,size,seeds,np.ones(2),periodic=np.random.random()>0.5)
elif approach == 'Voronoi':
grid = Grid.from_Voronoi_tessellation(cells,size,seeds, periodic=np.random.random()>0.5)
grid = GeomGrid.from_Voronoi_tessellation(cells,size,seeds, periodic=np.random.random()>0.5)
assert np.all(grid.material == material)
@pytest.mark.parametrize('surface',['Schwarz P',
@ -426,7 +426,7 @@ class TestGrid:
threshold = 2*np.random.rand()-1.
periods = np.random.randint(2)+1
materials = np.random.randint(0,40,2)
grid = Grid.from_minimal_surface(cells,size,surface,threshold,periods,materials)
grid = GeomGrid.from_minimal_surface(cells,size,surface,threshold,periods,materials)
assert set(grid.material.flatten()) | set(materials) == set(materials) \
and (grid.size == size).all() and (grid.cells == cells).all()
@ -445,7 +445,7 @@ class TestGrid:
])
def test_minimal_surface_volume(self,surface,threshold):
cells = np.ones(3,dtype=int)*64
grid = Grid.from_minimal_surface(cells,np.ones(3),surface,threshold)
grid = GeomGrid.from_minimal_surface(cells,np.ones(3),surface,threshold)
assert np.isclose(np.count_nonzero(grid.material==1)/np.prod(grid.cells),.5,rtol=1e-3)
def test_from_table(self):
@ -456,23 +456,23 @@ class TestGrid:
z[cells[:2].prod()*int(cells[2]/2):] = 0
t = Table({'coords':3,'z':1},np.column_stack((coords,z)))
t = t.set('indicator',t.get('coords')[:,0])
g = Grid.from_table(t,'coords',['indicator','z'])
g = GeomGrid.from_table(t,'coords',['indicator','z'])
assert g.N_materials == g.cells[0]*2 and (g.material[:,:,-1]-g.material[:,:,0] == cells[0]).all()
def test_from_table_recover(self,tmp_path):
cells = np.random.randint(60,100,3)
size = np.ones(3)+np.random.rand(3)
s = seeds.from_random(size,np.random.randint(60,100))
grid = Grid.from_Voronoi_tessellation(cells,size,s)
grid = GeomGrid.from_Voronoi_tessellation(cells,size,s)
coords = grid_filters.coordinates0_point(cells,size)
t = Table({'c':3,'m':1},np.column_stack((coords.reshape(-1,3,order='F'),grid.material.flatten(order='F'))))
assert grid.sort().renumber() == Grid.from_table(t,'c',['m'])
assert grid.sort().renumber() == GeomGrid.from_table(t,'c',['m'])
@pytest.mark.parametrize('periodic',[True,False])
@pytest.mark.parametrize('direction',['x','y','z',['x','y'],'zy','xz',['x','y','z']])
@pytest.mark.xfail(vtkVersion.GetVTKMajorVersion()<8, reason='missing METADATA')
def test_get_grain_boundaries(self,update,res_path,periodic,direction):
grid = Grid.load(res_path/'get_grain_boundaries_8g12x15x20.vti')
grid = GeomGrid.load(res_path/'get_grain_boundaries_8g12x15x20.vti')
current = grid.get_grain_boundaries(periodic,direction)
if update:
current.save(res_path/f'get_grain_boundaries_8g12x15x20_{direction}_{periodic}.vtu',parallel=False)
@ -485,24 +485,24 @@ class TestGrid:
default.get_grain_boundaries(directions=directions)
def test_load_DREAM3D(self,res_path):
grain = Grid.load_DREAM3D(res_path/'2phase_irregularGrid.dream3d','FeatureIds')
point = Grid.load_DREAM3D(res_path/'2phase_irregularGrid.dream3d')
grain = GeomGrid.load_DREAM3D(res_path/'2phase_irregularGrid.dream3d','FeatureIds')
point = GeomGrid.load_DREAM3D(res_path/'2phase_irregularGrid.dream3d')
assert np.allclose(grain.origin,point.origin) and \
np.allclose(grain.size,point.size) and \
(grain.sort().material == point.material+1).all()
def test_load_DREAM3D_reference(self,res_path,update):
current = Grid.load_DREAM3D(res_path/'measured.dream3d')
reference = Grid.load(res_path/'measured.vti')
current = GeomGrid.load_DREAM3D(res_path/'measured.dream3d')
reference = GeomGrid.load(res_path/'measured.vti')
if update:
current.save(res_path/'measured.vti')
assert current == reference
def test_load_Neper_reference(self,res_path,update):
current = Grid.load_Neper(res_path/'n10-id1_scaled.vtk').renumber()
reference = Grid.load(res_path/'n10-id1_scaled.vti')
current = GeomGrid.load_Neper(res_path/'n10-id1_scaled.vtk').renumber()
reference = GeomGrid.load(res_path/'n10-id1_scaled.vti')
if update:
current.save(res_path/'n10-id1_scaled.vti')

View File

@ -3,8 +3,8 @@ import numpy as np
from damask import grid_filters
from damask import mechanics
from damask import Grid
from damask import seeds
from damask import GeomGrid
class TestGridFilters:
@ -205,7 +205,7 @@ class TestGridFilters:
def test_regrid_double_cells(self):
size = np.random.random(3) # noqa
cells = np.random.randint(8,32,(3))
g = Grid.from_Voronoi_tessellation(cells,size,seeds.from_random(size,10))
g = GeomGrid.from_Voronoi_tessellation(cells,size,seeds.from_random(size,10))
F = np.broadcast_to(np.eye(3), (*cells,3,3))
assert g.scale(cells*2) == g.assemble(grid_filters.regrid(size,F,cells*2))

View File

@ -4,7 +4,7 @@ from scipy.spatial import cKDTree
from damask import seeds
from damask import grid_filters
from damask import Grid
from damask import GeomGrid
class TestSeeds:
@ -40,9 +40,9 @@ class TestSeeds:
N_seeds = np.random.randint(30,300)
size = np.ones(3) + np.random.random(3)
coords = seeds.from_random(size,N_seeds,cells)
grid_1 = Grid.from_Voronoi_tessellation(cells,size,coords)
grid_1 = GeomGrid.from_Voronoi_tessellation(cells,size,coords)
coords,material = seeds.from_grid(grid_1)
grid_2 = Grid.from_Voronoi_tessellation(cells,size,coords,material)
grid_2 = GeomGrid.from_Voronoi_tessellation(cells,size,coords,material)
assert (grid_2.material==grid_1.material).all()
@pytest.mark.parametrize('periodic',[True,False])
@ -52,9 +52,9 @@ class TestSeeds:
size = np.ones(3) + np.random.random(3)
coords = grid_filters.coordinates0_point(cells,size).reshape(-1,3)
np.random.shuffle(coords)
grid_1 = Grid.from_Voronoi_tessellation(cells,size,coords)
grid_1 = GeomGrid.from_Voronoi_tessellation(cells,size,coords)
coords,material = seeds.from_grid(grid_1,average=average,periodic=periodic)
grid_2 = Grid.from_Voronoi_tessellation(cells,size,coords,material)
grid_2 = GeomGrid.from_Voronoi_tessellation(cells,size,coords,material)
assert (grid_2.material==grid_1.material).all()
@pytest.mark.parametrize('periodic',[True,False])
@ -65,7 +65,7 @@ class TestSeeds:
N_seeds = np.random.randint(30,300)
size = np.ones(3) + np.random.random(3)
coords = seeds.from_random(size,N_seeds,cells)
grid = Grid.from_Voronoi_tessellation(cells,size,coords)
grid = GeomGrid.from_Voronoi_tessellation(cells,size,coords)
selection=np.random.randint(N_seeds)+1
coords,material = seeds.from_grid(grid,average=average,periodic=periodic,invert_selection=invert,selection=[selection])
assert selection not in material if invert else (selection==material).all()