rename: grid -> cells

This commit is contained in:
Martin Diehl 2020-12-03 21:58:24 +01:00
parent 676840ae1b
commit ac0a20696c
21 changed files with 319 additions and 316 deletions

@ -1 +1 @@
Subproject commit 68cde52291ebb683ca6f610879f2ae28372597a7 Subproject commit fc27bbd6e028aa73545327aebdb206840063e135

View File

@ -64,7 +64,7 @@ for name in filenames:
geom = damask.Geom.load(StringIO(''.join(sys.stdin.read())) if name is None else name) geom = damask.Geom.load(StringIO(''.join(sys.stdin.read())) if name is None else name)
grid_original = geom.grid grid_original = geom.cells
damask.util.croak(geom) damask.util.croak(geom)
material = np.tile(geom.material,np.where(grid_original == 1, 2,1)) # make one copy along dimensions with grid == 1 material = np.tile(geom.material,np.where(grid_original == 1, 2,1)) # make one copy along dimensions with grid == 1
grid = np.array(material.shape) grid = np.array(material.shape)

View File

@ -201,7 +201,7 @@ for name in filenames:
cmds = [\ cmds = [\
init(), init(),
mesh(geom.grid,geom.size), mesh(geom.cells,geom.size),
materials(), materials(),
geometry(), geometry(),
initial_conditions(material), initial_conditions(material),

View File

@ -212,7 +212,7 @@ points = np.array(options.grid).prod().astype('float')
# ----------- calculate target distribution and bin edges # ----------- calculate target distribution and bin edges
targetGeom = damask.Geom.load_ASCII(os.path.splitext(os.path.basename(options.target))[0]+'.geom') targetGeom = damask.Geom.load_ASCII(os.path.splitext(os.path.basename(options.target))[0]+'.geom')
nMaterials = len(np.unique(targetGeom.material)) nMaterials = len(np.unique(targetGeom.material))
targetVolFrac = np.bincount(targetGeom.material.flatten())/targetGeom.grid.prod().astype(np.float) targetVolFrac = np.bincount(targetGeom.material.flatten())/targetGeom.cells.prod().astype(np.float)
target = [] target = []
for i in range(1,nMaterials+1): for i in range(1,nMaterials+1):
targetHist,targetBins = np.histogram(targetVolFrac,bins=i) #bin boundaries targetHist,targetBins = np.histogram(targetVolFrac,bins=i) #bin boundaries

View File

@ -54,13 +54,13 @@ for name in filenames:
damask.util.report(scriptName,name) damask.util.report(scriptName,name)
geom = damask.Geom.load_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) geom = damask.Geom.load_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name)
offset =(np.amin(options.box, axis=1)*geom.grid/geom.size).astype(int) offset =(np.amin(options.box, axis=1)*geom.cells/geom.size).astype(int)
box = np.amax(options.box, axis=1) \ box = np.amax(options.box, axis=1) \
- np.amin(options.box, axis=1) - np.amin(options.box, axis=1)
Nx = int(options.N/np.sqrt(options.N*geom.size[1]*box[1]/geom.size[0]/box[0])) Nx = int(options.N/np.sqrt(options.N*geom.size[1]*box[1]/geom.size[0]/box[0]))
Ny = int(options.N/np.sqrt(options.N*geom.size[0]*box[0]/geom.size[1]/box[1])) Ny = int(options.N/np.sqrt(options.N*geom.size[0]*box[0]/geom.size[1]/box[1]))
Nz = int(box[2]*geom.grid[2]) Nz = int(box[2]*geom.cells[2])
damask.util.croak('poking {} x {} x {} in box {} {} {}...'.format(Nx,Ny,Nz,*box)) damask.util.croak('poking {} x {} x {} in box {} {} {}...'.format(Nx,Ny,Nz,*box))
@ -70,12 +70,12 @@ for name in filenames:
n = 0 n = 0
for i in range(Nx): for i in range(Nx):
for j in range(Ny): for j in range(Ny):
g[0] = round((i+0.5)*box[0]*geom.grid[0]/Nx-0.5)+offset[0] g[0] = round((i+0.5)*box[0]*geom.cells[0]/Nx-0.5)+offset[0]
g[1] = round((j+0.5)*box[1]*geom.grid[1]/Ny-0.5)+offset[1] g[1] = round((j+0.5)*box[1]*geom.cells[1]/Ny-0.5)+offset[1]
for k in range(Nz): for k in range(Nz):
g[2] = k + offset[2] g[2] = k + offset[2]
g %= geom.grid g %= geom.cells
seeds[n,0:3] = (g+0.5)/geom.grid # normalize coordinates to box seeds[n,0:3] = (g+0.5)/geom.cells # normalize coordinates to box
seeds[n, 3] = geom.material[g[0],g[1],g[2]] seeds[n, 3] = geom.material[g[0],g[1],g[2]]
if options.x: g[0] += 1 if options.x: g[0] += 1
if options.y: g[1] += 1 if options.y: g[1] += 1
@ -85,7 +85,7 @@ for name in filenames:
comments = geom.comments \ comments = geom.comments \
+ [scriptID + ' ' + ' '.join(sys.argv[1:]), + [scriptID + ' ' + ' '.join(sys.argv[1:]),
'poking\ta {}\tb {}\tc {}'.format(Nx,Ny,Nz), 'poking\ta {}\tb {}\tc {}'.format(Nx,Ny,Nz),
'grid\ta {}\tb {}\tc {}'.format(*geom.grid), 'grid\ta {}\tb {}\tc {}'.format(*geom.cells),
'size\tx {}\ty {}\tz {}'.format(*geom.size), 'size\tx {}\ty {}\tz {}'.format(*geom.size),
'origin\tx {}\ty {}\tz {}'.format(*geom.origin), 'origin\tx {}\ty {}\tz {}'.format(*geom.origin),
] ]

View File

@ -225,7 +225,7 @@ class Colormap(mpl.colors.ListedColormap):
def save_paraview(self,fname=None): def save_paraview(self,fname=None):
""" """
Write colormap to JSON file for Paraview. Save as JSON file for use in Paraview.
Parameters Parameters
---------- ----------
@ -260,7 +260,7 @@ class Colormap(mpl.colors.ListedColormap):
def save_ASCII(self,fname=None): def save_ASCII(self,fname=None):
""" """
Write colormap to ASCII table. Save as ASCII file.
Parameters Parameters
---------- ----------
@ -286,7 +286,7 @@ class Colormap(mpl.colors.ListedColormap):
def save_GOM(self,fname=None): def save_GOM(self,fname=None):
""" """
Write colormap to GOM Aramis compatible format. Save as ASCII file for use in GOM Aramis.
Parameters Parameters
---------- ----------
@ -314,7 +314,7 @@ class Colormap(mpl.colors.ListedColormap):
def save_gmsh(self,fname=None): def save_gmsh(self,fname=None):
""" """
Write colormap to Gmsh compatible format. Save as ASCII file for use in gmsh.
Parameters Parameters
---------- ----------

View File

@ -44,7 +44,7 @@ class Geom:
def __repr__(self): def __repr__(self):
"""Basic information on geometry definition.""" """Basic information on geometry definition."""
return util.srepr([ return util.srepr([
f'grid a b c: {util.srepr(self.grid, " x ")}', f'cells a b c: {util.srepr(self.cells, " 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'# materials: {self.N_materials}', f'# materials: {self.N_materials}',
@ -73,9 +73,9 @@ class Geom:
""" """
message = [] message = []
if np.any(other.grid != self.grid): if np.any(other.cells != self.cells):
message.append(util.deemph(f'grid a b c: {util.srepr(other.grid," x ")}')) message.append(util.deemph(f'cells a b c: {util.srepr(other.cells," x ")}'))
message.append(util.emph( f'grid a b c: {util.srepr( self.grid," x ")}')) message.append(util.emph( f'cells a b c: {util.srepr( self.cells," x ")}'))
if not np.allclose(other.size,self.size): if not np.allclose(other.size,self.size):
message.append(util.deemph(f'size x y z: {util.srepr(other.size," x ")}')) message.append(util.deemph(f'size x y z: {util.srepr(other.size," x ")}'))
@ -150,8 +150,8 @@ class Geom:
@property @property
def grid(self): def cells(self):
"""Grid dimension of geometry.""" """Number of cells in x,y,z direction."""
return np.asarray(self.material.shape) return np.asarray(self.material.shape)
@ -164,7 +164,7 @@ class Geom:
@staticmethod @staticmethod
def load(fname): def load(fname):
""" """
Read a VTK rectilinear grid. Load from VTK rectilinear grid file.
Parameters Parameters
---------- ----------
@ -175,10 +175,10 @@ class Geom:
""" """
v = VTK.load(fname if str(fname).endswith('.vtr') else str(fname)+'.vtr') v = VTK.load(fname if str(fname).endswith('.vtr') else str(fname)+'.vtr')
comments = v.get_comments() comments = v.get_comments()
grid = np.array(v.vtk_data.GetDimensions())-1 cells = np.array(v.vtk_data.GetDimensions())-1
bbox = np.array(v.vtk_data.GetBounds()).reshape(3,2).T bbox = np.array(v.vtk_data.GetBounds()).reshape(3,2).T
return Geom(material = v.get('material').reshape(grid,order='F'), return Geom(material = v.get('material').reshape(cells,order='F'),
size = bbox[1] - bbox[0], size = bbox[1] - bbox[0],
origin = bbox[0], origin = bbox[0],
comments=comments) comments=comments)
@ -187,7 +187,7 @@ class Geom:
@staticmethod @staticmethod
def load_ASCII(fname): def load_ASCII(fname):
""" """
Read a geom file. Load from geom file.
Storing geometry files in ASCII format is deprecated. Storing geometry files in ASCII format is deprecated.
This function will be removed in a future version of DAMASK. This function will be removed in a future version of DAMASK.
@ -219,7 +219,7 @@ class Geom:
items = line.split('#')[0].lower().strip().split() items = line.split('#')[0].lower().strip().split()
key = items[0] if items else '' key = items[0] if items else ''
if key == 'grid': if key == 'grid':
grid = np.array([ int(dict(zip(items[1::2],items[2::2]))[i]) for i in ['a','b','c']]) cells = np.array([ int(dict(zip(items[1::2],items[2::2]))[i]) for i in ['a','b','c']])
elif key == 'size': elif key == 'size':
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':
@ -227,7 +227,7 @@ class Geom:
else: else:
comments.append(line.strip()) comments.append(line.strip())
material = np.empty(grid.prod()) # initialize as flat array material = np.empty(cells.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()
@ -242,19 +242,19 @@ class Geom:
material[i:i+len(items)] = items material[i:i+len(items)] = items
i += len(items) i += len(items)
if i != grid.prod(): if i != cells.prod():
raise TypeError(f'Invalid file: expected {grid.prod()} entries, found {i}') raise TypeError(f'Invalid file: expected {cells.prod()} entries, found {i}')
if not np.any(np.mod(material,1) != 0.0): # no float present if not np.any(np.mod(material,1) != 0.0): # no float present
material = material.astype('int') - (1 if material.min() > 0 else 0) material = material.astype('int') - (1 if material.min() > 0 else 0)
return Geom(material.reshape(grid,order='F'),size,origin,comments) return Geom(material.reshape(cells,order='F'),size,origin,comments)
@staticmethod @staticmethod
def load_DREAM3D(fname,base_group,point_data=None,material='FeatureIds'): def load_DREAM3D(fname,base_group,point_data=None,material='FeatureIds'):
""" """
Load a DREAM.3D file. Load from DREAM.3D file.
Parameters Parameters
---------- ----------
@ -274,21 +274,21 @@ class Geom:
root_dir ='DataContainers' root_dir ='DataContainers'
f = h5py.File(fname, 'r') f = h5py.File(fname, 'r')
g = path.join(root_dir,base_group,'_SIMPL_GEOMETRY') g = path.join(root_dir,base_group,'_SIMPL_GEOMETRY')
grid = f[path.join(g,'DIMENSIONS')][()] cells = f[path.join(g,'DIMENSIONS')][()]
size = f[path.join(g,'SPACING')][()] * grid size = f[path.join(g,'SPACING')][()] * cells
origin = f[path.join(g,'ORIGIN')][()] origin = f[path.join(g,'ORIGIN')][()]
ma = np.arange(grid.prod(),dtype=int) \ ma = np.arange(cells.prod(),dtype=int) \
if point_data is None else \ if point_data is None else \
np.reshape(f[path.join(root_dir,base_group,point_data,material)],grid.prod()) np.reshape(f[path.join(root_dir,base_group,point_data,material)],cells.prod())
return Geom(ma.reshape(grid,order='F'),size,origin,util.execution_stamp('Geom','load_DREAM3D')) return Geom(ma.reshape(cells,order='F'),size,origin,util.execution_stamp('Geom','load_DREAM3D'))
@staticmethod @staticmethod
def from_table(table,coordinates,labels): def from_table(table,coordinates,labels):
""" """
Derive geometry from an ASCII table. Generate grid from ASCII table.
Parameters Parameters
---------- ----------
@ -302,15 +302,15 @@ class Geom:
Each unique combintation of values results in one material ID. Each unique combintation of values results in one material ID.
""" """
grid,size,origin = grid_filters.cell_coord0_gridSizeOrigin(table.get(coordinates)) cells,size,origin = grid_filters.cell_coord0_gridSizeOrigin(table.get(coordinates))
labels_ = [labels] if isinstance(labels,str) else labels labels_ = [labels] if isinstance(labels,str) else labels
unique,unique_inverse = np.unique(np.hstack([table.get(l) for l in labels_]),return_inverse=True,axis=0) unique,unique_inverse = np.unique(np.hstack([table.get(l) for l in labels_]),return_inverse=True,axis=0)
ma = np.arange(grid.prod()) if len(unique) == grid.prod() else \ ma = np.arange(cells.prod()) if len(unique) == cells.prod() else \
np.arange(unique.size)[np.argsort(pd.unique(unique_inverse))][unique_inverse] np.arange(unique.size)[np.argsort(pd.unique(unique_inverse))][unique_inverse]
return Geom(ma.reshape(grid,order='F'),size,origin,util.execution_stamp('Geom','from_table')) return Geom(ma.reshape(cells,order='F'),size,origin,util.execution_stamp('Geom','from_table'))
@staticmethod @staticmethod
@ -318,14 +318,14 @@ class Geom:
return np.argmin(np.sum((np.broadcast_to(point,(len(seeds),3))-seeds)**2,axis=1) - weights) return np.argmin(np.sum((np.broadcast_to(point,(len(seeds),3))-seeds)**2,axis=1) - weights)
@staticmethod @staticmethod
def from_Laguerre_tessellation(grid,size,seeds,weights,material=None,periodic=True): def from_Laguerre_tessellation(cells,size,seeds,weights,material=None,periodic=True):
""" """
Generate geometry from Laguerre tessellation. Generate grid from Laguerre tessellation.
Parameters Parameters
---------- ----------
grid : int numpy.ndarray of shape (3) cells : int numpy.ndarray of shape (3)
Number of grid points in x,y,z direction. Number of cells 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 geometry in meter. Physical size of the geometry in meter.
seeds : numpy.ndarray of shape (:,3) seeds : numpy.ndarray of shape (:,3)
@ -344,11 +344,11 @@ class Geom:
seeds_p = np.vstack((seeds -np.array([size[0],0.,0.]),seeds, seeds +np.array([size[0],0.,0.]))) seeds_p = np.vstack((seeds -np.array([size[0],0.,0.]),seeds, seeds +np.array([size[0],0.,0.])))
seeds_p = np.vstack((seeds_p-np.array([0.,size[1],0.]),seeds_p,seeds_p+np.array([0.,size[1],0.]))) seeds_p = np.vstack((seeds_p-np.array([0.,size[1],0.]),seeds_p,seeds_p+np.array([0.,size[1],0.])))
seeds_p = np.vstack((seeds_p-np.array([0.,0.,size[2]]),seeds_p,seeds_p+np.array([0.,0.,size[2]]))) seeds_p = np.vstack((seeds_p-np.array([0.,0.,size[2]]),seeds_p,seeds_p+np.array([0.,0.,size[2]])))
coords = grid_filters.cell_coord0(grid*3,size*3,-size).reshape(-1,3) coords = grid_filters.cell_coord0(cells*3,size*3,-size).reshape(-1,3)
else: else:
weights_p = weights weights_p = weights
seeds_p = seeds seeds_p = seeds
coords = grid_filters.cell_coord0(grid,size).reshape(-1,3) coords = grid_filters.cell_coord0(cells,size).reshape(-1,3)
pool = mp.Pool(processes = int(environment.options['DAMASK_NUM_THREADS'])) pool = mp.Pool(processes = int(environment.options['DAMASK_NUM_THREADS']))
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])
@ -357,10 +357,10 @@ class Geom:
material_ = np.array(result.get()) material_ = np.array(result.get())
if periodic: if periodic:
material_ = material_.reshape(grid*3) material_ = material_.reshape(cells*3)
material_ = material_[grid[0]:grid[0]*2,grid[1]:grid[1]*2,grid[2]:grid[2]*2]%seeds.shape[0] material_ = material_[cells[0]:cells[0]*2,cells[1]:cells[1]*2,cells[2]:cells[2]*2]%seeds.shape[0]
else: else:
material_ = material_.reshape(grid) material_ = material_.reshape(cells)
return Geom(material = material_ if material is None else material[material_], return Geom(material = material_ if material is None else material[material_],
size = size, size = size,
@ -369,14 +369,14 @@ class Geom:
@staticmethod @staticmethod
def from_Voronoi_tessellation(grid,size,seeds,material=None,periodic=True): def from_Voronoi_tessellation(cells,size,seeds,material=None,periodic=True):
""" """
Generate geometry from Voronoi tessellation. Generate grid from Voronoi tessellation.
Parameters Parameters
---------- ----------
grid : int numpy.ndarray of shape (3) cells : int numpy.ndarray of shape (3)
Number of grid points in x,y,z direction. Number of cells 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 geometry in meter. Physical size of the geometry in meter.
seeds : numpy.ndarray of shape (:,3) seeds : numpy.ndarray of shape (:,3)
@ -388,11 +388,11 @@ class Geom:
Perform a periodic tessellation. Defaults to True. Perform a periodic tessellation. Defaults to True.
""" """
coords = grid_filters.cell_coord0(grid,size).reshape(-1,3) coords = grid_filters.cell_coord0(cells,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,material_ = KDTree.query(coords) devNull,material_ = KDTree.query(coords)
return Geom(material = (material_ if material is None else material[material_]).reshape(grid), return Geom(material = (material_ if material is None else material[material_]).reshape(cells),
size = size, size = size,
comments = util.execution_stamp('Geom','from_Voronoi_tessellation'), comments = util.execution_stamp('Geom','from_Voronoi_tessellation'),
) )
@ -441,14 +441,14 @@ class Geom:
@staticmethod @staticmethod
def from_minimal_surface(grid,size,surface,threshold=0.0,periods=1,materials=(0,1)): def from_minimal_surface(cells,size,surface,threshold=0.0,periods=1,materials=(0,1)):
""" """
Generate geometry from definition of triply periodic minimal surface. Generate grid from definition of triply periodic minimal surface.
Parameters Parameters
---------- ----------
grid : int numpy.ndarray of shape (3) cells : int numpy.ndarray of shape (3)
Number of grid points in x,y,z direction. Number of cells 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 geometry in meter. Physical size of the geometry in meter.
surface : str surface : str
@ -493,9 +493,9 @@ class Geom:
https://doi.org/10.1016/j.simpa.2020.100026 https://doi.org/10.1016/j.simpa.2020.100026
""" """
x,y,z = np.meshgrid(periods*2.0*np.pi*(np.arange(grid[0])+0.5)/grid[0], x,y,z = np.meshgrid(periods*2.0*np.pi*(np.arange(cells[0])+0.5)/cells[0],
periods*2.0*np.pi*(np.arange(grid[1])+0.5)/grid[1], periods*2.0*np.pi*(np.arange(cells[1])+0.5)/cells[1],
periods*2.0*np.pi*(np.arange(grid[2])+0.5)/grid[2], periods*2.0*np.pi*(np.arange(cells[2])+0.5)/cells[2],
indexing='ij',sparse=True) indexing='ij',sparse=True)
return Geom(material = np.where(threshold < Geom._minimal_surface[surface](x,y,z),materials[1],materials[0]), return Geom(material = np.where(threshold < Geom._minimal_surface[surface](x,y,z),materials[1],materials[0]),
size = size, size = size,
@ -505,7 +505,7 @@ class Geom:
def save(self,fname,compress=True): def save(self,fname,compress=True):
""" """
Store as VTK rectilinear grid. Save as VTK rectilinear grid file.
Parameters Parameters
---------- ----------
@ -515,7 +515,7 @@ class Geom:
Compress with zlib algorithm. Defaults to True. Compress with zlib algorithm. Defaults to True.
""" """
v = VTK.from_rectilinear_grid(self.grid,self.size,self.origin) v = VTK.from_rectilinear_grid(self.cells,self.size,self.origin)
v.add(self.material.flatten(order='F'),'material') v.add(self.material.flatten(order='F'),'material')
v.add_comments(self.comments) v.add_comments(self.comments)
@ -524,7 +524,7 @@ class Geom:
def save_ASCII(self,fname): def save_ASCII(self,fname):
""" """
Write a geom file. Save as geom file.
Storing geometry files in ASCII format is deprecated. Storing geometry files in ASCII format is deprecated.
This function will be removed in a future version of DAMASK. This function will be removed in a future version of DAMASK.
@ -539,7 +539,7 @@ class Geom:
""" """
warnings.warn('Support for ASCII-based geom format will be removed in DAMASK 3.1.0', DeprecationWarning) warnings.warn('Support for ASCII-based geom format will be removed in DAMASK 3.1.0', DeprecationWarning)
header = [f'{len(self.comments)+4} header'] + self.comments \ header = [f'{len(self.comments)+4} header'] + self.comments \
+ ['grid a {} b {} c {}'.format(*self.grid), + ['grid a {} b {} c {}'.format(*self.cells),
'size x {} y {} z {}'.format(*self.size), 'size x {} y {} z {}'.format(*self.size),
'origin x {} y {} z {}'.format(*self.origin), 'origin x {} y {} z {}'.format(*self.origin),
'homogenization 1', 'homogenization 1',
@ -548,13 +548,13 @@ class Geom:
format_string = '%g' if self.material.dtype in np.sctypes['float'] else \ format_string = '%g' if self.material.dtype in np.sctypes['float'] else \
'%{}i'.format(1+int(np.floor(np.log10(np.nanmax(self.material))))) '%{}i'.format(1+int(np.floor(np.log10(np.nanmax(self.material)))))
np.savetxt(fname, np.savetxt(fname,
self.material.reshape([self.grid[0],np.prod(self.grid[1:])],order='F').T, self.material.reshape([self.cells[0],np.prod(self.cells[1:])],order='F').T,
header='\n'.join(header), fmt=format_string, comments='') header='\n'.join(header), fmt=format_string, comments='')
def show(self): def show(self):
"""Show on screen.""" """Show on screen."""
VTK.from_rectilinear_grid(self.grid,self.size,self.origin).show() VTK.from_rectilinear_grid(self.cells,self.size,self.origin).show()
def add_primitive(self,dimension,center,exponent, def add_primitive(self,dimension,center,exponent,
@ -566,11 +566,10 @@ class Geom:
---------- ----------
dimension : int or float numpy.ndarray of shape (3) dimension : int or float numpy.ndarray of shape (3)
Dimension (diameter/side length) of the primitive. If given as Dimension (diameter/side length) of the primitive. If given as
integers, grid point locations (cell centers) are addressed. integers, cell centers are addressed.
If given as floats, coordinates are addressed. If given as floats, coordinates are addressed.
center : int or float numpy.ndarray of shape (3) center : int or float numpy.ndarray of shape (3)
Center of the primitive. If given as integers, grid point Center of the primitive. If given as integers, cell centers are addressed.
coordinates (cell centers) are addressed.
If given as floats, coordinates in space are addressed. If given as floats, coordinates in space are addressed.
exponent : numpy.ndarray of shape (3) or float exponent : numpy.ndarray of shape (3) or float
Exponents for the three axes. Exponents for the three axes.
@ -588,22 +587,22 @@ class Geom:
""" """
# radius and center # radius and center
r = np.array(dimension)/2.0*self.size/self.grid if np.array(dimension).dtype in np.sctypes['int'] else \ r = np.array(dimension)/2.0*self.size/self.cells if np.array(dimension).dtype in np.sctypes['int'] else \
np.array(dimension)/2.0 np.array(dimension)/2.0
c = (np.array(center) + .5)*self.size/self.grid if np.array(center).dtype in np.sctypes['int'] else \ c = (np.array(center) + .5)*self.size/self.cells if np.array(center).dtype in np.sctypes['int'] else \
(np.array(center) - self.origin) (np.array(center) - self.origin)
coords = grid_filters.cell_coord0(self.grid,self.size, coords = grid_filters.cell_coord0(self.cells,self.size,
-(0.5*(self.size + (self.size/self.grid -(0.5*(self.size + (self.size/self.cells
if np.array(center).dtype in np.sctypes['int'] else if np.array(center).dtype in np.sctypes['int'] else
0)) if periodic else c)) 0)) if periodic else c))
coords_rot = R.broadcast_to(tuple(self.grid))@coords coords_rot = R.broadcast_to(tuple(self.cells))@coords
with np.errstate(all='ignore'): with np.errstate(all='ignore'):
mask = np.sum(np.power(coords_rot/r,2.0**np.array(exponent)),axis=-1) > 1.0 mask = np.sum(np.power(coords_rot/r,2.0**np.array(exponent)),axis=-1) > 1.0
if periodic: # translate back to center if periodic: # translate back to center
mask = np.roll(mask,((c/self.size-0.5)*self.grid).round().astype(int),(0,1,2)) mask = np.roll(mask,((c/self.size-0.5)*self.cells).round().astype(int),(0,1,2))
return Geom(material = np.where(np.logical_not(mask) if inverse else mask, return Geom(material = np.where(np.logical_not(mask) if inverse else mask,
self.material, self.material,
@ -642,7 +641,7 @@ class Geom:
mat = np.concatenate([mat,mat[:,:,limits[0]:limits[1]:-1]],2) mat = np.concatenate([mat,mat[:,:,limits[0]:limits[1]:-1]],2)
return Geom(material = mat, return Geom(material = mat,
size = self.size/self.grid*np.asarray(mat.shape), size = self.size/self.cells*np.asarray(mat.shape),
origin = self.origin, origin = self.origin,
comments = self.comments+[util.execution_stamp('Geom','mirror')], comments = self.comments+[util.execution_stamp('Geom','mirror')],
) )
@ -672,21 +671,21 @@ class Geom:
) )
def scale(self,grid,periodic=True): def scale(self,cells,periodic=True):
""" """
Scale geometry to new grid. Scale geometry to new cells.
Parameters Parameters
---------- ----------
grid : numpy.ndarray of shape (3) cells : numpy.ndarray of shape (3)
Number of grid points in x,y,z direction. Number of cells in x,y,z direction.
periodic : Boolean, optional periodic : Boolean, optional
Assume geometry to be periodic. Defaults to True. Assume geometry to be periodic. Defaults to True.
""" """
return Geom(material = ndimage.interpolation.zoom( return Geom(material = ndimage.interpolation.zoom(
self.material, self.material,
grid/self.grid, cells/self.cells,
output=self.material.dtype, output=self.material.dtype,
order=0, order=0,
mode=('wrap' if periodic else 'nearest'), mode=('wrap' if periodic else 'nearest'),
@ -737,7 +736,7 @@ class Geom:
"""Renumber sorted material indices as 0,...,N-1.""" """Renumber sorted material indices as 0,...,N-1."""
_,renumbered = np.unique(self.material,return_inverse=True) _,renumbered = np.unique(self.material,return_inverse=True)
return Geom(material = renumbered.reshape(self.grid), return Geom(material = renumbered.reshape(self.cells),
size = self.size, size = self.size,
origin = self.origin, origin = self.origin,
comments = self.comments+[util.execution_stamp('Geom','renumber')], comments = self.comments+[util.execution_stamp('Geom','renumber')],
@ -773,25 +772,25 @@ class Geom:
else: else:
material_in = material_out material_in = material_out
origin = self.origin-(np.asarray(material_in.shape)-self.grid)*.5 * self.size/self.grid origin = self.origin-(np.asarray(material_in.shape)-self.cells)*.5 * self.size/self.cells
return Geom(material = material_in, return Geom(material = material_in,
size = self.size/self.grid*np.asarray(material_in.shape), size = self.size/self.cells*np.asarray(material_in.shape),
origin = origin, origin = origin,
comments = self.comments+[util.execution_stamp('Geom','rotate')], comments = self.comments+[util.execution_stamp('Geom','rotate')],
) )
def canvas(self,grid=None,offset=None,fill=None): def canvas(self,cells=None,offset=None,fill=None):
""" """
Crop or enlarge/pad geometry. Crop or enlarge/pad geometry.
Parameters Parameters
---------- ----------
grid : numpy.ndarray of shape (3) cells : numpy.ndarray of shape (3)
Number of grid points in x,y,z direction. Number of cells 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 geometry [0,0,0]. Offset (measured in cells) from old to new geometry [0,0,0].
fill : int or float, optional fill : int or float, optional
Material index to fill the background. Defaults to material.max() + 1. Material index to fill the background. Defaults to material.max() + 1.
@ -800,18 +799,18 @@ class Geom:
if fill is None: fill = np.nanmax(self.material) + 1 if fill is None: fill = np.nanmax(self.material) + 1
dtype = float if int(fill) != fill or self.material.dtype in np.sctypes['float'] else int dtype = float if int(fill) != fill or self.material.dtype in np.sctypes['float'] else int
canvas = np.full(self.grid if grid is None else grid,fill,dtype) canvas = np.full(self.cells if cells is None else cells,fill,dtype)
LL = np.clip( offset, 0,np.minimum(self.grid, grid+offset)) LL = np.clip( offset, 0,np.minimum(self.cells, cells+offset))
UR = np.clip( offset+grid, 0,np.minimum(self.grid, grid+offset)) UR = np.clip( offset+cells, 0,np.minimum(self.cells, cells+offset))
ll = np.clip(-offset, 0,np.minimum( grid,self.grid-offset)) ll = np.clip(-offset, 0,np.minimum( cells,self.cells-offset))
ur = np.clip(-offset+self.grid,0,np.minimum( grid,self.grid-offset)) ur = np.clip(-offset+self.cells,0,np.minimum( cells,self.cells-offset))
canvas[ll[0]:ur[0],ll[1]:ur[1],ll[2]:ur[2]] = self.material[LL[0]:UR[0],LL[1]:UR[1],LL[2]:UR[2]] canvas[ll[0]:ur[0],ll[1]:ur[1],ll[2]:ur[2]] = self.material[LL[0]:UR[0],LL[1]:UR[1],LL[2]:UR[2]]
return Geom(material = canvas, return Geom(material = canvas,
size = self.size/self.grid*np.asarray(canvas.shape), size = self.size/self.cells*np.asarray(canvas.shape),
origin = self.origin+offset*self.size/self.grid, origin = self.origin+offset*self.size/self.cells,
comments = self.comments+[util.execution_stamp('Geom','canvas')], comments = self.comments+[util.execution_stamp('Geom','canvas')],
) )
@ -834,7 +833,7 @@ class Geom:
mp = np.vectorize(mp) mp = np.vectorize(mp)
mapper = dict(zip(from_material,to_material)) mapper = dict(zip(from_material,to_material))
return Geom(material = mp(self.material,mapper).reshape(self.grid), return Geom(material = mp(self.material,mapper).reshape(self.cells),
size = self.size, size = self.size,
origin = self.origin, origin = self.origin,
comments = self.comments+[util.execution_stamp('Geom','substitute')], comments = self.comments+[util.execution_stamp('Geom','substitute')],
@ -848,7 +847,7 @@ class Geom:
sort_idx = np.argsort(from_ma) sort_idx = np.argsort(from_ma)
ma = np.unique(a)[sort_idx][np.searchsorted(from_ma,a,sorter = sort_idx)] ma = np.unique(a)[sort_idx][np.searchsorted(from_ma,a,sorter = sort_idx)]
return Geom(material = ma.reshape(self.grid,order='F'), return Geom(material = ma.reshape(self.cells,order='F'),
size = self.size, size = self.size,
origin = self.origin, origin = self.origin,
comments = self.comments+[util.execution_stamp('Geom','sort')], comments = self.comments+[util.execution_stamp('Geom','sort')],
@ -916,9 +915,9 @@ 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.')
o = [[0, self.grid[0]+1, np.prod(self.grid[:2]+1)+self.grid[0]+1, np.prod(self.grid[:2]+1)], o = [[0, self.cells[0]+1, np.prod(self.cells[:2]+1)+self.cells[0]+1, np.prod(self.cells[:2]+1)],
[0, np.prod(self.grid[:2]+1), np.prod(self.grid[:2]+1)+1, 1], [0, np.prod(self.cells[:2]+1), np.prod(self.cells[:2]+1)+1, 1],
[0, 1, self.grid[0]+1+1, self.grid[0]+1]] # offset for connectivity [0, 1, self.cells[0]+1+1, self.cells[0]+1]] # offset for connectivity
connectivity = [] connectivity = []
for i,d in enumerate(['x','y','z']): for i,d in enumerate(['x','y','z']):
@ -933,5 +932,5 @@ class Geom:
base_nodes = np.argwhere(mask.flatten(order='F')).reshape(-1,1) base_nodes = np.argwhere(mask.flatten(order='F')).reshape(-1,1)
connectivity.append(np.block([base_nodes + o[i][k] for k in range(4)])) connectivity.append(np.block([base_nodes + o[i][k] for k in range(4)]))
coords = grid_filters.node_coord0(self.grid,self.size,self.origin).reshape(-1,3,order='F') coords = grid_filters.node_coord0(self.cells,self.size,self.origin).reshape(-1,3,order='F')
return VTK.from_unstructured_grid(coords,np.vstack(connectivity),'QUAD') return VTK.from_unstructured_grid(coords,np.vstack(connectivity),'QUAD')

View File

@ -46,13 +46,17 @@ class Result:
self.version_major = f.attrs['DADF5_version_major'] self.version_major = f.attrs['DADF5_version_major']
self.version_minor = f.attrs['DADF5_version_minor'] self.version_minor = f.attrs['DADF5_version_minor']
if self.version_major != 0 or not 7 <= self.version_minor <= 9: if self.version_major != 0 or not 7 <= self.version_minor <= 10:
raise TypeError(f'Unsupported DADF5 version {self.version_major}.{self.version_minor}') raise TypeError(f'Unsupported DADF5 version {self.version_major}.{self.version_minor}')
self.structured = 'grid' in f['geometry'].attrs.keys() self.structured = 'grid' in f['geometry'].attrs.keys() or \
'cells' in f['geometry'].attrs.keys()
if self.structured: if self.structured:
self.grid = f['geometry'].attrs['grid'] try:
self.cells = f['geometry'].attrs['cells']
except KeyError:
self.cells = f['geometry'].attrs['grid']
self.size = f['geometry'].attrs['size'] self.size = f['geometry'].attrs['size']
self.origin = f['geometry'].attrs['origin'] self.origin = f['geometry'].attrs['origin']
@ -561,7 +565,7 @@ class Result:
def cell_coordinates(self): def cell_coordinates(self):
"""Return initial coordinates of the cell centers.""" """Return initial coordinates of the cell centers."""
if self.structured: if self.structured:
return grid_filters.cell_coord0(self.grid,self.size,self.origin).reshape(-1,3,order='F') return grid_filters.cell_coord0(self.cells,self.size,self.origin).reshape(-1,3,order='F')
else: else:
with h5py.File(self.fname,'r') as f: with h5py.File(self.fname,'r') as f:
return f['geometry/x_c'][()] return f['geometry/x_c'][()]
@ -570,7 +574,7 @@ class Result:
def node_coordinates(self): def node_coordinates(self):
"""Return initial coordinates of the cell centers.""" """Return initial coordinates of the cell centers."""
if self.structured: if self.structured:
return grid_filters.node_coord0(self.grid,self.size,self.origin).reshape(-1,3,order='F') return grid_filters.node_coord0(self.cells,self.size,self.origin).reshape(-1,3,order='F')
else: else:
with h5py.File(self.fname,'r') as f: with h5py.File(self.fname,'r') as f:
return f['geometry/x_n'][()] return f['geometry/x_n'][()]
@ -1218,7 +1222,7 @@ class Result:
topology=ET.SubElement(grid, 'Topology') topology=ET.SubElement(grid, 'Topology')
topology.attrib={'TopologyType': '3DCoRectMesh', topology.attrib={'TopologyType': '3DCoRectMesh',
'Dimensions': '{} {} {}'.format(*self.grid+1)} 'Dimensions': '{} {} {}'.format(*self.cells+1)}
geometry=ET.SubElement(grid, 'Geometry') geometry=ET.SubElement(grid, 'Geometry')
geometry.attrib={'GeometryType':'Origin_DxDyDz'} geometry.attrib={'GeometryType':'Origin_DxDyDz'}
@ -1233,7 +1237,7 @@ class Result:
delta.attrib={'Format': 'XML', delta.attrib={'Format': 'XML',
'NumberType': 'Float', 'NumberType': 'Float',
'Dimensions': '3'} 'Dimensions': '3'}
delta.text="{} {} {}".format(*(self.size/self.grid)) delta.text="{} {} {}".format(*(self.size/self.cells))
with h5py.File(self.fname,'r') as f: with h5py.File(self.fname,'r') as f:
@ -1244,7 +1248,7 @@ class Result:
data_items.append(ET.SubElement(attributes[-1], 'DataItem')) data_items.append(ET.SubElement(attributes[-1], 'DataItem'))
data_items[-1].attrib={'Format': 'HDF', data_items[-1].attrib={'Format': 'HDF',
'Precision': '8', 'Precision': '8',
'Dimensions': '{} {} {} 3'.format(*(self.grid+1))} 'Dimensions': '{} {} {} 3'.format(*(self.cells+1))}
data_items[-1].text=f'{os.path.split(self.fname)[1]}:/{inc}/geometry/u_n' data_items[-1].text=f'{os.path.split(self.fname)[1]}:/{inc}/geometry/u_n'
for o,p in zip(['phases','homogenizations'],['out_type_ph','out_type_ho']): for o,p in zip(['phases','homogenizations'],['out_type_ph','out_type_ho']):
@ -1267,7 +1271,7 @@ class Result:
data_items[-1].attrib={'Format': 'HDF', data_items[-1].attrib={'Format': 'HDF',
'NumberType': number_type_map(dtype), 'NumberType': number_type_map(dtype),
'Precision': f'{dtype.itemsize}', 'Precision': f'{dtype.itemsize}',
'Dimensions': '{} {} {} {}'.format(*self.grid,1 if shape == () else 'Dimensions': '{} {} {} {}'.format(*self.cells,1 if shape == () else
np.prod(shape))} np.prod(shape))}
data_items[-1].text=f'{os.path.split(self.fname)[1]}:{name}' data_items[-1].text=f'{os.path.split(self.fname)[1]}:{name}'
@ -1291,7 +1295,7 @@ class Result:
if mode.lower()=='cell': if mode.lower()=='cell':
if self.structured: if self.structured:
v = VTK.from_rectilinear_grid(self.grid,self.size,self.origin) v = VTK.from_rectilinear_grid(self.cells,self.size,self.origin)
else: else:
with h5py.File(self.fname,'r') as f: with h5py.File(self.fname,'r') as f:
v = VTK.from_unstructured_grid(f['/geometry/x_n'][()], v = VTK.from_unstructured_grid(f['/geometry/x_n'][()],

View File

@ -73,7 +73,7 @@ class Table:
@staticmethod @staticmethod
def load(fname): def load(fname):
""" """
Load ASCII table file. Load from ASCII table file.
In legacy style, the first line indicates the number of In legacy style, the first line indicates the number of
subsequent header lines as "N header", with the last header line being subsequent header lines as "N header", with the last header line being
@ -131,7 +131,7 @@ class Table:
@staticmethod @staticmethod
def load_ang(fname): def load_ang(fname):
""" """
Load ang file. Load from ang file.
A valid TSL ang file needs to contains the following columns: A valid TSL ang file needs to contains the following columns:
* Euler angles (Bunge notation) in radians, 3 floats, label 'eu'. * Euler angles (Bunge notation) in radians, 3 floats, label 'eu'.

View File

@ -128,7 +128,7 @@ class VTK:
@staticmethod @staticmethod
def load(fname,dataset_type=None): def load(fname,dataset_type=None):
""" """
Create VTK from file. Load from VTK file.
Parameters Parameters
---------- ----------
@ -181,7 +181,7 @@ class VTK:
writer.Write() writer.Write()
def save(self,fname,parallel=True,compress=True): def save(self,fname,parallel=True,compress=True):
""" """
Write to file. Save as VTK file.
Parameters Parameters
---------- ----------

View File

@ -8,14 +8,14 @@ This convention is consistent with the geom file format.
When converting to/from a plain list (e.g. storage in ASCII table), When converting to/from a plain list (e.g. storage in ASCII table),
the following operations are required for tensorial data: the following operations are required for tensorial data:
D3 = D1.reshape(grid+(-1,),order='F').reshape(grid+(3,3)) D3 = D1.reshape(cells+(-1,),order='F').reshape(cells+(3,3))
D1 = D3.reshape(grid+(-1,)).reshape(-1,9,order='F') D1 = D3.reshape(cells+(-1,)).reshape(-1,9,order='F')
""" """
from scipy import spatial as _spatial from scipy import spatial as _spatial
import numpy as _np import numpy as _np
def _ks(size,grid,first_order=False): def _ks(size,cells,first_order=False):
""" """
Get wave numbers operator. Get wave numbers operator.
@ -23,19 +23,19 @@ def _ks(size,grid,first_order=False):
---------- ----------
size : numpy.ndarray of shape (3) size : numpy.ndarray of shape (3)
physical size of the periodic field. physical size of the periodic field.
grid : numpy.ndarray of shape (3) cells : numpy.ndarray of shape (3)
number of grid points. number of cells.
first_order : bool, optional first_order : bool, optional
correction for first order derivatives, defaults to False. correction for first order derivatives, defaults to False.
""" """
k_sk = _np.where(_np.arange(grid[0])>grid[0]//2,_np.arange(grid[0])-grid[0],_np.arange(grid[0]))/size[0] k_sk = _np.where(_np.arange(cells[0])>cells[0]//2,_np.arange(cells[0])-cells[0],_np.arange(cells[0]))/size[0]
if grid[0]%2 == 0 and first_order: k_sk[grid[0]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) if cells[0]%2 == 0 and first_order: k_sk[cells[0]//2] = 0 # Nyquist freq=0 for even cells (Johnson, MIT, 2011)
k_sj = _np.where(_np.arange(grid[1])>grid[1]//2,_np.arange(grid[1])-grid[1],_np.arange(grid[1]))/size[1] k_sj = _np.where(_np.arange(cells[1])>cells[1]//2,_np.arange(cells[1])-cells[1],_np.arange(cells[1]))/size[1]
if grid[1]%2 == 0 and first_order: k_sj[grid[1]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) if cells[1]%2 == 0 and first_order: k_sj[cells[1]//2] = 0 # Nyquist freq=0 for even cells (Johnson, MIT, 2011)
k_si = _np.arange(grid[2]//2+1)/size[2] k_si = _np.arange(cells[2]//2+1)/size[2]
return _np.stack(_np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij'), axis=-1) return _np.stack(_np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij'), axis=-1)
@ -110,26 +110,26 @@ def gradient(size,field):
return _np.fft.irfftn(grad_,axes=(0,1,2),s=field.shape[:3]) return _np.fft.irfftn(grad_,axes=(0,1,2),s=field.shape[:3])
def cell_coord0(grid,size,origin=_np.zeros(3)): def cell_coord0(cells,size,origin=_np.zeros(3)):
""" """
Cell center positions (undeformed). Cell center positions (undeformed).
Parameters Parameters
---------- ----------
grid : numpy.ndarray of shape (3) cells : numpy.ndarray of shape (3)
number of grid points. number of cells.
size : numpy.ndarray of shape (3) size : numpy.ndarray of shape (3)
physical size of the periodic field. physical size of the periodic field.
origin : numpy.ndarray, optional origin : numpy.ndarray, optional
physical origin of the periodic field. Defaults to [0.0,0.0,0.0]. physical origin of the periodic field. Defaults to [0.0,0.0,0.0].
""" """
start = origin + size/grid*.5 start = origin + size/cells*.5
end = origin + size - size/grid*.5 end = origin + size - size/cells*.5
return _np.stack(_np.meshgrid(_np.linspace(start[0],end[0],grid[0]), return _np.stack(_np.meshgrid(_np.linspace(start[0],end[0],cells[0]),
_np.linspace(start[1],end[1],grid[1]), _np.linspace(start[1],end[1],cells[1]),
_np.linspace(start[2],end[2],grid[2]),indexing = 'ij'), _np.linspace(start[2],end[2],cells[2]),indexing = 'ij'),
axis = -1) axis = -1)
@ -210,7 +210,7 @@ def cell_coord(size,F,origin=_np.zeros(3)):
def cell_coord0_gridSizeOrigin(coord0,ordered=True): def cell_coord0_gridSizeOrigin(coord0,ordered=True):
""" """
Return grid 'DNA', i.e. grid, size, and origin from 1D array of cell positions. Return grid 'DNA', i.e. cells, size, and origin from 1D array of cell positions.
Parameters Parameters
---------- ----------
@ -223,31 +223,31 @@ def cell_coord0_gridSizeOrigin(coord0,ordered=True):
coords = [_np.unique(coord0[:,i]) for i in range(3)] coords = [_np.unique(coord0[:,i]) for i in range(3)]
mincorner = _np.array(list(map(min,coords))) mincorner = _np.array(list(map(min,coords)))
maxcorner = _np.array(list(map(max,coords))) maxcorner = _np.array(list(map(max,coords)))
grid = _np.array(list(map(len,coords)),'i') cells = _np.array(list(map(len,coords)),'i')
size = grid/_np.maximum(grid-1,1) * (maxcorner-mincorner) size = cells/_np.maximum(cells-1,1) * (maxcorner-mincorner)
delta = size/grid delta = size/cells
origin = mincorner - delta*.5 origin = mincorner - delta*.5
# 1D/2D: size/origin combination undefined, set origin to 0.0 # 1D/2D: size/origin combination undefined, set origin to 0.0
size [_np.where(grid==1)] = origin[_np.where(grid==1)]*2. size [_np.where(cells==1)] = origin[_np.where(cells==1)]*2.
origin[_np.where(grid==1)] = 0.0 origin[_np.where(cells==1)] = 0.0
if grid.prod() != len(coord0): if cells.prod() != len(coord0):
raise ValueError('Data count {len(coord0)} does not match grid {grid}.') raise ValueError('Data count {len(coord0)} does not match cells {cells}.')
start = origin + delta*.5 start = origin + delta*.5
end = origin - delta*.5 + size end = origin - delta*.5 + size
atol = _np.max(size)*5e-2 atol = _np.max(size)*5e-2
if not (_np.allclose(coords[0],_np.linspace(start[0],end[0],grid[0]),atol=atol) and \ if not (_np.allclose(coords[0],_np.linspace(start[0],end[0],cells[0]),atol=atol) and \
_np.allclose(coords[1],_np.linspace(start[1],end[1],grid[1]),atol=atol) and \ _np.allclose(coords[1],_np.linspace(start[1],end[1],cells[1]),atol=atol) and \
_np.allclose(coords[2],_np.linspace(start[2],end[2],grid[2]),atol=atol)): _np.allclose(coords[2],_np.linspace(start[2],end[2],cells[2]),atol=atol)):
raise ValueError('Regular grid spacing violated.') raise ValueError('Regular cells spacing violated.')
if ordered and not _np.allclose(coord0.reshape(tuple(grid)+(3,),order='F'),cell_coord0(grid,size,origin),atol=atol): if ordered and not _np.allclose(coord0.reshape(tuple(cells)+(3,),order='F'),cell_coord0(cells,size,origin),atol=atol):
raise ValueError('Input data is not ordered (x fast, z slow).') raise ValueError('Input data is not ordered (x fast, z slow).')
return (grid,size,origin) return (cells,size,origin)
def coord0_check(coord0): def coord0_check(coord0):
@ -263,23 +263,23 @@ def coord0_check(coord0):
cell_coord0_gridSizeOrigin(coord0,ordered=True) cell_coord0_gridSizeOrigin(coord0,ordered=True)
def node_coord0(grid,size,origin=_np.zeros(3)): def node_coord0(cells,size,origin=_np.zeros(3)):
""" """
Nodal positions (undeformed). Nodal positions (undeformed).
Parameters Parameters
---------- ----------
grid : numpy.ndarray of shape (3) cells : numpy.ndarray of shape (3)
number of grid points. number of cells.
size : numpy.ndarray of shape (3) size : numpy.ndarray of shape (3)
physical size of the periodic field. physical size of the periodic field.
origin : numpy.ndarray of shape (3), optional origin : numpy.ndarray of shape (3), optional
physical origin of the periodic field. Defaults to [0.0,0.0,0.0]. physical origin of the periodic field. Defaults to [0.0,0.0,0.0].
""" """
return _np.stack(_np.meshgrid(_np.linspace(origin[0],size[0]+origin[0],grid[0]+1), return _np.stack(_np.meshgrid(_np.linspace(origin[0],size[0]+origin[0],cells[0]+1),
_np.linspace(origin[1],size[1]+origin[1],grid[1]+1), _np.linspace(origin[1],size[1]+origin[1],cells[1]+1),
_np.linspace(origin[2],size[2]+origin[2],grid[2]+1),indexing = 'ij'), _np.linspace(origin[2],size[2]+origin[2],cells[2]+1),indexing = 'ij'),
axis = -1) axis = -1)
@ -366,7 +366,7 @@ def node_2_cell(node_data):
def node_coord0_gridSizeOrigin(coord0,ordered=True): def node_coord0_gridSizeOrigin(coord0,ordered=True):
""" """
Return grid 'DNA', i.e. grid, size, and origin from 1D array of nodal positions. Return grid 'DNA', i.e. cells, size, and origin from 1D array of nodal positions.
Parameters Parameters
---------- ----------
@ -379,37 +379,37 @@ def node_coord0_gridSizeOrigin(coord0,ordered=True):
coords = [_np.unique(coord0[:,i]) for i in range(3)] coords = [_np.unique(coord0[:,i]) for i in range(3)]
mincorner = _np.array(list(map(min,coords))) mincorner = _np.array(list(map(min,coords)))
maxcorner = _np.array(list(map(max,coords))) maxcorner = _np.array(list(map(max,coords)))
grid = _np.array(list(map(len,coords)),'i') - 1 cells = _np.array(list(map(len,coords)),'i') - 1
size = maxcorner-mincorner size = maxcorner-mincorner
origin = mincorner origin = mincorner
if (grid+1).prod() != len(coord0): if (cells+1).prod() != len(coord0):
raise ValueError('Data count {len(coord0)} does not match grid {grid}.') raise ValueError('Data count {len(coord0)} does not match cells {cells}.')
atol = _np.max(size)*5e-2 atol = _np.max(size)*5e-2
if not (_np.allclose(coords[0],_np.linspace(mincorner[0],maxcorner[0],grid[0]+1),atol=atol) and \ if not (_np.allclose(coords[0],_np.linspace(mincorner[0],maxcorner[0],cells[0]+1),atol=atol) and \
_np.allclose(coords[1],_np.linspace(mincorner[1],maxcorner[1],grid[1]+1),atol=atol) and \ _np.allclose(coords[1],_np.linspace(mincorner[1],maxcorner[1],cells[1]+1),atol=atol) and \
_np.allclose(coords[2],_np.linspace(mincorner[2],maxcorner[2],grid[2]+1),atol=atol)): _np.allclose(coords[2],_np.linspace(mincorner[2],maxcorner[2],cells[2]+1),atol=atol)):
raise ValueError('Regular grid spacing violated.') raise ValueError('Regular cells spacing violated.')
if ordered and not _np.allclose(coord0.reshape(tuple(grid+1)+(3,),order='F'),node_coord0(grid,size,origin),atol=atol): if ordered and not _np.allclose(coord0.reshape(tuple(cells+1)+(3,),order='F'),node_coord0(cells,size,origin),atol=atol):
raise ValueError('Input data is not ordered (x fast, z slow).') raise ValueError('Input data is not ordered (x fast, z slow).')
return (grid,size,origin) return (cells,size,origin)
def regrid(size,F,new_grid): def regrid(size,F,cells_new):
""" """
Return mapping from coordinates in deformed configuration to a regular grid. Return mapping from coordinates in deformed configuration to a regular cells.
Parameters Parameters
---------- ----------
size : numpy.ndarray of shape (3) size : numpy.ndarray of shape (3)
physical size Physical size.
F : numpy.ndarray of shape (:,:,:,3,3) F : numpy.ndarray of shape (:,:,:,3,3)
deformation gradient field Deformation gradient field.
new_grid : numpy.ndarray of shape (3) cells_new : numpy.ndarray of shape (3)
new grid for undeformed coordinates New cells for undeformed coordinates.
""" """
c = cell_coord0(F.shape[:3],size) \ c = cell_coord0(F.shape[:3],size) \
@ -422,4 +422,4 @@ def regrid(size,F,new_grid):
c[_np.where(c[:,:,:,d]>outer[d])] -= outer[d] c[_np.where(c[:,:,:,d]>outer[d])] -= outer[d]
tree = _spatial.cKDTree(c.reshape(-1,3),boxsize=outer) tree = _spatial.cKDTree(c.reshape(-1,3),boxsize=outer)
return tree.query(cell_coord0(new_grid,outer))[1].flatten() return tree.query(cell_coord0(cells_new,outer))[1].flatten()

View File

@ -7,7 +7,7 @@ from . import util
from . import grid_filters from . import grid_filters
def from_random(size,N_seeds,grid=None,rng_seed=None): def from_random(size,N_seeds,cells=None,rng_seed=None):
""" """
Random seeding in space. Random seeding in space.
@ -17,7 +17,7 @@ def from_random(size,N_seeds,grid=None,rng_seed=None):
Physical size of the seeding domain. Physical size of the seeding domain.
N_seeds : int N_seeds : int
Number of seeds. Number of seeds.
grid : numpy.ndarray of shape (3), optional. cells : numpy.ndarray of shape (3), optional.
If given, ensures that all seeds initiate one grain if using a If given, ensures that all seeds initiate one grain if using a
standard Voronoi tessellation. standard Voronoi tessellation.
rng_seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional rng_seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional
@ -26,12 +26,12 @@ def from_random(size,N_seeds,grid=None,rng_seed=None):
""" """
rng = _np.random.default_rng(rng_seed) rng = _np.random.default_rng(rng_seed)
if grid is None: if cells is None:
coords = rng.random((N_seeds,3)) * size coords = rng.random((N_seeds,3)) * size
else: else:
grid_coords = grid_filters.cell_coord0(grid,size).reshape(-1,3,order='F') grid_coords = grid_filters.cell_coord0(cells,size).reshape(-1,3,order='F')
coords = grid_coords[rng.choice(_np.prod(grid),N_seeds, replace=False)] \ coords = grid_coords[rng.choice(_np.prod(cells),N_seeds, replace=False)] \
+ _np.broadcast_to(size/grid,(N_seeds,3))*(rng.random((N_seeds,3))*.5-.25) # wobble without leaving grid + _np.broadcast_to(size/cells,(N_seeds,3))*(rng.random((N_seeds,3))*.5-.25) # wobble without leaving cells
return coords return coords
@ -51,7 +51,7 @@ def from_Poisson_disc(size,N_seeds,N_candidates,distance,periodic=True,rng_seed=
distance : float distance : float
Minimum acceptable distance to other seeds. Minimum acceptable distance to other seeds.
periodic : boolean, optional periodic : boolean, optional
Calculate minimum distance for periodically repeated grid. Calculate minimum distance for periodically repeated cells.
rng_seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional rng_seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional
A seed to initialize the BitGenerator. Defaults to None. A seed to initialize the BitGenerator. Defaults to None.
If None, then fresh, unpredictable entropy will be pulled from the OS. If None, then fresh, unpredictable entropy will be pulled from the OS.
@ -96,9 +96,9 @@ def from_geom(geom,selection=None,invert=False,average=False,periodic=True):
""" """
material = geom.material.reshape((-1,1),order='F') material = geom.material.reshape((-1,1),order='F')
mask = _np.full(geom.grid.prod(),True,dtype=bool) if selection is None else \ mask = _np.full(geom.cells.prod(),True,dtype=bool) if selection is None else \
_np.isin(material,selection,invert=invert).flatten() _np.isin(material,selection,invert=invert).flatten()
coords = grid_filters.cell_coord0(geom.grid,geom.size).reshape(-1,3,order='F') coords = grid_filters.cell_coord0(geom.cells,geom.size).reshape(-1,3,order='F')
if not average: if not average:
return (coords[mask],material[mask]) return (coords[mask],material[mask])

View File

@ -12,7 +12,7 @@ from damask import grid_filters
def geom_equal(a,b): def geom_equal(a,b):
return np.all(a.material == b.material) and \ return np.all(a.material == b.material) and \
np.all(a.grid == b.grid) and \ np.all(a.cells == b.cells) 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))
@ -167,7 +167,7 @@ class TestGeom:
) )
@pytest.mark.parametrize('grid',[ @pytest.mark.parametrize('cells',[
(10,11,10), (10,11,10),
[10,13,10], [10,13,10],
np.array((10,10,10)), np.array((10,10,10)),
@ -176,9 +176,9 @@ class TestGeom:
np.array((10,20,2)) np.array((10,20,2))
] ]
) )
def test_scale(self,default,update,ref_path,grid): def test_scale(self,default,update,ref_path,cells):
modified = default.scale(grid) modified = default.scale(cells)
tag = f'grid_{util.srepr(grid,"-")}' tag = f'grid_{util.srepr(cells,"-")}'
reference = ref_path/f'scale_{tag}.vtr' reference = ref_path/f'scale_{tag}.vtr'
if update: modified.save(reference) if update: modified.save(reference)
assert geom_equal(Geom.load(reference), assert geom_equal(Geom.load(reference),
@ -216,8 +216,8 @@ class TestGeom:
assert geom_equal(default, modified.substitute(t,f)) assert geom_equal(default, modified.substitute(t,f))
def test_sort(self): def test_sort(self):
grid = np.random.randint(5,20,3) cells = np.random.randint(5,20,3)
m = Geom(np.random.randint(1,20,grid)*3,np.ones(3)).sort().material.flatten(order='F') m = Geom(np.random.randint(1,20,cells)*3,np.ones(3)).sort().material.flatten(order='F')
for i,v in enumerate(m): for i,v in enumerate(m):
assert i==0 or v > m[:i].max() or v in m[:i] assert i==0 or v > m[:i].max() or v in m[:i]
@ -242,10 +242,10 @@ class TestGeom:
def test_canvas(self,default): def test_canvas(self,default):
grid = default.grid cells = default.cells
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(cells + grid_add)
assert np.all(modified.material[:grid[0],:grid[1],:grid[2]] == default.material) assert np.all(modified.material[:cells[0],:cells[1],:cells[2]] == default.material)
@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),
@ -314,38 +314,38 @@ class TestGeom:
@pytest.mark.parametrize('periodic',[True,False]) @pytest.mark.parametrize('periodic',[True,False])
def test_tessellation_approaches(self,periodic): def test_tessellation_approaches(self,periodic):
grid = np.random.randint(10,20,3) cells = np.random.randint(10,20,3)
size = np.random.random(3) + 1.0 size = np.random.random(3) + 1.0
N_seeds= np.random.randint(10,30) N_seeds= np.random.randint(10,30)
seeds = np.random.rand(N_seeds,3) * np.broadcast_to(size,(N_seeds,3)) seeds = np.random.rand(N_seeds,3) * np.broadcast_to(size,(N_seeds,3))
Voronoi = Geom.from_Voronoi_tessellation( grid,size,seeds, np.arange(N_seeds)+5,periodic) Voronoi = Geom.from_Voronoi_tessellation( cells,size,seeds, np.arange(N_seeds)+5,periodic)
Laguerre = Geom.from_Laguerre_tessellation(grid,size,seeds,np.ones(N_seeds),np.arange(N_seeds)+5,periodic) Laguerre = Geom.from_Laguerre_tessellation(cells,size,seeds,np.ones(N_seeds),np.arange(N_seeds)+5,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) cells = np.random.randint(10,20,3)
size = np.random.random(3) + 1.0 size = np.random.random(3) + 1.0
N_seeds= np.random.randint(10,30) N_seeds= np.random.randint(10,30)
seeds = np.random.rand(N_seeds,3) * np.broadcast_to(size,(N_seeds,3)) seeds = np.random.rand(N_seeds,3) * np.broadcast_to(size,(N_seeds,3))
weights= np.full((N_seeds),-np.inf) weights= np.full((N_seeds),-np.inf)
ms = np.random.randint(N_seeds) ms = np.random.randint(N_seeds)
weights[ms] = np.random.random() weights[ms] = np.random.random()
Laguerre = Geom.from_Laguerre_tessellation(grid,size,seeds,weights,periodic=np.random.random()>0.5) Laguerre = Geom.from_Laguerre_tessellation(cells,size,seeds,weights,periodic=np.random.random()>0.5)
assert np.all(Laguerre.material == ms) assert np.all(Laguerre.material == 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 cells = np.random.randint(5,10,3)*2
size = grid.astype(np.float) size = cells.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])))
material = np.zeros(grid) material = np.zeros(cells)
material[:,grid[1]//2:,:] = 1 material[:,cells[1]//2:,:] = 1
if approach == 'Laguerre': if approach == 'Laguerre':
geom = Geom.from_Laguerre_tessellation(grid,size,seeds,np.ones(2),periodic=np.random.random()>0.5) geom = Geom.from_Laguerre_tessellation(cells,size,seeds,np.ones(2),periodic=np.random.random()>0.5)
elif approach == 'Voronoi': elif approach == 'Voronoi':
geom = Geom.from_Voronoi_tessellation(grid,size,seeds, periodic=np.random.random()>0.5) geom = Geom.from_Voronoi_tessellation(cells,size,seeds, periodic=np.random.random()>0.5)
assert np.all(geom.material == material) assert np.all(geom.material == material)
@ -363,14 +363,14 @@ class TestGeom:
'Fisher-Koch S', 'Fisher-Koch S',
]) ])
def test_minimal_surface_basic_properties(self,surface): def test_minimal_surface_basic_properties(self,surface):
grid = np.random.randint(60,100,3) cells = np.random.randint(60,100,3)
size = np.ones(3)+np.random.rand(3) size = np.ones(3)+np.random.rand(3)
threshold = 2*np.random.rand()-1. threshold = 2*np.random.rand()-1.
periods = np.random.randint(2)+1 periods = np.random.randint(2)+1
materials = np.random.randint(0,40,2) materials = np.random.randint(0,40,2)
geom = Geom.from_minimal_surface(grid,size,surface,threshold,periods,materials) geom = Geom.from_minimal_surface(cells,size,surface,threshold,periods,materials)
assert set(geom.material.flatten()) | set(materials) == set(materials) \ assert set(geom.material.flatten()) | set(materials) == set(materials) \
and (geom.size == size).all() and (geom.grid == grid).all() and (geom.size == size).all() and (geom.cells == cells).all()
@pytest.mark.parametrize('surface,threshold',[('Schwarz P',0), @pytest.mark.parametrize('surface,threshold',[('Schwarz P',0),
('Double Primitive',-1./6.), ('Double Primitive',-1./6.),
@ -386,28 +386,28 @@ class TestGeom:
('Fisher-Koch S',0), ('Fisher-Koch S',0),
]) ])
def test_minimal_surface_volume(self,surface,threshold): def test_minimal_surface_volume(self,surface,threshold):
grid = np.ones(3,dtype=int)*64 cells = np.ones(3,dtype=int)*64
geom = Geom.from_minimal_surface(grid,np.ones(3),surface,threshold) geom = Geom.from_minimal_surface(cells,np.ones(3),surface,threshold)
assert np.isclose(np.count_nonzero(geom.material==1)/np.prod(geom.grid),.5,rtol=1e-3) assert np.isclose(np.count_nonzero(geom.material==1)/np.prod(geom.cells),.5,rtol=1e-3)
def test_from_table(self): def test_from_table(self):
grid = np.random.randint(60,100,3) cells = np.random.randint(60,100,3)
size = np.ones(3)+np.random.rand(3) size = np.ones(3)+np.random.rand(3)
coords = grid_filters.cell_coord0(grid,size).reshape(-1,3,order='F') coords = grid_filters.cell_coord0(cells,size).reshape(-1,3,order='F')
z=np.ones(grid.prod()) z=np.ones(cells.prod())
z[grid[:2].prod()*int(grid[2]/2):]=0 z[cells[:2].prod()*int(cells[2]/2):]=0
t = Table(np.column_stack((coords,z)),{'coords':3,'z':1}) t = Table(np.column_stack((coords,z)),{'coords':3,'z':1})
g = Geom.from_table(t,'coords',['1_coords','z']) g = Geom.from_table(t,'coords',['1_coords','z'])
assert g.N_materials == g.grid[0]*2 and (g.material[:,:,-1]-g.material[:,:,0] == grid[0]).all() assert g.N_materials == g.cells[0]*2 and (g.material[:,:,-1]-g.material[:,:,0] == cells[0]).all()
def test_from_table_recover(self,tmp_path): def test_from_table_recover(self,tmp_path):
grid = np.random.randint(60,100,3) cells = np.random.randint(60,100,3)
size = np.ones(3)+np.random.rand(3) size = np.ones(3)+np.random.rand(3)
s = seeds.from_random(size,np.random.randint(60,100)) s = seeds.from_random(size,np.random.randint(60,100))
geom = Geom.from_Voronoi_tessellation(grid,size,s) geom = Geom.from_Voronoi_tessellation(cells,size,s)
coords = grid_filters.cell_coord0(grid,size) coords = grid_filters.cell_coord0(cells,size)
t = Table(np.column_stack((coords.reshape(-1,3,order='F'),geom.material.flatten(order='F'))),{'c':3,'m':1}) t = Table(np.column_stack((coords.reshape(-1,3,order='F'),geom.material.flatten(order='F'))),{'c':3,'m':1})
assert geom_equal(geom.sort().renumber(),Geom.from_table(t,'c',['m'])) assert geom_equal(geom.sort().renumber(),Geom.from_table(t,'c',['m']))

View File

@ -356,11 +356,11 @@ class TestResult:
@pytest.mark.parametrize('mode',['cell','node']) @pytest.mark.parametrize('mode',['cell','node'])
def test_coordinates(self,default,mode): def test_coordinates(self,default,mode):
if mode == 'cell': if mode == 'cell':
a = grid_filters.cell_coord0(default.grid,default.size,default.origin) a = grid_filters.cell_coord0(default.cells,default.size,default.origin)
b = default.cell_coordinates.reshape(tuple(default.grid)+(3,),order='F') b = default.cell_coordinates.reshape(tuple(default.cells)+(3,),order='F')
elif mode == 'node': elif mode == 'node':
a = grid_filters.node_coord0(default.grid,default.size,default.origin) a = grid_filters.node_coord0(default.cells,default.size,default.origin)
b = default.node_coordinates.reshape(tuple(default.grid+1)+(3,),order='F') b = default.node_coordinates.reshape(tuple(default.cells+1)+(3,),order='F')
assert np.allclose(a,b) assert np.allclose(a,b)
@pytest.mark.parametrize('output',['F',[],['F','P']]) @pytest.mark.parametrize('output',['F',[],['F','P']])

View File

@ -16,9 +16,9 @@ def ref_path(ref_path_base):
@pytest.fixture @pytest.fixture
def default(): def default():
"""Simple VTK.""" """Simple VTK."""
grid = np.array([5,6,7],int) cells = np.array([5,6,7],int)
size = np.array([.6,1.,.5]) size = np.array([.6,1.,.5])
return VTK.from_rectilinear_grid(grid,size) return VTK.from_rectilinear_grid(cells,size)
class TestVTK: class TestVTK:
@ -27,10 +27,10 @@ class TestVTK:
print('patched damask.util.execution_stamp') print('patched damask.util.execution_stamp')
def test_rectilinearGrid(self,tmp_path): def test_rectilinearGrid(self,tmp_path):
grid = np.random.randint(5,10,3)*2 cells = np.random.randint(5,10,3)*2
size = np.random.random(3) + 1.0 size = np.random.random(3) + 1.0
origin = np.random.random(3) origin = np.random.random(3)
v = VTK.from_rectilinear_grid(grid,size,origin) v = VTK.from_rectilinear_grid(cells,size,origin)
string = v.__repr__() string = v.__repr__()
v.save(tmp_path/'rectilinearGrid',False) v.save(tmp_path/'rectilinearGrid',False)
vtr = VTK.load(tmp_path/'rectilinearGrid.vtr') vtr = VTK.load(tmp_path/'rectilinearGrid.vtr')
@ -152,11 +152,11 @@ class TestVTK:
np.allclose(polyData.get('coordinates'),points) np.allclose(polyData.get('coordinates'),points)
def test_compare_reference_rectilinearGrid(self,update,ref_path,tmp_path): def test_compare_reference_rectilinearGrid(self,update,ref_path,tmp_path):
grid = np.array([5,6,7],int) cells = np.array([5,6,7],int)
size = np.array([.6,1.,.5]) size = np.array([.6,1.,.5])
rectilinearGrid = VTK.from_rectilinear_grid(grid,size) rectilinearGrid = VTK.from_rectilinear_grid(cells,size)
c = grid_filters.cell_coord0(grid,size).reshape(-1,3,order='F') c = grid_filters.cell_coord0(cells,size).reshape(-1,3,order='F')
n = grid_filters.node_coord0(grid,size).reshape(-1,3,order='F') n = grid_filters.node_coord0(cells,size).reshape(-1,3,order='F')
rectilinearGrid.add(c,'cell') rectilinearGrid.add(c,'cell')
rectilinearGrid.add(n,'node') rectilinearGrid.add(n,'node')
if update: if update:

View File

@ -7,58 +7,58 @@ class TestGridFilters:
def test_cell_coord0(self): def test_cell_coord0(self):
size = np.random.random(3) size = np.random.random(3)
grid = np.random.randint(8,32,(3)) cells = np.random.randint(8,32,(3))
coord = grid_filters.cell_coord0(grid,size) coord = grid_filters.cell_coord0(cells,size)
assert np.allclose(coord[0,0,0],size/grid*.5) and coord.shape == tuple(grid) + (3,) assert np.allclose(coord[0,0,0],size/cells*.5) and coord.shape == tuple(cells) + (3,)
def test_node_coord0(self): def test_node_coord0(self):
size = np.random.random(3) size = np.random.random(3)
grid = np.random.randint(8,32,(3)) cells = np.random.randint(8,32,(3))
coord = grid_filters.node_coord0(grid,size) coord = grid_filters.node_coord0(cells,size)
assert np.allclose(coord[-1,-1,-1],size) and coord.shape == tuple(grid+1) + (3,) assert np.allclose(coord[-1,-1,-1],size) and coord.shape == tuple(cells+1) + (3,)
def test_coord0(self): def test_coord0(self):
size = np.random.random(3) size = np.random.random(3)
grid = np.random.randint(8,32,(3)) cells = np.random.randint(8,32,(3))
c = grid_filters.cell_coord0(grid+1,size+size/grid) c = grid_filters.cell_coord0(cells+1,size+size/cells)
n = grid_filters.node_coord0(grid,size) + size/grid*.5 n = grid_filters.node_coord0(cells,size) + size/cells*.5
assert np.allclose(c,n) assert np.allclose(c,n)
@pytest.mark.parametrize('mode',['cell','node']) @pytest.mark.parametrize('mode',['cell','node'])
def test_grid_DNA(self,mode): def test_grid_DNA(self,mode):
"""Ensure that xx_coord0_gridSizeOrigin is the inverse of xx_coord0.""" """Ensure that xx_coord0_gridSizeOrigin is the inverse of xx_coord0."""
grid = np.random.randint(8,32,(3)) cells = np.random.randint(8,32,(3))
size = np.random.random(3) size = np.random.random(3)
origin = np.random.random(3) origin = np.random.random(3)
coord0 = eval(f'grid_filters.{mode}_coord0(grid,size,origin)') # noqa coord0 = eval(f'grid_filters.{mode}_coord0(cells,size,origin)') # noqa
_grid,_size,_origin = eval(f'grid_filters.{mode}_coord0_gridSizeOrigin(coord0.reshape(-1,3,order="F"))') _cells,_size,_origin = eval(f'grid_filters.{mode}_coord0_gridSizeOrigin(coord0.reshape(-1,3,order="F"))')
assert np.allclose(grid,_grid) and np.allclose(size,_size) and np.allclose(origin,_origin) assert np.allclose(cells,_cells) and np.allclose(size,_size) and np.allclose(origin,_origin)
def test_displacement_fluct_equivalence(self): def test_displacement_fluct_equivalence(self):
"""Ensure that fluctuations are periodic.""" """Ensure that fluctuations are periodic."""
size = np.random.random(3) size = np.random.random(3)
grid = np.random.randint(8,32,(3)) cells = np.random.randint(8,32,(3))
F = np.random.random(tuple(grid)+(3,3)) F = np.random.random(tuple(cells)+(3,3))
assert np.allclose(grid_filters.node_displacement_fluct(size,F), assert np.allclose(grid_filters.node_displacement_fluct(size,F),
grid_filters.cell_2_node(grid_filters.cell_displacement_fluct(size,F))) grid_filters.cell_2_node(grid_filters.cell_displacement_fluct(size,F)))
def test_interpolation_to_node(self): def test_interpolation_to_node(self):
size = np.random.random(3) size = np.random.random(3)
grid = np.random.randint(8,32,(3)) cells = np.random.randint(8,32,(3))
F = np.random.random(tuple(grid)+(3,3)) F = np.random.random(tuple(cells)+(3,3))
assert np.allclose(grid_filters.node_coord(size,F) [1:-1,1:-1,1:-1], assert np.allclose(grid_filters.node_coord(size,F) [1:-1,1:-1,1:-1],
grid_filters.cell_2_node(grid_filters.cell_coord(size,F))[1:-1,1:-1,1:-1]) grid_filters.cell_2_node(grid_filters.cell_coord(size,F))[1:-1,1:-1,1:-1])
def test_interpolation_to_cell(self): def test_interpolation_to_cell(self):
grid = np.random.randint(1,30,(3)) cells = np.random.randint(1,30,(3))
node_coord_x = np.linspace(0,np.pi*2,num=grid[0]+1) node_coord_x = np.linspace(0,np.pi*2,num=cells[0]+1)
node_field_x = np.cos(node_coord_x) node_field_x = np.cos(node_coord_x)
node_field = np.broadcast_to(node_field_x.reshape(-1,1,1),grid+1) node_field = np.broadcast_to(node_field_x.reshape(-1,1,1),cells+1)
cell_coord_x = node_coord_x[:-1]+node_coord_x[1]*.5 cell_coord_x = node_coord_x[:-1]+node_coord_x[1]*.5
cell_field_x = np.interp(cell_coord_x,node_coord_x,node_field_x,period=np.pi*2.) cell_field_x = np.interp(cell_coord_x,node_coord_x,node_field_x,period=np.pi*2.)
cell_field = np.broadcast_to(cell_field_x.reshape(-1,1,1),grid) cell_field = np.broadcast_to(cell_field_x.reshape(-1,1,1),cells)
assert np.allclose(cell_field,grid_filters.node_2_cell(node_field)) assert np.allclose(cell_field,grid_filters.node_2_cell(node_field))
@ -66,21 +66,21 @@ class TestGridFilters:
def test_coord0_origin(self,mode): def test_coord0_origin(self,mode):
origin= np.random.random(3) origin= np.random.random(3)
size = np.random.random(3) # noqa size = np.random.random(3) # noqa
grid = np.random.randint(8,32,(3)) cells = np.random.randint(8,32,(3))
shifted = eval(f'grid_filters.{mode}_coord0(grid,size,origin)') shifted = eval(f'grid_filters.{mode}_coord0(cells,size,origin)')
unshifted = eval(f'grid_filters.{mode}_coord0(grid,size)') unshifted = eval(f'grid_filters.{mode}_coord0(cells,size)')
if mode == 'cell': if mode == 'cell':
assert np.allclose(shifted,unshifted+np.broadcast_to(origin,tuple(grid) +(3,))) assert np.allclose(shifted,unshifted+np.broadcast_to(origin,tuple(cells) +(3,)))
elif mode == 'node': elif mode == 'node':
assert np.allclose(shifted,unshifted+np.broadcast_to(origin,tuple(grid+1)+(3,))) assert np.allclose(shifted,unshifted+np.broadcast_to(origin,tuple(cells+1)+(3,)))
@pytest.mark.parametrize('function',[grid_filters.cell_displacement_avg, @pytest.mark.parametrize('function',[grid_filters.cell_displacement_avg,
grid_filters.node_displacement_avg]) grid_filters.node_displacement_avg])
def test_displacement_avg_vanishes(self,function): def test_displacement_avg_vanishes(self,function):
"""Ensure that random fluctuations in F do not result in average displacement.""" """Ensure that random fluctuations in F do not result in average displacement."""
size = np.random.random(3) size = np.random.random(3)
grid = np.random.randint(8,32,(3)) cells = np.random.randint(8,32,(3))
F = np.random.random(tuple(grid)+(3,3)) F = np.random.random(tuple(cells)+(3,3))
F += np.eye(3) - np.average(F,axis=(0,1,2)) F += np.eye(3) - np.average(F,axis=(0,1,2))
assert np.allclose(function(size,F),0.0) assert np.allclose(function(size,F),0.0)
@ -89,8 +89,8 @@ class TestGridFilters:
def test_displacement_fluct_vanishes(self,function): def test_displacement_fluct_vanishes(self,function):
"""Ensure that constant F does not result in fluctuating displacement.""" """Ensure that constant F does not result in fluctuating displacement."""
size = np.random.random(3) size = np.random.random(3)
grid = np.random.randint(8,32,(3)) cells = np.random.randint(8,32,(3))
F = np.broadcast_to(np.random.random((3,3)), tuple(grid)+(3,3)) F = np.broadcast_to(np.random.random((3,3)), tuple(cells)+(3,3))
assert np.allclose(function(size,F),0.0) assert np.allclose(function(size,F),0.0)
@pytest.mark.parametrize('function',[grid_filters.coord0_check, @pytest.mark.parametrize('function',[grid_filters.coord0_check,
@ -106,11 +106,11 @@ class TestGridFilters:
def test_uneven_spaced_coordinates(self,function): def test_uneven_spaced_coordinates(self,function):
start = np.random.random(3) start = np.random.random(3)
end = np.random.random(3)*10. + start end = np.random.random(3)*10. + start
grid = np.random.randint(8,32,(3)) cells = np.random.randint(8,32,(3))
uneven = np.stack(np.meshgrid(np.logspace(start[0],end[0],grid[0]), uneven = np.stack(np.meshgrid(np.logspace(start[0],end[0],cells[0]),
np.logspace(start[1],end[1],grid[1]), np.logspace(start[1],end[1],cells[1]),
np.logspace(start[2],end[2],grid[2]),indexing = 'ij'), np.logspace(start[2],end[2],cells[2]),indexing = 'ij'),
axis = -1).reshape((grid.prod(),3),order='F') axis = -1).reshape((cells.prod(),3),order='F')
with pytest.raises(ValueError): with pytest.raises(ValueError):
function(uneven) function(uneven)
@ -121,8 +121,8 @@ class TestGridFilters:
def test_unordered_coordinates(self,function,mode): def test_unordered_coordinates(self,function,mode):
origin = np.random.random(3) origin = np.random.random(3)
size = np.random.random(3)*10.+origin size = np.random.random(3)*10.+origin
grid = np.random.randint(8,32,(3)) cells = np.random.randint(8,32,(3))
unordered = grid_filters.node_coord0(grid,size,origin).reshape(-1,3) unordered = grid_filters.node_coord0(cells,size,origin).reshape(-1,3)
if mode: if mode:
with pytest.raises(ValueError): with pytest.raises(ValueError):
function(unordered,mode) function(unordered,mode)
@ -131,9 +131,9 @@ class TestGridFilters:
def test_regrid(self): def test_regrid(self):
size = np.random.random(3) size = np.random.random(3)
grid = np.random.randint(8,32,(3)) cells = np.random.randint(8,32,(3))
F = np.broadcast_to(np.eye(3), tuple(grid)+(3,3)) F = np.broadcast_to(np.eye(3), tuple(cells)+(3,3))
assert all(grid_filters.regrid(size,F,grid) == np.arange(grid.prod())) assert all(grid_filters.regrid(size,F,cells) == np.arange(cells.prod()))
@pytest.mark.parametrize('differential_operator',[grid_filters.curl, @pytest.mark.parametrize('differential_operator',[grid_filters.curl,
@ -141,14 +141,14 @@ class TestGridFilters:
grid_filters.gradient]) grid_filters.gradient])
def test_differential_operator_constant(self,differential_operator): def test_differential_operator_constant(self,differential_operator):
size = np.random.random(3)+1.0 size = np.random.random(3)+1.0
grid = np.random.randint(8,32,(3)) cells = np.random.randint(8,32,(3))
shapes = { shapes = {
grid_filters.curl: [(3,),(3,3)], grid_filters.curl: [(3,),(3,3)],
grid_filters.divergence:[(3,),(3,3)], grid_filters.divergence:[(3,),(3,3)],
grid_filters.gradient: [(1,),(3,)] grid_filters.gradient: [(1,),(3,)]
} }
for shape in shapes[differential_operator]: for shape in shapes[differential_operator]:
field = np.ones(tuple(grid)+shape)*np.random.random()*1.0e5 field = np.ones(tuple(cells)+shape)*np.random.random()*1.0e5
assert np.allclose(differential_operator(size,field),0.0) assert np.allclose(differential_operator(size,field),0.0)
@ -190,15 +190,15 @@ class TestGridFilters:
@pytest.mark.parametrize('field_def,grad_def',grad_test_data) @pytest.mark.parametrize('field_def,grad_def',grad_test_data)
def test_grad(self,field_def,grad_def): def test_grad(self,field_def,grad_def):
size = np.random.random(3)+1.0 size = np.random.random(3)+1.0
grid = np.random.randint(8,32,(3)) cells = np.random.randint(8,32,(3))
nodes = grid_filters.cell_coord0(grid,size) nodes = grid_filters.cell_coord0(cells,size)
my_locals = locals() # needed for list comprehension my_locals = locals() # needed for list comprehension
field = np.stack([np.broadcast_to(eval(f,globals(),my_locals),grid) for f in field_def],axis=-1) field = np.stack([np.broadcast_to(eval(f,globals(),my_locals),cells) for f in field_def],axis=-1)
field = field.reshape(tuple(grid) + ((3,) if len(field_def)==3 else (1,))) field = field.reshape(tuple(cells) + ((3,) if len(field_def)==3 else (1,)))
grad = np.stack([np.broadcast_to(eval(c,globals(),my_locals),grid) for c in grad_def], axis=-1) grad = np.stack([np.broadcast_to(eval(c,globals(),my_locals),cells) for c in grad_def], axis=-1)
grad = grad.reshape(tuple(grid) + ((3,3) if len(grad_def)==9 else (3,))) grad = grad.reshape(tuple(cells) + ((3,3) if len(grad_def)==9 else (3,)))
assert np.allclose(grad,grid_filters.gradient(size,field)) assert np.allclose(grad,grid_filters.gradient(size,field))
@ -250,15 +250,15 @@ class TestGridFilters:
@pytest.mark.parametrize('field_def,curl_def',curl_test_data) @pytest.mark.parametrize('field_def,curl_def',curl_test_data)
def test_curl(self,field_def,curl_def): def test_curl(self,field_def,curl_def):
size = np.random.random(3)+1.0 size = np.random.random(3)+1.0
grid = np.random.randint(8,32,(3)) cells = np.random.randint(8,32,(3))
nodes = grid_filters.cell_coord0(grid,size) nodes = grid_filters.cell_coord0(cells,size)
my_locals = locals() # needed for list comprehension my_locals = locals() # needed for list comprehension
field = np.stack([np.broadcast_to(eval(f,globals(),my_locals),grid) for f in field_def],axis=-1) field = np.stack([np.broadcast_to(eval(f,globals(),my_locals),cells) for f in field_def],axis=-1)
field = field.reshape(tuple(grid) + ((3,3) if len(field_def)==9 else (3,))) field = field.reshape(tuple(cells) + ((3,3) if len(field_def)==9 else (3,)))
curl = np.stack([np.broadcast_to(eval(c,globals(),my_locals),grid) for c in curl_def], axis=-1) curl = np.stack([np.broadcast_to(eval(c,globals(),my_locals),cells) for c in curl_def], axis=-1)
curl = curl.reshape(tuple(grid) + ((3,3) if len(curl_def)==9 else (3,))) curl = curl.reshape(tuple(cells) + ((3,3) if len(curl_def)==9 else (3,)))
assert np.allclose(curl,grid_filters.curl(size,field)) assert np.allclose(curl,grid_filters.curl(size,field))
@ -303,17 +303,17 @@ class TestGridFilters:
def test_div(self,field_def,div_def): def test_div(self,field_def,div_def):
size = np.random.random(3)+1.0 size = np.random.random(3)+1.0
grid = np.random.randint(8,32,(3)) cells = np.random.randint(8,32,(3))
nodes = grid_filters.cell_coord0(grid,size) nodes = grid_filters.cell_coord0(cells,size)
my_locals = locals() # needed for list comprehension my_locals = locals() # needed for list comprehension
field = np.stack([np.broadcast_to(eval(f,globals(),my_locals),grid) for f in field_def],axis=-1) field = np.stack([np.broadcast_to(eval(f,globals(),my_locals),cells) for f in field_def],axis=-1)
field = field.reshape(tuple(grid) + ((3,3) if len(field_def)==9 else (3,))) field = field.reshape(tuple(cells) + ((3,3) if len(field_def)==9 else (3,)))
div = np.stack([np.broadcast_to(eval(c,globals(),my_locals),grid) for c in div_def], axis=-1) div = np.stack([np.broadcast_to(eval(c,globals(),my_locals),cells) for c in div_def], axis=-1)
if len(div_def)==3: if len(div_def)==3:
div = div.reshape(tuple(grid) + ((3,))) div = div.reshape(tuple(cells) + ((3,)))
else: else:
div=div.reshape(tuple(grid)) div=div.reshape(tuple(cells))
assert np.allclose(div,grid_filters.divergence(size,field)) assert np.allclose(div,grid_filters.divergence(size,field))

View File

@ -8,11 +8,11 @@ from damask import Geom
class TestSeeds: class TestSeeds:
@pytest.mark.parametrize('grid',[None,np.ones(3,dtype='i')*10]) @pytest.mark.parametrize('cells',[None,np.ones(3,dtype='i')*10])
def test_from_random(self,grid): def test_from_random(self,cells):
N_seeds = np.random.randint(30,300) N_seeds = np.random.randint(30,300)
size = np.ones(3) + np.random.random(3) size = np.ones(3) + np.random.random(3)
coords = seeds.from_random(size,N_seeds,grid) coords = seeds.from_random(size,N_seeds,cells)
assert (0<=coords).all() and (coords<size).all() assert (0<=coords).all() and (coords<size).all()
@pytest.mark.parametrize('periodic',[True,False]) @pytest.mark.parametrize('periodic',[True,False])
@ -27,36 +27,36 @@ class TestSeeds:
assert (0<= coords).all() and (coords<size).all() and np.min(min_dists[:,1])>=distance assert (0<= coords).all() and (coords<size).all() and np.min(min_dists[:,1])>=distance
def test_from_geom_reconstruct(self): def test_from_geom_reconstruct(self):
grid = np.random.randint(10,20,3) cells = np.random.randint(10,20,3)
N_seeds = np.random.randint(30,300) N_seeds = np.random.randint(30,300)
size = np.ones(3) + np.random.random(3) size = np.ones(3) + np.random.random(3)
coords = seeds.from_random(size,N_seeds,grid) coords = seeds.from_random(size,N_seeds,cells)
geom_1 = Geom.from_Voronoi_tessellation(grid,size,coords) geom_1 = Geom.from_Voronoi_tessellation(cells,size,coords)
coords,material = seeds.from_geom(geom_1) coords,material = seeds.from_geom(geom_1)
geom_2 = Geom.from_Voronoi_tessellation(grid,size,coords,material) geom_2 = Geom.from_Voronoi_tessellation(cells,size,coords,material)
assert (geom_2.material==geom_1.material).all() assert (geom_2.material==geom_1.material).all()
@pytest.mark.parametrize('periodic',[True,False]) @pytest.mark.parametrize('periodic',[True,False])
@pytest.mark.parametrize('average',[True,False]) @pytest.mark.parametrize('average',[True,False])
def test_from_geom_grid(self,periodic,average): def test_from_geom_grid(self,periodic,average):
grid = np.random.randint(10,20,3) cells = np.random.randint(10,20,3)
size = np.ones(3) + np.random.random(3) size = np.ones(3) + np.random.random(3)
coords = grid_filters.cell_coord0(grid,size).reshape(-1,3) coords = grid_filters.cell_coord0(cells,size).reshape(-1,3)
np.random.shuffle(coords) np.random.shuffle(coords)
geom_1 = Geom.from_Voronoi_tessellation(grid,size,coords) geom_1 = Geom.from_Voronoi_tessellation(cells,size,coords)
coords,material = seeds.from_geom(geom_1,average=average,periodic=periodic) coords,material = seeds.from_geom(geom_1,average=average,periodic=periodic)
geom_2 = Geom.from_Voronoi_tessellation(grid,size,coords,material) geom_2 = Geom.from_Voronoi_tessellation(cells,size,coords,material)
assert (geom_2.material==geom_1.material).all() assert (geom_2.material==geom_1.material).all()
@pytest.mark.parametrize('periodic',[True,False]) @pytest.mark.parametrize('periodic',[True,False])
@pytest.mark.parametrize('average',[True,False]) @pytest.mark.parametrize('average',[True,False])
@pytest.mark.parametrize('invert',[True,False]) @pytest.mark.parametrize('invert',[True,False])
def test_from_geom_selection(self,periodic,average,invert): def test_from_geom_selection(self,periodic,average,invert):
grid = np.random.randint(10,20,3) cells = np.random.randint(10,20,3)
N_seeds = np.random.randint(30,300) N_seeds = np.random.randint(30,300)
size = np.ones(3) + np.random.random(3) size = np.ones(3) + np.random.random(3)
coords = seeds.from_random(size,N_seeds,grid) coords = seeds.from_random(size,N_seeds,cells)
geom = Geom.from_Voronoi_tessellation(grid,size,coords) geom = Geom.from_Voronoi_tessellation(cells,size,coords)
selection=np.random.randint(N_seeds)+1 selection=np.random.randint(N_seeds)+1
coords,material = seeds.from_geom(geom,average=average,periodic=periodic,invert=invert,selection=[selection]) coords,material = seeds.from_geom(geom,average=average,periodic=periodic,invert=invert,selection=[selection])
assert selection not in material if invert else (selection==material).all() assert selection not in material if invert else (selection==material).all()

View File

@ -86,7 +86,7 @@ subroutine discretization_results
u = discretization_IPcoords & u = discretization_IPcoords &
- discretization_IPcoords0 - discretization_IPcoords0
call results_writeDataset('current/geometry',u,'u_p','displacements of the materialpoints','m') call results_writeDataset('current/geometry',u,'u_p','displacements of the materialpoints (cell centers)','m')
end subroutine discretization_results end subroutine discretization_results

View File

@ -79,7 +79,7 @@ subroutine discretization_grid_init(restart)
call MPI_Bcast(origin,3,MPI_DOUBLE,0,PETSC_COMM_WORLD, ierr) call MPI_Bcast(origin,3,MPI_DOUBLE,0,PETSC_COMM_WORLD, ierr)
if (ierr /= 0) error stop 'MPI error' if (ierr /= 0) error stop 'MPI error'
print'(/,a,3(i12 ))', ' grid a b c: ', grid print'(/,a,3(i12 ))', ' cells a b c: ', grid
print'(a,3(es12.5))', ' size x y z: ', geomSize print'(a,3(es12.5))', ' size x y z: ', geomSize
print'(a,3(es12.5))', ' origin x y z: ', origin print'(a,3(es12.5))', ' origin x y z: ', origin
@ -125,9 +125,9 @@ subroutine discretization_grid_init(restart)
if(.not. restart) then if(.not. restart) then
call results_openJobFile call results_openJobFile
call results_closeGroup(results_addGroup('geometry')) call results_closeGroup(results_addGroup('geometry'))
call results_addAttribute('grid', grid, 'geometry') call results_addAttribute('cells', grid, '/geometry')
call results_addAttribute('size', geomSize,'geometry') call results_addAttribute('size', geomSize,'/geometry')
call results_addAttribute('origin',origin, 'geometry') call results_addAttribute('origin',origin, '/geometry')
call results_closeJobFile call results_closeJobFile
endif endif

View File

@ -162,7 +162,7 @@ subroutine writeGeometry(elem, &
coordinates_temp = coordinates_points coordinates_temp = coordinates_points
call results_writeDataset('geometry',coordinates_temp,'x_p', & call results_writeDataset('geometry',coordinates_temp,'x_p', &
'initial coordinates of the materialpoints','m') 'initial coordinates of the materialpoints (cell centers)','m')
call results_closeJobFile call results_closeJobFile

View File

@ -74,7 +74,7 @@ subroutine results_init(restart)
if(.not. restart) then if(.not. restart) then
resultsFile = HDF5_openFile(trim(getSolverJobName())//'.hdf5','w',.true.) resultsFile = HDF5_openFile(trim(getSolverJobName())//'.hdf5','w',.true.)
call results_addAttribute('DADF5_version_major',0) call results_addAttribute('DADF5_version_major',0)
call results_addAttribute('DADF5_version_minor',9) call results_addAttribute('DADF5_version_minor',10)
call results_addAttribute('DAMASK_version',DAMASKVERSION) call results_addAttribute('DAMASK_version',DAMASKVERSION)
call get_command(commandLine) call get_command(commandLine)
call results_addAttribute('Call',trim(commandLine)) call results_addAttribute('Call',trim(commandLine))