removed set_X(), duplicate(), and .homogenization; renamed ".microstructure" to ".materials"
This commit is contained in:
parent
e683cbef69
commit
05835bacd3
|
@ -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')],
|
||||||
)
|
)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue