removed set_X(), duplicate(), and .homogenization; renamed ".microstructure" to ".materials"

This commit is contained in:
Philip Eisenlohr 2020-09-22 14:47:08 -04:00
parent e683cbef69
commit 05835bacd3
2 changed files with 246 additions and 390 deletions

View File

@ -17,29 +17,44 @@ from . import grid_filters
class Geom: class Geom:
"""Geometry definition for grid solvers.""" """Geometry definition for grid solvers."""
def __init__(self,microstructure,size,origin=[0.0,0.0,0.0],homogenization=1,comments=[]): def __init__(self,materials,size,origin=[0.0,0.0,0.0],comments=[]):
""" """
New geometry definition from array of microstructures and size. New geometry definition from array of materials, size, and origin.
Parameters Parameters
---------- ----------
microstructure : numpy.ndarray materials : numpy.ndarray
Microstructure array (3D) Material index array (3D).
size : list or numpy.ndarray size : list or numpy.ndarray
Physical size of the microstructure in meter. Physical size of the geometry in meter.
origin : list or numpy.ndarray, optional origin : list or numpy.ndarray, optional
Physical origin of the microstructure in meter. Physical origin of the geometry in meter.
homogenization : int, optional
Homogenization index.
comments : list of str, optional comments : list of str, optional
Comment lines. Comment lines.
""" """
self.set_microstructure(microstructure,inplace=True) if len(materials.shape) != 3:
self.set_size(size,inplace=True) raise ValueError(f'Invalid materials shape {materials.shape}.')
self.set_origin(origin,inplace=True) elif materials.dtype not in np.sctypes['float'] + np.sctypes['int']:
self.set_homogenization(homogenization,inplace=True) raise TypeError(f'Invalid materials data type {materials.dtype}.')
self.set_comments(comments,inplace=True) else:
self.materials = np.copy(materials)
if self.materials.dtype in np.sctypes['float'] and \
np.all(self.materials == self.materials.astype(int).astype(float)):
self.materials = self.materials.astype(int)
if len(size) != 3 or any(np.array(size) <= 0):
raise ValueError(f'Invalid size {size}.')
else:
self.size = np.array(size)
if len(origin) != 3:
raise ValueError(f'Invalid origin {origin}.')
else:
self.origin = np.array(origin)
self.comments = [str(c) for c in comments] if isinstance(comments,list) else [str(comments)]
def __repr__(self): def __repr__(self):
@ -48,8 +63,8 @@ class Geom:
f'grid a b c: {util.srepr(self.grid, " x ")}', f'grid a b c: {util.srepr(self.grid, " x ")}',
f'size x y z: {util.srepr(self.size, " x ")}', f'size x y z: {util.srepr(self.size, " x ")}',
f'origin x y z: {util.srepr(self.origin," ")}', f'origin x y z: {util.srepr(self.origin," ")}',
f'# materialpoints: {self.N_microstructure}', f'# materialpoints: {self.N_materials}',
f'max materialpoint: {np.nanmax(self.microstructure)}', f'max materialpoint: {np.nanmax(self.materials)}',
]) ])
@ -63,42 +78,6 @@ class Geom:
return self.__copy__() return self.__copy__()
def duplicate(self,microstructure=None,size=None,origin=None,comments=None,autosize=False):
"""
Create a duplicate having updated microstructure, size, and origin.
Parameters
----------
microstructure : numpy.ndarray, optional
Microstructure array (3D).
size : list or numpy.ndarray, optional
Physical size of the microstructure in meter.
origin : list or numpy.ndarray, optional
Physical origin of the microstructure in meter.
comments : list of str, optional
Comment lines.
autosize : bool, optional
Ignore size parameter and rescale according to change of grid points.
"""
if size is not None and autosize:
raise ValueError('Auto-sizing conflicts with explicit size parameter.')
grid_old = self.grid
dup = self.set_microstructure(microstructure)\
.set_origin(origin)
if comments is not None:
dup.set_comments(comments,inplace=True)
if size is not None:
dup.set_size(size,inplace=True)
elif autosize:
dup.set_size(dup.grid/grid_old*self.size,inplace=True)
return dup
def diff(self,other): def diff(self,other):
""" """
Report property differences of self relative to other. Report property differences of self relative to other.
@ -114,154 +93,33 @@ class Geom:
message.append(util.delete(f'grid a b c: {util.srepr(other.grid," x ")}')) message.append(util.delete(f'grid a b c: {util.srepr(other.grid," x ")}'))
message.append(util.emph( f'grid a b c: {util.srepr( self.grid," x ")}')) message.append(util.emph( f'grid a b c: {util.srepr( self.grid," x ")}'))
if np.any(other.size != self.size): if not np.allclose(other.size,self.size):
message.append(util.delete(f'size x y z: {util.srepr(other.size," x ")}')) message.append(util.delete(f'size x y z: {util.srepr(other.size," x ")}'))
message.append(util.emph( f'size x y z: {util.srepr( self.size," x ")}')) message.append(util.emph( f'size x y z: {util.srepr( self.size," x ")}'))
if np.any(other.origin != self.origin): if not np.allclose(other.origin,self.origin):
message.append(util.delete(f'origin x y z: {util.srepr(other.origin," ")}')) message.append(util.delete(f'origin x y z: {util.srepr(other.origin," ")}'))
message.append(util.emph( f'origin x y z: {util.srepr( self.origin," ")}')) message.append(util.emph( f'origin x y z: {util.srepr( self.origin," ")}'))
if other.N_microstructure != self.N_microstructure: if other.N_materials != self.N_materials:
message.append(util.delete(f'# materialpoints: {other.N_microstructure}')) message.append(util.delete(f'# materialpoints: {other.N_materials}'))
message.append(util.emph( f'# materialpoints: { self.N_microstructure}')) message.append(util.emph( f'# materialpoints: { self.N_materials}'))
if np.nanmax(other.microstructure) != np.nanmax(self.microstructure): if np.nanmax(other.materials) != np.nanmax(self.materials):
message.append(util.delete(f'max materialpoint: {np.nanmax(other.microstructure)}')) message.append(util.delete(f'max materialpoint: {np.nanmax(other.materials)}'))
message.append(util.emph( f'max materialpoint: {np.nanmax( self.microstructure)}')) message.append(util.emph( f'max materialpoint: {np.nanmax( self.materials)}'))
return util.return_message(message) return util.return_message(message)
def set_comments(self,comments,inplace=False):
"""
Replace all existing comments.
Parameters
----------
comments : list of str
All comments.
"""
target = self if inplace else self.copy()
target.comments = []
target.add_comments(comments,inplace=True)
if not inplace: return target
def add_comments(self,comments,inplace=False):
"""
Append comments to existing comments.
Parameters
----------
comments : list of str
New comments.
"""
target = self if inplace else self.copy()
target.comments += [str(c) for c in comments] if isinstance(comments,list) else [str(comments)]
if not inplace: return target
def set_microstructure(self,microstructure,inplace=False):
"""
Replace the existing microstructure representation.
The complete microstructure is replaced (indcluding grid definition),
unless a masked array is provided in which case the grid dimensions
need to match and masked entries are not replaced.
Parameters
----------
microstructure : numpy.ndarray or numpy.ma.core.MaskedArray of shape (:,:,:)
Microstructure indices.
"""
target = self if inplace else self.copy()
if microstructure is not None:
if isinstance(microstructure,np.ma.core.MaskedArray):
target.microstructure = np.where(microstructure.mask,
target.microstructure,microstructure.data)
else:
target.microstructure = np.copy(microstructure)
if target.microstructure.dtype in np.sctypes['float'] and \
np.all(target.microstructure == target.microstructure.astype(int).astype(float)):
target.microstructure = target.microstructure.astype(int)
if len(target.microstructure.shape) != 3:
raise ValueError(f'Invalid microstructure shape {microstructure.shape}')
elif target.microstructure.dtype not in np.sctypes['float'] + np.sctypes['int']:
raise TypeError(f'Invalid microstructure data type {microstructure.dtype}')
if not inplace: return target
def set_size(self,size,inplace=False):
"""
Replace the existing size information.
Parameters
----------
size : list or numpy.ndarray
Physical size of the microstructure in meter.
"""
target = self if inplace else self.copy()
if size is not None:
if len(size) != 3 or any(np.array(size) <= 0):
raise ValueError(f'Invalid size {size}')
else:
target.size = np.array(size)
if not inplace: return target
def set_origin(self,origin,inplace=False):
"""
Replace the existing origin information.
Parameters
----------
origin : list or numpy.ndarray
Physical origin of the microstructure in meter.
"""
target = self if inplace else self.copy()
if origin is not None:
if len(origin) != 3:
raise ValueError(f'Invalid origin {origin}')
else:
target.origin = np.array(origin)
if not inplace: return target
def set_homogenization(self,homogenization,inplace=False):
"""
Replace the existing homogenization index.
Parameters
----------
homogenization : int
Homogenization index.
"""
target = self if inplace else self.copy()
if homogenization is not None:
if not isinstance(homogenization,int) or homogenization < 1:
raise TypeError(f'Invalid homogenization {homogenization}.')
else:
target.homogenization = homogenization
if not inplace: return target
@property @property
def grid(self): def grid(self):
return np.asarray(self.microstructure.shape) return np.asarray(self.materials.shape)
@property @property
def N_microstructure(self): def N_materials(self):
return np.unique(self.microstructure).size return np.unique(self.materials).size
@staticmethod @staticmethod
@ -301,12 +159,10 @@ class Geom:
size = np.array([float(dict(zip(items[1::2],items[2::2]))[i]) for i in ['x','y','z']]) size = np.array([float(dict(zip(items[1::2],items[2::2]))[i]) for i in ['x','y','z']])
elif key == 'origin': elif key == 'origin':
origin = np.array([float(dict(zip(items[1::2],items[2::2]))[i]) for i in ['x','y','z']]) origin = np.array([float(dict(zip(items[1::2],items[2::2]))[i]) for i in ['x','y','z']])
elif key == 'homogenization':
homogenization = int(items[1])
else: else:
comments.append(line.strip()) comments.append(line.strip())
microstructure = np.empty(grid.prod()) # initialize as flat array materials = np.empty(grid.prod()) # initialize as flat array
i = 0 i = 0
for line in content[header_length:]: for line in content[header_length:]:
items = line.split('#')[0].split() items = line.split('#')[0].split()
@ -318,16 +174,16 @@ class Geom:
abs(int(items[2])-int(items[0]))+1,dtype=float) abs(int(items[2])-int(items[0]))+1,dtype=float)
else: items = list(map(float,items)) else: items = list(map(float,items))
else: items = list(map(float,items)) else: items = list(map(float,items))
microstructure[i:i+len(items)] = items materials[i:i+len(items)] = items
i += len(items) i += len(items)
if i != grid.prod(): if i != grid.prod():
raise TypeError(f'Invalid file: expected {grid.prod()} entries, found {i}') raise TypeError(f'Invalid file: expected {grid.prod()} entries, found {i}')
if not np.any(np.mod(microstructure,1) != 0.0): # no float present if not np.any(np.mod(materials,1) != 0.0): # no float present
microstructure = microstructure.astype('int') materials = materials.astype('int')
return Geom(microstructure.reshape(grid,order='F'),size,origin,homogenization,comments) return Geom(materials.reshape(grid,order='F'),size,origin,comments)
@staticmethod @staticmethod
@ -365,7 +221,7 @@ class Geom:
grid : int numpy.ndarray of shape (3) grid : int numpy.ndarray of shape (3)
Number of grid points in x,y,z direction. Number of grid points in x,y,z direction.
size : list or numpy.ndarray of shape (3) size : list or numpy.ndarray of shape (3)
Physical size of the microstructure in meter. Physical size of the geometry in meter.
seeds : numpy.ndarray of shape (:,3) seeds : numpy.ndarray of shape (:,3)
Position of the seed points in meter. All points need to lay within the box. Position of the seed points in meter. All points need to lay within the box.
weights : numpy.ndarray of shape (seeds.shape[0]) weights : numpy.ndarray of shape (seeds.shape[0])
@ -389,15 +245,16 @@ class Geom:
result = pool.map_async(partial(Geom._find_closest_seed,seeds_p,weights_p), [coord for coord in coords]) result = pool.map_async(partial(Geom._find_closest_seed,seeds_p,weights_p), [coord for coord in coords])
pool.close() pool.close()
pool.join() pool.join()
microstructure = np.array(result.get()) materials = np.array(result.get())
if periodic: if periodic:
microstructure = microstructure.reshape(grid*3) materials = materials.reshape(grid*3)
microstructure = microstructure[grid[0]:grid[0]*2,grid[1]:grid[1]*2,grid[2]:grid[2]*2]%seeds.shape[0] materials = materials[grid[0]:grid[0]*2,grid[1]:grid[1]*2,grid[2]:grid[2]*2]%seeds.shape[0]
else: else:
microstructure = microstructure.reshape(grid) materials = materials.reshape(grid)
return Geom(microstructure+1,size,homogenization=1, return Geom(materials = materials+1,
size = size,
comments = util.execution_stamp('Geom','from_Laguerre_tessellation'), comments = util.execution_stamp('Geom','from_Laguerre_tessellation'),
) )
@ -412,7 +269,7 @@ class Geom:
grid : int numpy.ndarray of shape (3) grid : int numpy.ndarray of shape (3)
Number of grid points in x,y,z direction. Number of grid points in x,y,z direction.
size : list or numpy.ndarray of shape (3) size : list or numpy.ndarray of shape (3)
Physical size of the microstructure in meter. Physical size of the geometry in meter.
seeds : numpy.ndarray of shape (:,3) seeds : numpy.ndarray of shape (:,3)
Position of the seed points in meter. All points need to lay within the box. Position of the seed points in meter. All points need to lay within the box.
periodic : Boolean, optional periodic : Boolean, optional
@ -421,9 +278,10 @@ class Geom:
""" """
coords = grid_filters.cell_coord0(grid,size).reshape(-1,3) coords = grid_filters.cell_coord0(grid,size).reshape(-1,3)
KDTree = spatial.cKDTree(seeds,boxsize=size) if periodic else spatial.cKDTree(seeds) KDTree = spatial.cKDTree(seeds,boxsize=size) if periodic else spatial.cKDTree(seeds)
devNull,microstructure = KDTree.query(coords) devNull,materials = KDTree.query(coords)
return Geom(microstructure.reshape(grid)+1,size,homogenization=1, return Geom(materials = materials.reshape(grid)+1,
size = size,
comments = util.execution_stamp('Geom','from_Voronoi_tessellation'), comments = util.execution_stamp('Geom','from_Voronoi_tessellation'),
) )
@ -462,21 +320,21 @@ class Geom:
+[ 'grid a {} b {} c {}'.format(*geom.grid), +[ 'grid a {} b {} c {}'.format(*geom.grid),
'size x {} y {} z {}'.format(*geom.size), 'size x {} y {} z {}'.format(*geom.size),
'origin x {} y {} z {}'.format(*geom.origin), 'origin x {} y {} z {}'.format(*geom.origin),
f'homogenization {geom.homogenization}', 'homogenization 1',
] ]
grid = geom.grid grid = geom.grid
if pack is None: if pack is None:
plain = grid.prod()/geom.N_microstructure < 250 plain = grid.prod()/geom.N_materials < 250
else: else:
plain = not pack plain = not pack
if plain: if plain:
format_string = '%g' if geom.microstructure.dtype in np.sctypes['float'] else \ format_string = '%g' if geom.materials.dtype in np.sctypes['float'] else \
'%{}i'.format(1+int(np.floor(np.log10(np.nanmax(geom.microstructure))))) '%{}i'.format(1+int(np.floor(np.log10(np.nanmax(geom.materials)))))
np.savetxt(fname, np.savetxt(fname,
geom.microstructure.reshape([grid[0],np.prod(grid[1:])],order='F').T, geom.materials.reshape([grid[0],np.prod(grid[1:])],order='F').T,
header='\n'.join(header), fmt=format_string, comments='') header='\n'.join(header), fmt=format_string, comments='')
else: else:
try: try:
@ -487,7 +345,7 @@ class Geom:
compressType = None compressType = None
former = start = -1 former = start = -1
reps = 0 reps = 0
for current in geom.microstructure.flatten('F'): for current in geom.materials.flatten('F'):
if abs(current - former) == 1 and (start - current) == reps*(former - current): if abs(current - former) == 1 and (start - current) == reps*(former - current):
compressType = 'to' compressType = 'to'
reps += 1 reps += 1
@ -532,7 +390,7 @@ class Geom:
""" """
v = VTK.from_rectilinearGrid(geom.grid,geom.size,geom.origin) v = VTK.from_rectilinearGrid(geom.grid,geom.size,geom.origin)
v.add(geom.microstructure.flatten(order='F'),'materialpoint') v.add(geom.materials.flatten(order='F'),'materialpoint')
v.add_comments(geom.comments) v.add_comments(geom.comments)
if fname: if fname:
@ -575,11 +433,11 @@ class Geom:
0 gives octahedron (|x|^(2^0) + |y|^(2^0) + |z|^(2^0) < 1) 0 gives octahedron (|x|^(2^0) + |y|^(2^0) + |z|^(2^0) < 1)
1 gives a sphere (|x|^(2^1) + |y|^(2^1) + |z|^(2^1) < 1) 1 gives a sphere (|x|^(2^1) + |y|^(2^1) + |z|^(2^1) < 1)
fill : int, optional fill : int, optional
Fill value for primitive. Defaults to microstructure.max() + 1. Fill value for primitive. Defaults to materials.max() + 1.
R : damask.Rotation, optional R : damask.Rotation, optional
Rotation of primitive. Defaults to no rotation. Rotation of primitive. Defaults to no rotation.
inverse : Boolean, optional inverse : Boolean, optional
Retain original microstructure within primitive and fill outside. Retain original materials within primitive and fill outside.
Defaults to False. Defaults to False.
periodic : Boolean, optional periodic : Boolean, optional
Repeat primitive over boundaries. Defaults to True. Repeat primitive over boundaries. Defaults to True.
@ -601,22 +459,23 @@ class Geom:
if periodic: # translate back to center if periodic: # translate back to center
mask = np.roll(mask,((c-np.ones(3)*.5)*self.grid).astype(int),(0,1,2)) mask = np.roll(mask,((c-np.ones(3)*.5)*self.grid).astype(int),(0,1,2))
fill_ = np.full_like(self.microstructure,np.nanmax(self.microstructure)+1 if fill is None else fill) fill_ = np.full_like(self.materials,np.nanmax(self.materials)+1 if fill is None else fill)
ms = np.ma.MaskedArray(fill_,np.logical_not(mask) if inverse else mask)
return self.duplicate(ms, return Geom(materials = np.where(np.logical_not(mask) if inverse else mask, self.materials,fill_),
size = self.size,
origin = self.origin,
comments = self.comments+[util.execution_stamp('Geom','add_primitive')], comments = self.comments+[util.execution_stamp('Geom','add_primitive')],
) )
def mirror(self,directions,reflect=False): def mirror(self,directions,reflect=False):
""" """
Mirror microstructure along given directions. Mirror geometry along given directions.
Parameters Parameters
---------- ----------
directions : iterable containing str directions : iterable containing str
Direction(s) along which the microstructure is mirrored. Direction(s) along which the geometry is mirrored.
Valid entries are 'x', 'y', 'z'. Valid entries are 'x', 'y', 'z'.
reflect : bool, optional reflect : bool, optional
Reflect (include) outermost layers. Defaults to False. Reflect (include) outermost layers. Defaults to False.
@ -627,28 +486,30 @@ class Geom:
raise ValueError(f'Invalid direction {set(directions).difference(valid)} specified.') raise ValueError(f'Invalid direction {set(directions).difference(valid)} specified.')
limits = [None,None] if reflect else [-2,0] limits = [None,None] if reflect else [-2,0]
ms = self.microstructure.copy() ms = self.materials.copy()
if 'z' in directions:
ms = np.concatenate([ms,ms[:,:,limits[0]:limits[1]:-1]],2)
if 'y' in directions:
ms = np.concatenate([ms,ms[:,limits[0]:limits[1]:-1,:]],1)
if 'x' in directions: if 'x' in directions:
ms = np.concatenate([ms,ms[limits[0]:limits[1]:-1,:,:]],0) ms = np.concatenate([ms,ms[limits[0]:limits[1]:-1,:,:]],0)
if 'y' in directions:
ms = np.concatenate([ms,ms[:,limits[0]:limits[1]:-1,:]],1)
if 'z' in directions:
ms = np.concatenate([ms,ms[:,:,limits[0]:limits[1]:-1]],2)
return self.duplicate(ms, return Geom(materials = ms,
size = self.size/self.grid*np.asarray(ms.shape),
origin = self.origin,
comments = self.comments+[util.execution_stamp('Geom','mirror')], comments = self.comments+[util.execution_stamp('Geom','mirror')],
autosize=True) )
def flip(self,directions): def flip(self,directions):
""" """
Flip microstructure along given directions. Flip geometry along given directions.
Parameters Parameters
---------- ----------
directions : iterable containing str directions : iterable containing str
Direction(s) along which the microstructure is flipped. Direction(s) along which the geometry is flipped.
Valid entries are 'x', 'y', 'z'. Valid entries are 'x', 'y', 'z'.
""" """
@ -656,16 +517,18 @@ class Geom:
if not set(directions).issubset(valid): if not set(directions).issubset(valid):
raise ValueError(f'Invalid direction {set(directions).difference(valid)} specified.') raise ValueError(f'Invalid direction {set(directions).difference(valid)} specified.')
ms = np.flip(self.microstructure, (valid.index(d) for d in directions if d in valid)) ms = np.flip(self.materials, (valid.index(d) for d in directions if d in valid))
return self.duplicate(ms, return Geom(materials = ms,
size = self.size,
origin = self.origin,
comments = self.comments+[util.execution_stamp('Geom','flip')], comments = self.comments+[util.execution_stamp('Geom','flip')],
) )
def scale(self,grid,periodic=True): def scale(self,grid,periodic=True):
""" """
Scale microstructure to new grid. Scale geometry to new grid.
Parameters Parameters
---------- ----------
@ -675,21 +538,23 @@ class Geom:
Assume geometry to be periodic. Defaults to True. Assume geometry to be periodic. Defaults to True.
""" """
return self.duplicate(ndimage.interpolation.zoom( return Geom(materials = ndimage.interpolation.zoom(
self.microstructure, self.materials,
grid/self.grid, grid/self.grid,
output=self.microstructure.dtype, output=self.materials.dtype,
order=0, order=0,
mode=('wrap' if periodic else 'nearest'), mode=('wrap' if periodic else 'nearest'),
prefilter=False prefilter=False
), ),
size = self.size,
origin = self.origin,
comments = self.comments+[util.execution_stamp('Geom','scale')], comments = self.comments+[util.execution_stamp('Geom','scale')],
) )
def clean(self,stencil=3,selection=None,periodic=True): def clean(self,stencil=3,selection=None,periodic=True):
""" """
Smooth microstructure by selecting most frequent index within given stencil at each location. Smooth geometry by selecting most frequent material index within given stencil at each location.
Parameters Parameters
---------- ----------
@ -709,83 +574,87 @@ class Geom:
else: else:
return me return me
return self.duplicate(ndimage.filters.generic_filter( return Geom(materials = ndimage.filters.generic_filter(
self.microstructure, self.materials,
mostFrequent, mostFrequent,
size=(stencil if selection is None else stencil//2*2+1,)*3, size=(stencil if selection is None else stencil//2*2+1,)*3,
mode=('wrap' if periodic else 'nearest'), mode=('wrap' if periodic else 'nearest'),
extra_keywords=dict(selection=selection), extra_keywords=dict(selection=selection),
).astype(self.microstructure.dtype), ).astype(self.materials.dtype),
size = self.size,
origin = self.origin,
comments = self.comments+[util.execution_stamp('Geom','clean')], comments = self.comments+[util.execution_stamp('Geom','clean')],
) )
def renumber(self): def renumber(self):
"""Renumber sorted microstructure indices to 1,...,N.""" """Renumber sorted material indices to 1,...,N."""
renumbered = np.empty(self.grid,dtype=self.microstructure.dtype) renumbered = np.empty(self.grid,dtype=self.materials.dtype)
for i, oldID in enumerate(np.unique(self.microstructure)): for i, oldID in enumerate(np.unique(self.materials)):
renumbered = np.where(self.microstructure == oldID, i+1, renumbered) renumbered = np.where(self.materials == oldID, i+1, renumbered)
return self.duplicate(renumbered, return Geom(materials = renumbered,
size = self.size,
origin = self.origin,
comments = self.comments+[util.execution_stamp('Geom','renumber')], comments = self.comments+[util.execution_stamp('Geom','renumber')],
) )
def rotate(self,R,fill=None): def rotate(self,R,fill=None):
""" """
Rotate microstructure (pad if required). Rotate geometry (pad if required).
Parameters Parameters
---------- ----------
R : damask.Rotation R : damask.Rotation
Rotation to apply to the microstructure. Rotation to apply to the geometry.
fill : int or float, optional fill : int or float, optional
Microstructure index to fill the corners. Defaults to microstructure.max() + 1. Material index to fill the corners. Defaults to materials.max() + 1.
""" """
if fill is None: fill = np.nanmax(self.microstructure) + 1 if fill is None: fill = np.nanmax(self.materials) + 1
dtype = float if np.isnan(fill) or int(fill) != fill or self.microstructure.dtype==np.float else int dtype = float if np.isnan(fill) or int(fill) != fill or self.materials.dtype==np.float else int
Eulers = R.as_Eulers(degrees=True) Eulers = R.as_Eulers(degrees=True)
microstructure_in = self.microstructure.copy() materials_in = self.materials.copy()
# These rotations are always applied in the reference coordinate system, i.e. (z,x,z) not (z,x',z'') # These rotations are always applied in the reference coordinate system, i.e. (z,x,z) not (z,x',z'')
# see https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf # see https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf
for angle,axes in zip(Eulers[::-1], [(0,1),(1,2),(0,1)]): for angle,axes in zip(Eulers[::-1], [(0,1),(1,2),(0,1)]):
microstructure_out = ndimage.rotate(microstructure_in,angle,axes,order=0, materials_out = ndimage.rotate(materials_in,angle,axes,order=0,
prefilter=False,output=dtype,cval=fill) prefilter=False,output=dtype,cval=fill)
if np.prod(microstructure_in.shape) == np.prod(microstructure_out.shape): if np.prod(materials_in.shape) == np.prod(materials_out.shape):
# avoid scipy interpolation errors for rotations close to multiples of 90° # avoid scipy interpolation errors for rotations close to multiples of 90°
microstructure_in = np.rot90(microstructure_in,k=np.rint(angle/90.).astype(int),axes=axes) materials_in = np.rot90(materials_in,k=np.rint(angle/90.).astype(int),axes=axes)
else: else:
microstructure_in = microstructure_out materials_in = materials_out
origin = self.origin-(np.asarray(microstructure_in.shape)-self.grid)*.5 * self.size/self.grid origin = self.origin-(np.asarray(materials_in.shape)-self.grid)*.5 * self.size/self.grid
return self.duplicate(microstructure_in, return Geom(materials = materials_in,
size = self.size/self.grid*np.asarray(materials_in.shape),
origin = origin, origin = origin,
comments = self.comments+[util.execution_stamp('Geom','rotate')], comments = self.comments+[util.execution_stamp('Geom','rotate')],
autosize=True,
) )
def canvas(self,grid=None,offset=None,fill=None): def canvas(self,grid=None,offset=None,fill=None):
""" """
Crop or enlarge/pad microstructure. Crop or enlarge/pad geometry.
Parameters Parameters
---------- ----------
grid : numpy.ndarray of shape (3) grid : numpy.ndarray of shape (3)
Number of grid points in x,y,z direction. Number of grid points in x,y,z direction.
offset : numpy.ndarray of shape (3) offset : numpy.ndarray of shape (3)
Offset (measured in grid points) from old to new microstructure[0,0,0]. Offset (measured in grid points) from old to new geometry [0,0,0].
fill : int or float, optional fill : int or float, optional
Microstructure index to fill the background. Defaults to microstructure.max() + 1. Material index to fill the background. Defaults to materials.max() + 1.
""" """
if offset is None: offset = 0 if offset is None: offset = 0
if fill is None: fill = np.nanmax(self.microstructure) + 1 if fill is None: fill = np.nanmax(self.materials) + 1
dtype = float if int(fill) != fill or self.microstructure.dtype in np.sctypes['float'] else int dtype = float if int(fill) != fill or self.materials.dtype in np.sctypes['float'] else int
canvas = np.full(self.grid if grid is None else grid,fill,dtype) canvas = np.full(self.grid if grid is None else grid,fill,dtype)
@ -794,39 +663,41 @@ class Geom:
ll = np.clip(-offset, 0,np.minimum( grid,self.grid-offset)) ll = np.clip(-offset, 0,np.minimum( grid,self.grid-offset))
ur = np.clip(-offset+self.grid,0,np.minimum( grid,self.grid-offset)) ur = np.clip(-offset+self.grid,0,np.minimum( grid,self.grid-offset))
canvas[ll[0]:ur[0],ll[1]:ur[1],ll[2]:ur[2]] = self.microstructure[LL[0]:UR[0],LL[1]:UR[1],LL[2]:UR[2]] canvas[ll[0]:ur[0],ll[1]:ur[1],ll[2]:ur[2]] = self.materials[LL[0]:UR[0],LL[1]:UR[1],LL[2]:UR[2]]
return self.duplicate(canvas, return Geom(materials = canvas,
size = self.size/self.grid*np.asarray(canvas.shape),
origin = self.origin+offset*self.size/self.grid, origin = self.origin+offset*self.size/self.grid,
comments = self.comments+[util.execution_stamp('Geom','canvas')], comments = self.comments+[util.execution_stamp('Geom','canvas')],
autosize=True,
) )
def substitute(self,from_microstructure,to_microstructure): def substitute(self,from_materials,to_materials):
""" """
Substitute microstructure indices. Substitute material indices.
Parameters Parameters
---------- ----------
from_microstructure : iterable of ints from_materials : iterable of ints
Microstructure indices to be substituted. Material indices to be substituted.
to_microstructure : iterable of ints to_materials : iterable of ints
New microstructure indices. New material indices.
""" """
substituted = self.microstructure.copy() substituted = self.materials.copy()
for from_ms,to_ms in zip(from_microstructure,to_microstructure): for from_ms,to_ms in zip(from_materials,to_materials):
substituted[self.microstructure==from_ms] = to_ms substituted[self.materials==from_ms] = to_ms
return self.duplicate(substituted, return Geom(materials = substituted,
size = self.size,
origin = self.origin,
comments = self.comments+[util.execution_stamp('Geom','substitute')], comments = self.comments+[util.execution_stamp('Geom','substitute')],
) )
def vicinity_offset(self,vicinity=1,offset=None,trigger=[],periodic=True): def vicinity_offset(self,vicinity=1,offset=None,trigger=[],periodic=True):
""" """
Offset microstructure index of points in the vicinity of xxx. Offset material index of points in the vicinity of xxx.
Different from themselves (or listed as triggers) within a given (cubic) vicinity, Different from themselves (or listed as triggers) within a given (cubic) vicinity,
i.e. within the region close to a grain/phase boundary. i.e. within the region close to a grain/phase boundary.
@ -835,14 +706,14 @@ class Geom:
Parameters Parameters
---------- ----------
vicinity : int, optional vicinity : int, optional
Voxel distance checked for presence of other microstructure. Voxel distance checked for presence of other materials.
Defaults to 1. Defaults to 1.
offset : int, optional offset : int, optional
Offset (positive or negative) to tag microstructure indices, Offset (positive or negative) to tag material indices,
defaults to microstructure.max() + 1. defaults to materials.max() + 1.
trigger : list of ints, optional trigger : list of ints, optional
List of microstructure indices triggering a change. List of material indices that trigger a change.
Defaults to [], meaning that different neigboors trigger a change. Defaults to [], meaning that any different neighbor triggers a change.
periodic : Boolean, optional periodic : Boolean, optional
Assume geometry to be periodic. Defaults to True. Assume geometry to be periodic. Defaults to True.
@ -858,14 +729,15 @@ class Geom:
trigger = list(trigger) trigger = list(trigger)
return np.any(np.in1d(stencil,np.array(trigger))) return np.any(np.in1d(stencil,np.array(trigger)))
offset_ = np.nanmax(self.microstructure) if offset is None else offset offset_ = np.nanmax(self.materials) if offset is None else offset
mask = ndimage.filters.generic_filter(self.microstructure, mask = ndimage.filters.generic_filter(self.materials,
tainted_neighborhood, tainted_neighborhood,
size=1+2*vicinity, size=1+2*vicinity,
mode='wrap' if periodic else 'nearest', mode='wrap' if periodic else 'nearest',
extra_keywords={'trigger':trigger}) extra_keywords={'trigger':trigger})
microstructure = np.ma.MaskedArray(self.microstructure + offset_, np.logical_not(mask))
return self.duplicate(microstructure, return Geom(materials = np.where(mask, self.materials + offset_,self.materials),
size = self.size,
origin = self.origin,
comments = self.comments+[util.execution_stamp('Geom','vicinity_offset')], comments = self.comments+[util.execution_stamp('Geom','vicinity_offset')],
) )

View File

@ -11,7 +11,7 @@ from damask import util
def geom_equal(a,b): def geom_equal(a,b):
return np.all(a.microstructure == b.microstructure) and \ return np.all(a.materials == b.materials) and \
np.all(a.grid == b.grid) and \ np.all(a.grid == b.grid) and \
np.allclose(a.size, b.size) and \ np.allclose(a.size, b.size) and \
str(a.diff(b)) == str(b.diff(a)) str(a.diff(b)) == str(b.diff(a))
@ -33,64 +33,21 @@ def reference_dir(reference_dir_base):
class TestGeom: class TestGeom:
@pytest.mark.parametrize('flavor',['plain','explicit'])
def test_duplicate(self,default,flavor):
if flavor == 'plain':
modified = default.duplicate()
elif flavor == 'explicit':
modified = default.duplicate(
default.microstructure,
default.size,
default.origin
)
print(modified)
assert geom_equal(default,modified)
def test_diff_equal(self,default): def test_diff_equal(self,default):
assert str(default.diff(default)) == '' assert str(default.diff(default)) == ''
def test_diff_not_equal(self,default): def test_diff_not_equal(self,default):
new = Geom(default.microstructure[1:,1:,1:]+1,default.size*.9,np.ones(3)-default.origin,comments=['modified']) new = Geom(default.materials[1:,1:,1:]+1,default.size*.9,np.ones(3)-default.origin,comments=['modified'])
assert str(default.diff(new)) != '' assert str(default.diff(new)) != ''
def test_set_inplace_outofplace_homogenization(self,default):
default.set_homogenization(123,inplace=True)
outofplace = default.set_homogenization(321,inplace=False)
assert default.homogenization == 123 and outofplace.homogenization == 321
def test_set_inplace_outofplace_microstructure(self,default):
default.set_microstructure(np.arange(72).reshape((2,4,9)),inplace=True)
outofplace = default.set_microstructure(np.arange(72).reshape((8,3,3)),inplace=False)
assert np.array_equal(default.grid,[2,4,9]) and np.array_equal(outofplace.grid,[8,3,3])
def test_set_inplace_outofplace_size(self,default):
default.set_size(np.array([1,2,3]),inplace=True)
outofplace = default.set_size(np.array([3,2,1]),inplace=False)
assert np.array_equal(default.size,[1,2,3]) and np.array_equal(outofplace.size,[3,2,1])
def test_set_inplace_outofplace_comments(self,default):
default.set_comments(['a','and','b'],inplace=True)
outofplace = default.set_comments(['b','or','a'],inplace=False)
assert default.comments == ['a','and','b'] and outofplace.comments == ['b','or','a']
@pytest.mark.parametrize('masked',[True,False])
def test_set_microstructure(self,default,masked):
old = default.microstructure
new = np.random.randint(200,size=default.grid)
default.set_microstructure(np.ma.MaskedArray(new,np.full_like(new,masked)),inplace=True)
assert np.all(default.microstructure==(old if masked else new))
def test_write_read_str(self,default,tmpdir): def test_write_read_str(self,default,tmpdir):
default.to_file(str(tmpdir/'default.geom'),format='ASCII') default.to_file(str(tmpdir/'default.geom'),format='ASCII')
new = Geom.from_file(str(tmpdir/'default.geom')) new = Geom.from_file(str(tmpdir/'default.geom'))
assert geom_equal(default,new) assert geom_equal(default,new)
def test_write_read_file(self,default,tmpdir): def test_write_read_file(self,default,tmpdir):
with open(tmpdir/'default.geom','w') as f: with open(tmpdir/'default.geom','w') as f:
default.to_file(f,format='ASCII',pack=True) default.to_file(f,format='ASCII',pack=True)
@ -98,6 +55,7 @@ class TestGeom:
new = Geom.from_file(f) new = Geom.from_file(f)
assert geom_equal(default,new) assert geom_equal(default,new)
def test_write_as_ASCII(self,default,tmpdir): def test_write_as_ASCII(self,default,tmpdir):
with open(tmpdir/'str.geom','w') as f: with open(tmpdir/'str.geom','w') as f:
f.write(default.as_ASCII()) f.write(default.as_ASCII())
@ -105,6 +63,7 @@ class TestGeom:
new = Geom.from_file(f) new = Geom.from_file(f)
assert geom_equal(default,new) assert geom_equal(default,new)
def test_read_write_vtr(self,default,tmpdir): def test_read_write_vtr(self,default,tmpdir):
default.to_file(tmpdir/'default',format='vtr') default.to_file(tmpdir/'default',format='vtr')
for _ in range(10): for _ in range(10):
@ -114,6 +73,7 @@ class TestGeom:
new = Geom.from_vtr(tmpdir/'default.vtr') new = Geom.from_vtr(tmpdir/'default.vtr')
assert geom_equal(new,default) assert geom_equal(new,default)
def test_invalid_geom(self,tmpdir): def test_invalid_geom(self,tmpdir):
with open('invalid_file','w') as f: with open('invalid_file','w') as f:
f.write('this is not a valid header') f.write('this is not a valid header')
@ -121,6 +81,7 @@ class TestGeom:
with pytest.raises(TypeError): with pytest.raises(TypeError):
Geom.from_file(f) Geom.from_file(f)
def test_invalid_vtr(self,tmpdir): def test_invalid_vtr(self,tmpdir):
v = VTK.from_rectilinearGrid(np.random.randint(5,10,3)*2,np.random.random(3) + 1.0) v = VTK.from_rectilinearGrid(np.random.randint(5,10,3)*2,np.random.random(3) + 1.0)
v.to_file(tmpdir/'no_materialpoint.vtr') v.to_file(tmpdir/'no_materialpoint.vtr')
@ -137,36 +98,38 @@ class TestGeom:
new = Geom.from_file(tmpdir/'default.geom') new = Geom.from_file(tmpdir/'default.geom')
assert geom_equal(new,default) assert geom_equal(new,default)
def test_invalid_combination(self,default):
with pytest.raises(ValueError):
default.duplicate(default.microstructure[1:,1:,1:],size=np.ones(3), autosize=True)
def test_invalid_size(self,default): def test_invalid_size(self,default):
with pytest.raises(ValueError): with pytest.raises(ValueError):
default.duplicate(default.microstructure[1:,1:,1:],size=np.ones(2)) Geom(default.materials[1:,1:,1:],
size=np.ones(2))
def test_invalid_origin(self,default): def test_invalid_origin(self,default):
with pytest.raises(ValueError): with pytest.raises(ValueError):
default.duplicate(default.microstructure[1:,1:,1:],origin=np.ones(4)) Geom(default.materials[1:,1:,1:],
size=np.ones(3),
origin=np.ones(4))
def test_invalid_microstructure_size(self,default):
microstructure = np.ones((3,3)) def test_invalid_materials_shape(self,default):
materials = np.ones((3,3))
with pytest.raises(ValueError): with pytest.raises(ValueError):
default.duplicate(microstructure) Geom(materials,
size=np.ones(3))
def test_invalid_microstructure_type(self,default):
microstructure = np.random.randint(1,300,(3,4,5))==1
with pytest.raises(TypeError):
default.duplicate(microstructure)
def test_invalid_homogenization(self,default): def test_invalid_materials_type(self,default):
materials = np.random.randint(1,300,(3,4,5))==1
with pytest.raises(TypeError): with pytest.raises(TypeError):
default.set_homogenization(homogenization=0) Geom(materials)
def test_invalid_write_format(self,default): def test_invalid_write_format(self,default):
with pytest.raises(TypeError): with pytest.raises(TypeError):
default.to_file(format='invalid') default.to_file(format='invalid')
@pytest.mark.parametrize('directions,reflect',[ @pytest.mark.parametrize('directions,reflect',[
(['x'], False), (['x'], False),
(['x','y','z'],True), (['x','y','z'],True),
@ -182,6 +145,7 @@ class TestGeom:
assert geom_equal(Geom.from_file(reference), assert geom_equal(Geom.from_file(reference),
modified) modified)
@pytest.mark.parametrize('directions',[(1,2,'y'),('a','b','x'),[1]]) @pytest.mark.parametrize('directions',[(1,2,'y'),('a','b','x'),[1]])
def test_mirror_invalid(self,default,directions): def test_mirror_invalid(self,default,directions):
with pytest.raises(ValueError): with pytest.raises(ValueError):
@ -203,13 +167,16 @@ class TestGeom:
assert geom_equal(Geom.from_file(reference), assert geom_equal(Geom.from_file(reference),
modified) modified)
def test_flip_invariant(self,default): def test_flip_invariant(self,default):
assert geom_equal(default,default.flip([])) assert geom_equal(default,default.flip([]))
@pytest.mark.parametrize('direction',[['x'],['x','y']]) @pytest.mark.parametrize('direction',[['x'],['x','y']])
def test_flip_double(self,default,direction): def test_flip_double(self,default,direction):
assert geom_equal(default,default.flip(direction).flip(direction)) assert geom_equal(default,default.flip(direction).flip(direction))
@pytest.mark.parametrize('directions',[(1,2,'y'),('a','b','x'),[1]]) @pytest.mark.parametrize('directions',[(1,2,'y'),('a','b','x'),[1]])
def test_flip_invalid(self,default,directions): def test_flip_invalid(self,default,directions):
with pytest.raises(ValueError): with pytest.raises(ValueError):
@ -231,6 +198,7 @@ class TestGeom:
current current
) )
@pytest.mark.parametrize('grid',[ @pytest.mark.parametrize('grid',[
(10,11,10), (10,11,10),
[10,13,10], [10,13,10],
@ -248,22 +216,29 @@ class TestGeom:
assert geom_equal(Geom.from_file(reference), assert geom_equal(Geom.from_file(reference),
modified) modified)
def test_renumber(self,default): def test_renumber(self,default):
microstructure = default.microstructure.copy() materials = default.materials.copy()
for m in np.unique(microstructure): for m in np.unique(materials):
microstructure[microstructure==m] = microstructure.max() + np.random.randint(1,30) materials[materials==m] = materials.max() + np.random.randint(1,30)
modified = default.duplicate(microstructure) modified = Geom(materials,
default.size,
default.origin)
assert not geom_equal(modified,default) assert not geom_equal(modified,default)
assert geom_equal(default, assert geom_equal(default,
modified.renumber()) modified.renumber())
def test_substitute(self,default): def test_substitute(self,default):
offset = np.random.randint(1,500) offset = np.random.randint(1,500)
modified = default.duplicate(default.microstructure + offset) modified = Geom(default.materials + offset,
default.size,
default.origin)
assert not geom_equal(modified,default) assert not geom_equal(modified,default)
assert geom_equal(default, assert geom_equal(default,
modified.substitute(np.arange(default.microstructure.max())+1+offset, modified.substitute(np.arange(default.materials.max())+1+offset,
np.arange(default.microstructure.max())+1)) np.arange(default.materials.max())+1))
@pytest.mark.parametrize('axis_angle',[np.array([1,0,0,86.7]), np.array([0,1,0,90.4]), np.array([0,0,1,90]), @pytest.mark.parametrize('axis_angle',[np.array([1,0,0,86.7]), np.array([0,1,0,90.4]), np.array([0,0,1,90]),
np.array([1,0,0,175]),np.array([0,-1,0,178]),np.array([0,0,1,180])]) np.array([1,0,0,175]),np.array([0,-1,0,178]),np.array([0,0,1,180])])
@ -273,6 +248,7 @@ class TestGeom:
modified.rotate(Rotation.from_axis_angle(axis_angle,degrees=True)) modified.rotate(Rotation.from_axis_angle(axis_angle,degrees=True))
assert geom_equal(default,modified) assert geom_equal(default,modified)
@pytest.mark.parametrize('Eulers',[[32.0,68.0,21.0], @pytest.mark.parametrize('Eulers',[[32.0,68.0,21.0],
[0.0,32.0,240.0]]) [0.0,32.0,240.0]])
def test_rotate(self,default,update,reference_dir,Eulers): def test_rotate(self,default,update,reference_dir,Eulers):
@ -283,11 +259,13 @@ class TestGeom:
assert geom_equal(Geom.from_file(reference), assert geom_equal(Geom.from_file(reference),
modified) modified)
def test_canvas(self,default): def test_canvas(self,default):
grid = default.grid grid = default.grid
grid_add = np.random.randint(0,30,(3)) grid_add = np.random.randint(0,30,(3))
modified = default.canvas(grid + grid_add) modified = default.canvas(grid + grid_add)
assert np.all(modified.microstructure[:grid[0],:grid[1],:grid[2]] == default.microstructure) assert np.all(modified.materials[:grid[0],:grid[1],:grid[2]] == default.materials)
@pytest.mark.parametrize('center1,center2',[(np.random.random(3)*.5,np.random.random()*8), @pytest.mark.parametrize('center1,center2',[(np.random.random(3)*.5,np.random.random()*8),
(np.random.randint(4,8,(3)),np.random.randint(9,12,(3)))]) (np.random.randint(4,8,(3)),np.random.randint(9,12,(3)))])
@ -300,13 +278,14 @@ class TestGeom:
np.random.rand()*4, np.random.rand()*4,
np.random.randint(20)]) np.random.randint(20)])
def test_add_primitive_shift(self,center1,center2,diameter,exponent): def test_add_primitive_shift(self,center1,center2,diameter,exponent):
"""Same volume fraction for periodic microstructures and different center.""" """Same volume fraction for periodic geometries and different center."""
o = np.random.random(3)-.5 o = np.random.random(3)-.5
g = np.random.randint(8,32,(3)) g = np.random.randint(8,32,(3))
s = np.random.random(3)+.5 s = np.random.random(3)+.5
G_1 = Geom(np.ones(g,'i'),s,o).add_primitive(diameter,center1,exponent) G_1 = Geom(np.ones(g,'i'),s,o).add_primitive(diameter,center1,exponent)
G_2 = Geom(np.ones(g,'i'),s,o).add_primitive(diameter,center2,exponent) G_2 = Geom(np.ones(g,'i'),s,o).add_primitive(diameter,center2,exponent)
assert np.count_nonzero(G_1.microstructure!=2) == np.count_nonzero(G_2.microstructure!=2) assert np.count_nonzero(G_1.materials!=2) == np.count_nonzero(G_2.materials!=2)
@pytest.mark.parametrize('center',[np.random.randint(4,10,(3)), @pytest.mark.parametrize('center',[np.random.randint(4,10,(3)),
np.random.randint(2,10), np.random.randint(2,10),
@ -323,6 +302,7 @@ class TestGeom:
G_2 = Geom(np.ones(g,'i'),[1.,1.,1.]).add_primitive(.3,center,1,fill,Rotation.from_Eulers(eu),inverse,periodic=periodic) G_2 = Geom(np.ones(g,'i'),[1.,1.,1.]).add_primitive(.3,center,1,fill,Rotation.from_Eulers(eu),inverse,periodic=periodic)
assert geom_equal(G_1,G_2) assert geom_equal(G_1,G_2)
@pytest.mark.parametrize('trigger',[[1],[]]) @pytest.mark.parametrize('trigger',[[1],[]])
def test_vicinity_offset(self,trigger): def test_vicinity_offset(self,trigger):
offset = np.random.randint(2,4) offset = np.random.randint(2,4)
@ -341,13 +321,15 @@ class TestGeom:
geom = Geom(m,np.random.rand(3)).vicinity_offset(vicinity,offset,trigger=trigger) geom = Geom(m,np.random.rand(3)).vicinity_offset(vicinity,offset,trigger=trigger)
assert np.all(m2==geom.microstructure) assert np.all(m2==geom.materials)
@pytest.mark.parametrize('periodic',[True,False]) @pytest.mark.parametrize('periodic',[True,False])
def test_vicinity_offset_invariant(self,default,periodic): def test_vicinity_offset_invariant(self,default,periodic):
offset = default.vicinity_offset(trigger=[default.microstructure.max()+1, offset = default.vicinity_offset(trigger=[default.materials.max()+1,
default.microstructure.min()-1]) default.materials.min()-1])
assert np.all(offset.microstructure==default.microstructure) assert np.all(offset.materials==default.materials)
@pytest.mark.parametrize('periodic',[True,False]) @pytest.mark.parametrize('periodic',[True,False])
def test_tessellation_approaches(self,periodic): def test_tessellation_approaches(self,periodic):
@ -359,6 +341,7 @@ class TestGeom:
Laguerre = Geom.from_Laguerre_tessellation(grid,size,seeds,np.ones(N_seeds),periodic) Laguerre = Geom.from_Laguerre_tessellation(grid,size,seeds,np.ones(N_seeds),periodic)
assert geom_equal(Laguerre,Voronoi) assert geom_equal(Laguerre,Voronoi)
def test_Laguerre_weights(self): def test_Laguerre_weights(self):
grid = np.random.randint(10,20,3) grid = np.random.randint(10,20,3)
size = np.random.random(3) + 1.0 size = np.random.random(3) + 1.0
@ -368,17 +351,18 @@ class TestGeom:
ms = np.random.randint(1, N_seeds+1) ms = np.random.randint(1, N_seeds+1)
weights[ms-1] = np.random.random() weights[ms-1] = np.random.random()
Laguerre = Geom.from_Laguerre_tessellation(grid,size,seeds,weights,np.random.random()>0.5) Laguerre = Geom.from_Laguerre_tessellation(grid,size,seeds,weights,np.random.random()>0.5)
assert np.all(Laguerre.microstructure == ms) assert np.all(Laguerre.materials == ms)
@pytest.mark.parametrize('approach',['Laguerre','Voronoi']) @pytest.mark.parametrize('approach',['Laguerre','Voronoi'])
def test_tessellate_bicrystal(self,approach): def test_tessellate_bicrystal(self,approach):
grid = np.random.randint(5,10,3)*2 grid = np.random.randint(5,10,3)*2
size = grid.astype(np.float) size = grid.astype(np.float)
seeds = np.vstack((size*np.array([0.5,0.25,0.5]),size*np.array([0.5,0.75,0.5]))) seeds = np.vstack((size*np.array([0.5,0.25,0.5]),size*np.array([0.5,0.75,0.5])))
microstructure = np.ones(grid) materials = np.ones(grid)
microstructure[:,grid[1]//2:,:] = 2 materials[:,grid[1]//2:,:] = 2
if approach == 'Laguerre': if approach == 'Laguerre':
geom = Geom.from_Laguerre_tessellation(grid,size,seeds,np.ones(2),np.random.random()>0.5) geom = Geom.from_Laguerre_tessellation(grid,size,seeds,np.ones(2),np.random.random()>0.5)
elif approach == 'Voronoi': elif approach == 'Voronoi':
geom = Geom.from_Voronoi_tessellation(grid,size,seeds, np.random.random()>0.5) geom = Geom.from_Voronoi_tessellation(grid,size,seeds, np.random.random()>0.5)
assert np.all(geom.microstructure == microstructure) assert np.all(geom.materials == materials)