Merge branch 'development' into CI-with-statistics
This commit is contained in:
commit
f57a9aa952
|
@ -45,7 +45,7 @@ variables:
|
||||||
# ++++++++++++ PETSc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
# ++++++++++++ PETSc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
PETSC_GNU: "Libraries/PETSc/3.16.4/GNU-10-OpenMPI-4.1.2"
|
PETSC_GNU: "Libraries/PETSc/3.16.4/GNU-10-OpenMPI-4.1.2"
|
||||||
PETSC_INTELLLVM: "Libraries/PETSc/3.16.3/oneAPI-2022.0.1-IntelMPI-2021.5.0"
|
PETSC_INTELLLVM: "Libraries/PETSc/3.16.3/oneAPI-2022.0.1-IntelMPI-2021.5.0"
|
||||||
PETSC_INTEL: "Libraries/PETSc/3.16.4/Intel-2022.0.1-IntelMPI-2021.5.0"
|
PETSC_INTEL: "Libraries/PETSc/3.16.5/Intel-2022.0.1-IntelMPI-2021.5.0"
|
||||||
# ++++++++++++ MSC Marc +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
# ++++++++++++ MSC Marc +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
MSC: "FEM/MSC/2021.3.1"
|
MSC: "FEM/MSC/2021.3.1"
|
||||||
IntelMarc: "Compiler/Intel/19.1.2 Libraries/IMKL/2020"
|
IntelMarc: "Compiler/Intel/19.1.2 Libraries/IMKL/2020"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
v3.0.0-alpha6-126-g8d9d13c15
|
v3.0.0-alpha6-140-g527c1c222
|
||||||
|
|
|
@ -373,7 +373,7 @@ class Colormap(mpl.colors.ListedColormap):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
labels = {'RGBA':4} if self.colors.shape[1] == 4 else {'RGB': 3}
|
labels = {'RGBA':4} if self.colors.shape[1] == 4 else {'RGB': 3}
|
||||||
t = Table(self.colors,labels,f'Creator: {util.execution_stamp("Colormap")}')
|
t = Table(labels,self.colors,f'Creator: {util.execution_stamp("Colormap")}')
|
||||||
t.save(self._get_file_handle(fname,'.txt'))
|
t.save(self._get_file_handle(fname,'.txt'))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -662,9 +662,9 @@ class Grid:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
v = VTK.from_image_data(self.cells,self.size,self.origin)\
|
v = VTK.from_image_data(self.cells,self.size,self.origin)\
|
||||||
.add(self.material.flatten(order='F'),'material')
|
.add('material',self.material.flatten(order='F'))
|
||||||
for label,data in self.ic.items():
|
for label,data in self.ic.items():
|
||||||
v = v.add(data.flatten(order='F'),label)
|
v = v.add(label,data.flatten(order='F'))
|
||||||
v.comments = self.comments
|
v.comments = self.comments
|
||||||
|
|
||||||
v.save(fname,parallel=False,compress=compress)
|
v.save(fname,parallel=False,compress=compress)
|
||||||
|
@ -713,10 +713,372 @@ class Grid:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
VTK.from_image_data(self.cells,self.size,self.origin) \
|
VTK.from_image_data(self.cells,self.size,self.origin) \
|
||||||
.add(self.material.flatten('F'),'material') \
|
.add('material',self.material.flatten('F'),) \
|
||||||
.show('material',colormap)
|
.show('material',colormap)
|
||||||
|
|
||||||
|
|
||||||
|
def canvas(self,
|
||||||
|
cells: IntSequence = None,
|
||||||
|
offset: IntSequence = None,
|
||||||
|
fill: int = None) -> 'Grid':
|
||||||
|
"""
|
||||||
|
Crop or enlarge/pad grid.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
cells : sequence of int, len (3), optional
|
||||||
|
Number of cells x,y,z direction.
|
||||||
|
offset : sequence of int, len (3), optional
|
||||||
|
Offset (measured in cells) from old to new grid.
|
||||||
|
Defaults to [0,0,0].
|
||||||
|
fill : int, optional
|
||||||
|
Material ID to fill the background.
|
||||||
|
Defaults to material.max() + 1.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
updated : damask.Grid
|
||||||
|
Updated grid-based geometry.
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
Remove lower 1/2 of the microstructure in z-direction.
|
||||||
|
|
||||||
|
>>> import numpy as np
|
||||||
|
>>> import damask
|
||||||
|
>>> g = damask.Grid(np.zeros([32]*3,int),np.ones(3)*1e-4)
|
||||||
|
>>> g.canvas([32,32,16],[0,0,16])
|
||||||
|
cells : 33 x 32 x 16
|
||||||
|
size : 0.0001 x 0.0001 x 5e-05 m³
|
||||||
|
origin: 0.0 0.0 5e-05 m
|
||||||
|
# materials: 1
|
||||||
|
|
||||||
|
"""
|
||||||
|
offset_ = np.array(offset,int) if offset is not None else np.zeros(3,int)
|
||||||
|
cells_ = np.array(cells,int) if cells is not None else self.cells
|
||||||
|
|
||||||
|
canvas = np.full(cells_,np.nanmax(self.material) + 1 if fill is None else fill,self.material.dtype)
|
||||||
|
|
||||||
|
LL = np.clip( offset_, 0,np.minimum(self.cells, cells_+offset_))
|
||||||
|
UR = np.clip( offset_+cells_, 0,np.minimum(self.cells, cells_+offset_))
|
||||||
|
ll = np.clip(-offset_, 0,np.minimum( cells_,self.cells-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]]
|
||||||
|
|
||||||
|
return Grid(material = canvas,
|
||||||
|
size = self.size/self.cells*np.asarray(canvas.shape),
|
||||||
|
origin = self.origin+offset_*self.size/self.cells,
|
||||||
|
comments = self.comments+[util.execution_stamp('Grid','canvas')],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def mirror(self,
|
||||||
|
directions: Sequence[str],
|
||||||
|
reflect: bool = False) -> 'Grid':
|
||||||
|
"""
|
||||||
|
Mirror grid along given directions.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
directions : (sequence of) {'x', 'y', 'z'}
|
||||||
|
Direction(s) along which the grid is mirrored.
|
||||||
|
reflect : bool, optional
|
||||||
|
Reflect (include) outermost layers. Defaults to False.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
updated : damask.Grid
|
||||||
|
Updated grid-based geometry.
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
Mirror along x- and y-direction.
|
||||||
|
|
||||||
|
>>> import numpy as np
|
||||||
|
>>> import damask
|
||||||
|
>>> g = damask.Grid(np.zeros([32]*3,int), np.ones(3)*1e-4)
|
||||||
|
>>> g.mirror('xy',True)
|
||||||
|
cells : 64 x 64 x 32
|
||||||
|
size : 0.0002 x 0.0002 x 0.0001 m³
|
||||||
|
origin: 0.0 0.0 0.0 m
|
||||||
|
# materials: 1
|
||||||
|
|
||||||
|
"""
|
||||||
|
if not set(directions).issubset(valid := ['x', 'y', 'z']):
|
||||||
|
raise ValueError(f'invalid direction "{set(directions).difference(valid)}" specified')
|
||||||
|
|
||||||
|
limits: Sequence[Optional[int]] = [None,None] if reflect else [-2,0]
|
||||||
|
mat = self.material.copy()
|
||||||
|
|
||||||
|
if 'x' in directions:
|
||||||
|
mat = np.concatenate([mat,mat[limits[0]:limits[1]:-1,:,:]],0)
|
||||||
|
if 'y' in directions:
|
||||||
|
mat = np.concatenate([mat,mat[:,limits[0]:limits[1]:-1,:]],1)
|
||||||
|
if 'z' in directions:
|
||||||
|
mat = np.concatenate([mat,mat[:,:,limits[0]:limits[1]:-1]],2)
|
||||||
|
|
||||||
|
return Grid(material = mat,
|
||||||
|
size = self.size/self.cells*np.asarray(mat.shape),
|
||||||
|
origin = self.origin,
|
||||||
|
comments = self.comments+[util.execution_stamp('Grid','mirror')],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def flip(self,
|
||||||
|
directions: Sequence[str]) -> 'Grid':
|
||||||
|
"""
|
||||||
|
Flip grid along given directions.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
directions : (sequence of) {'x', 'y', 'z'}
|
||||||
|
Direction(s) along which the grid is flipped.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
updated : damask.Grid
|
||||||
|
Updated grid-based geometry.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if not set(directions).issubset(valid := ['x', 'y', 'z']):
|
||||||
|
raise ValueError(f'invalid direction "{set(directions).difference(valid)}" specified')
|
||||||
|
|
||||||
|
|
||||||
|
mat = np.flip(self.material, [valid.index(d) for d in directions if d in valid])
|
||||||
|
|
||||||
|
return Grid(material = mat,
|
||||||
|
size = self.size,
|
||||||
|
origin = self.origin,
|
||||||
|
comments = self.comments+[util.execution_stamp('Grid','flip')],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def rotate(self,
|
||||||
|
R: Rotation,
|
||||||
|
fill: int = None) -> 'Grid':
|
||||||
|
"""
|
||||||
|
Rotate grid (and pad if required).
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
R : damask.Rotation
|
||||||
|
Rotation to apply to the grid.
|
||||||
|
fill : int, optional
|
||||||
|
Material ID to fill enlarged bounding box.
|
||||||
|
Defaults to material.max() + 1.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
updated : damask.Grid
|
||||||
|
Updated grid-based geometry.
|
||||||
|
|
||||||
|
"""
|
||||||
|
material = self.material
|
||||||
|
# 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
|
||||||
|
for angle,axes in zip(R.as_Euler_angles(degrees=True)[::-1], [(0,1),(1,2),(0,1)]):
|
||||||
|
material_temp = ndimage.rotate(material,angle,axes,order=0,prefilter=False,
|
||||||
|
output=self.material.dtype,
|
||||||
|
cval=np.nanmax(self.material) + 1 if fill is None else fill)
|
||||||
|
# avoid scipy interpolation errors for rotations close to multiples of 90°
|
||||||
|
material = material_temp if np.prod(material_temp.shape) != np.prod(material.shape) else \
|
||||||
|
np.rot90(material,k=np.rint(angle/90.).astype(int),axes=axes)
|
||||||
|
|
||||||
|
origin = self.origin-(np.asarray(material.shape)-self.cells)*.5 * self.size/self.cells
|
||||||
|
|
||||||
|
return Grid(material = material,
|
||||||
|
size = self.size/self.cells*np.asarray(material.shape),
|
||||||
|
origin = origin,
|
||||||
|
comments = self.comments+[util.execution_stamp('Grid','rotate')],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def scale(self,
|
||||||
|
cells: IntSequence,
|
||||||
|
periodic: bool = True) -> 'Grid':
|
||||||
|
"""
|
||||||
|
Scale grid to new cells.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
cells : sequence of int, len (3)
|
||||||
|
Number of cells in x,y,z direction.
|
||||||
|
periodic : bool, optional
|
||||||
|
Assume grid to be periodic. Defaults to True.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
updated : damask.Grid
|
||||||
|
Updated grid-based geometry.
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
Double resolution.
|
||||||
|
|
||||||
|
>>> import numpy as np
|
||||||
|
>>> import damask
|
||||||
|
>>> g = damask.Grid(np.zeros([32]*3,int),np.ones(3)*1e-4)
|
||||||
|
>>> g.scale(g.cells*2)
|
||||||
|
cells : 64 x 64 x 64
|
||||||
|
size : 0.0001 x 0.0001 x 0.0001 m³
|
||||||
|
origin: 0.0 0.0 0.0 m
|
||||||
|
# materials: 1
|
||||||
|
|
||||||
|
"""
|
||||||
|
return Grid(material = ndimage.interpolation.zoom(
|
||||||
|
self.material,
|
||||||
|
cells/self.cells,
|
||||||
|
output=self.material.dtype,
|
||||||
|
order=0,
|
||||||
|
mode='wrap' if periodic else 'nearest',
|
||||||
|
prefilter=False
|
||||||
|
),
|
||||||
|
size = self.size,
|
||||||
|
origin = self.origin,
|
||||||
|
comments = self.comments+[util.execution_stamp('Grid','scale')],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def renumber(self) -> 'Grid':
|
||||||
|
"""
|
||||||
|
Renumber sorted material indices as 0,...,N-1.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
updated : damask.Grid
|
||||||
|
Updated grid-based geometry.
|
||||||
|
|
||||||
|
"""
|
||||||
|
_,renumbered = np.unique(self.material,return_inverse=True)
|
||||||
|
|
||||||
|
return Grid(material = renumbered.reshape(self.cells),
|
||||||
|
size = self.size,
|
||||||
|
origin = self.origin,
|
||||||
|
comments = self.comments+[util.execution_stamp('Grid','renumber')],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def substitute(self,
|
||||||
|
from_material: Union[int,IntSequence],
|
||||||
|
to_material: Union[int,IntSequence]) -> 'Grid':
|
||||||
|
"""
|
||||||
|
Substitute material indices.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
from_material : int or sequence of int
|
||||||
|
Material indices to be substituted.
|
||||||
|
to_material : int or sequence of int
|
||||||
|
New material indices.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
updated : damask.Grid
|
||||||
|
Updated grid-based geometry.
|
||||||
|
|
||||||
|
"""
|
||||||
|
material = self.material.copy()
|
||||||
|
for f,t in zip(from_material if isinstance(from_material,(Sequence,np.ndarray)) else [from_material],
|
||||||
|
to_material if isinstance(to_material,(Sequence,np.ndarray)) else [to_material]): # ToDo Python 3.10 has strict mode for zip
|
||||||
|
material[self.material==f] = t
|
||||||
|
|
||||||
|
return Grid(material = material,
|
||||||
|
size = self.size,
|
||||||
|
origin = self.origin,
|
||||||
|
comments = self.comments+[util.execution_stamp('Grid','substitute')],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def sort(self) -> 'Grid':
|
||||||
|
"""
|
||||||
|
Sort material indices such that min(material) is located at (0,0,0).
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
updated : damask.Grid
|
||||||
|
Updated grid-based geometry.
|
||||||
|
|
||||||
|
"""
|
||||||
|
a = self.material.flatten(order='F')
|
||||||
|
from_ma = pd.unique(a)
|
||||||
|
sort_idx = np.argsort(from_ma)
|
||||||
|
ma = np.unique(a)[sort_idx][np.searchsorted(from_ma,a,sorter = sort_idx)]
|
||||||
|
|
||||||
|
return Grid(material = ma.reshape(self.cells,order='F'),
|
||||||
|
size = self.size,
|
||||||
|
origin = self.origin,
|
||||||
|
comments = self.comments+[util.execution_stamp('Grid','sort')],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def clean(self,
|
||||||
|
distance: float = np.sqrt(3),
|
||||||
|
selection: IntCollection = None,
|
||||||
|
invert_selection: bool = False,
|
||||||
|
periodic: bool = True,
|
||||||
|
rng_seed: NumpyRngSeed = None) -> 'Grid':
|
||||||
|
"""
|
||||||
|
Smooth grid by selecting most frequent material ID within given stencil at each location.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
distance : float, optional
|
||||||
|
Voxel distance checked for presence of other materials.
|
||||||
|
Defaults to sqrt(3).
|
||||||
|
selection : int or collection of int, optional
|
||||||
|
Material IDs to consider. Defaults to all.
|
||||||
|
invert_selection : bool, optional
|
||||||
|
Consider all material IDs except those in selection. Defaults to False.
|
||||||
|
periodic : bool, optional
|
||||||
|
Assume grid to be periodic. Defaults to True.
|
||||||
|
rng_seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional
|
||||||
|
A seed to initialize the BitGenerator. Defaults to None.
|
||||||
|
If None, then fresh, unpredictable entropy will be pulled from the OS.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
updated : damask.Grid
|
||||||
|
Updated grid-based geometry.
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
If multiple material IDs are most frequent within a stencil, a random choice is taken.
|
||||||
|
|
||||||
|
"""
|
||||||
|
def most_frequent(stencil: np.ndarray,
|
||||||
|
selection: Union[None,set],
|
||||||
|
rng):
|
||||||
|
me = stencil[stencil.size//2]
|
||||||
|
if selection is None or me in selection:
|
||||||
|
unique, counts = np.unique(stencil,return_counts=True)
|
||||||
|
return rng.choice(unique[counts==np.max(counts)])
|
||||||
|
else:
|
||||||
|
return me
|
||||||
|
|
||||||
|
rng = np.random.default_rng(rng_seed)
|
||||||
|
|
||||||
|
d = np.floor(distance).astype(int)
|
||||||
|
ext = np.linspace(-d,d,1+2*d,dtype=float),
|
||||||
|
xx,yy,zz = np.meshgrid(ext,ext,ext)
|
||||||
|
footprint = xx**2+yy**2+zz**2 <= distance**2+distance*1e-8
|
||||||
|
selection_ = None if selection is None else \
|
||||||
|
set(self.material.flatten()) - set(util.aslist(selection)) if invert_selection else \
|
||||||
|
set(self.material.flatten()) & set(util.aslist(selection))
|
||||||
|
material = ndimage.filters.generic_filter(
|
||||||
|
self.material,
|
||||||
|
most_frequent,
|
||||||
|
footprint=footprint,
|
||||||
|
mode='wrap' if periodic else 'nearest',
|
||||||
|
extra_keywords=dict(selection=selection_,rng=rng),
|
||||||
|
).astype(self.material.dtype)
|
||||||
|
return Grid(material = material,
|
||||||
|
size = self.size,
|
||||||
|
origin = self.origin,
|
||||||
|
comments = self.comments+[util.execution_stamp('Grid','clean')],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def add_primitive(self,
|
def add_primitive(self,
|
||||||
dimension: Union[FloatSequence, IntSequence],
|
dimension: Union[FloatSequence, IntSequence],
|
||||||
center: Union[FloatSequence, IntSequence],
|
center: Union[FloatSequence, IntSequence],
|
||||||
|
@ -809,366 +1171,6 @@ class Grid:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def mirror(self,
|
|
||||||
directions: Sequence[str],
|
|
||||||
reflect: bool = False) -> 'Grid':
|
|
||||||
"""
|
|
||||||
Mirror grid along given directions.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
directions : (sequence of) {'x', 'y', 'z'}
|
|
||||||
Direction(s) along which the grid is mirrored.
|
|
||||||
reflect : bool, optional
|
|
||||||
Reflect (include) outermost layers. Defaults to False.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
updated : damask.Grid
|
|
||||||
Updated grid-based geometry.
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
Mirror along x- and y-direction.
|
|
||||||
|
|
||||||
>>> import numpy as np
|
|
||||||
>>> import damask
|
|
||||||
>>> g = damask.Grid(np.zeros([32]*3,int), np.ones(3)*1e-4)
|
|
||||||
>>> g.mirror('xy',True)
|
|
||||||
cells : 64 x 64 x 32
|
|
||||||
size : 0.0002 x 0.0002 x 0.0001 m³
|
|
||||||
origin: 0.0 0.0 0.0 m
|
|
||||||
# materials: 1
|
|
||||||
|
|
||||||
"""
|
|
||||||
if not set(directions).issubset(valid := ['x', 'y', 'z']):
|
|
||||||
raise ValueError(f'invalid direction "{set(directions).difference(valid)}" specified')
|
|
||||||
|
|
||||||
limits: Sequence[Optional[int]] = [None,None] if reflect else [-2,0]
|
|
||||||
mat = self.material.copy()
|
|
||||||
|
|
||||||
if 'x' in directions:
|
|
||||||
mat = np.concatenate([mat,mat[limits[0]:limits[1]:-1,:,:]],0)
|
|
||||||
if 'y' in directions:
|
|
||||||
mat = np.concatenate([mat,mat[:,limits[0]:limits[1]:-1,:]],1)
|
|
||||||
if 'z' in directions:
|
|
||||||
mat = np.concatenate([mat,mat[:,:,limits[0]:limits[1]:-1]],2)
|
|
||||||
|
|
||||||
return Grid(material = mat,
|
|
||||||
size = self.size/self.cells*np.asarray(mat.shape),
|
|
||||||
origin = self.origin,
|
|
||||||
comments = self.comments+[util.execution_stamp('Grid','mirror')],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def flip(self,
|
|
||||||
directions: Sequence[str]) -> 'Grid':
|
|
||||||
"""
|
|
||||||
Flip grid along given directions.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
directions : (sequence of) {'x', 'y', 'z'}
|
|
||||||
Direction(s) along which the grid is flipped.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
updated : damask.Grid
|
|
||||||
Updated grid-based geometry.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if not set(directions).issubset(valid := ['x', 'y', 'z']):
|
|
||||||
raise ValueError(f'invalid direction "{set(directions).difference(valid)}" specified')
|
|
||||||
|
|
||||||
|
|
||||||
mat = np.flip(self.material, [valid.index(d) for d in directions if d in valid])
|
|
||||||
|
|
||||||
return Grid(material = mat,
|
|
||||||
size = self.size,
|
|
||||||
origin = self.origin,
|
|
||||||
comments = self.comments+[util.execution_stamp('Grid','flip')],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def scale(self,
|
|
||||||
cells: IntSequence,
|
|
||||||
periodic: bool = True) -> 'Grid':
|
|
||||||
"""
|
|
||||||
Scale grid to new cells.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
cells : sequence of int, len (3)
|
|
||||||
Number of cells in x,y,z direction.
|
|
||||||
periodic : bool, optional
|
|
||||||
Assume grid to be periodic. Defaults to True.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
updated : damask.Grid
|
|
||||||
Updated grid-based geometry.
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
Double resolution.
|
|
||||||
|
|
||||||
>>> import numpy as np
|
|
||||||
>>> import damask
|
|
||||||
>>> g = damask.Grid(np.zeros([32]*3,int),np.ones(3)*1e-4)
|
|
||||||
>>> g.scale(g.cells*2)
|
|
||||||
cells : 64 x 64 x 64
|
|
||||||
size : 0.0001 x 0.0001 x 0.0001 m³
|
|
||||||
origin: 0.0 0.0 0.0 m
|
|
||||||
# materials: 1
|
|
||||||
|
|
||||||
"""
|
|
||||||
return Grid(material = ndimage.interpolation.zoom(
|
|
||||||
self.material,
|
|
||||||
cells/self.cells,
|
|
||||||
output=self.material.dtype,
|
|
||||||
order=0,
|
|
||||||
mode='wrap' if periodic else 'nearest',
|
|
||||||
prefilter=False
|
|
||||||
),
|
|
||||||
size = self.size,
|
|
||||||
origin = self.origin,
|
|
||||||
comments = self.comments+[util.execution_stamp('Grid','scale')],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def clean(self,
|
|
||||||
distance: float = np.sqrt(3),
|
|
||||||
selection: IntCollection = None,
|
|
||||||
invert_selection: bool = False,
|
|
||||||
periodic: bool = True,
|
|
||||||
rng_seed: NumpyRngSeed = None) -> 'Grid':
|
|
||||||
"""
|
|
||||||
Smooth grid by selecting most frequent material ID within given stencil at each location.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
distance : float, optional
|
|
||||||
Voxel distance checked for presence of other materials.
|
|
||||||
Defaults to sqrt(3).
|
|
||||||
selection : int or collection of int, optional
|
|
||||||
Material IDs to consider.
|
|
||||||
invert_selection : bool, optional
|
|
||||||
Consider all material IDs except those in selection. Defaults to False.
|
|
||||||
periodic : bool, optional
|
|
||||||
Assume grid to be periodic. Defaults to True.
|
|
||||||
rng_seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional
|
|
||||||
A seed to initialize the BitGenerator. Defaults to None.
|
|
||||||
If None, then fresh, unpredictable entropy will be pulled from the OS.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
updated : damask.Grid
|
|
||||||
Updated grid-based geometry.
|
|
||||||
|
|
||||||
Notes
|
|
||||||
-----
|
|
||||||
If multiple material IDs are most frequent within a stencil, a random choice is taken.
|
|
||||||
|
|
||||||
"""
|
|
||||||
def most_frequent(stencil: np.ndarray,
|
|
||||||
selection: set,
|
|
||||||
rng):
|
|
||||||
me = stencil[stencil.size//2]
|
|
||||||
if not selection or me in selection:
|
|
||||||
unique, counts = np.unique(stencil,return_counts=True)
|
|
||||||
return rng.choice(unique[counts==np.max(counts)])
|
|
||||||
else:
|
|
||||||
return me
|
|
||||||
|
|
||||||
rng = np.random.default_rng(rng_seed)
|
|
||||||
d = np.floor(distance).astype(int)
|
|
||||||
ext = np.linspace(-d,d,1+2*d,dtype=float),
|
|
||||||
xx,yy,zz = np.meshgrid(ext,ext,ext)
|
|
||||||
footprint = xx**2+yy**2+zz**2 <= distance**2+distance*1e-8
|
|
||||||
selection_ = set(self.material.flatten()) - set(util.aslist(selection)) if invert_selection else \
|
|
||||||
set(util.aslist(selection))
|
|
||||||
material = ndimage.filters.generic_filter(
|
|
||||||
self.material,
|
|
||||||
most_frequent,
|
|
||||||
footprint=footprint,
|
|
||||||
mode='wrap' if periodic else 'nearest',
|
|
||||||
extra_keywords=dict(selection=selection_,rng=rng),
|
|
||||||
).astype(self.material.dtype)
|
|
||||||
return Grid(material = material,
|
|
||||||
size = self.size,
|
|
||||||
origin = self.origin,
|
|
||||||
comments = self.comments+[util.execution_stamp('Grid','clean')],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def renumber(self) -> 'Grid':
|
|
||||||
"""
|
|
||||||
Renumber sorted material indices as 0,...,N-1.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
updated : damask.Grid
|
|
||||||
Updated grid-based geometry.
|
|
||||||
|
|
||||||
"""
|
|
||||||
_,renumbered = np.unique(self.material,return_inverse=True)
|
|
||||||
|
|
||||||
return Grid(material = renumbered.reshape(self.cells),
|
|
||||||
size = self.size,
|
|
||||||
origin = self.origin,
|
|
||||||
comments = self.comments+[util.execution_stamp('Grid','renumber')],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def rotate(self,
|
|
||||||
R: Rotation,
|
|
||||||
fill: int = None) -> 'Grid':
|
|
||||||
"""
|
|
||||||
Rotate grid (and pad if required).
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
R : damask.Rotation
|
|
||||||
Rotation to apply to the grid.
|
|
||||||
fill : int, optional
|
|
||||||
Material ID to fill enlarged bounding box.
|
|
||||||
Defaults to material.max() + 1.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
updated : damask.Grid
|
|
||||||
Updated grid-based geometry.
|
|
||||||
|
|
||||||
"""
|
|
||||||
material = self.material
|
|
||||||
# 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
|
|
||||||
for angle,axes in zip(R.as_Euler_angles(degrees=True)[::-1], [(0,1),(1,2),(0,1)]):
|
|
||||||
material_temp = ndimage.rotate(material,angle,axes,order=0,prefilter=False,
|
|
||||||
output=self.material.dtype,
|
|
||||||
cval=np.nanmax(self.material) + 1 if fill is None else fill)
|
|
||||||
# avoid scipy interpolation errors for rotations close to multiples of 90°
|
|
||||||
material = material_temp if np.prod(material_temp.shape) != np.prod(material.shape) else \
|
|
||||||
np.rot90(material,k=np.rint(angle/90.).astype(int),axes=axes)
|
|
||||||
|
|
||||||
origin = self.origin-(np.asarray(material.shape)-self.cells)*.5 * self.size/self.cells
|
|
||||||
|
|
||||||
return Grid(material = material,
|
|
||||||
size = self.size/self.cells*np.asarray(material.shape),
|
|
||||||
origin = origin,
|
|
||||||
comments = self.comments+[util.execution_stamp('Grid','rotate')],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def canvas(self,
|
|
||||||
cells: IntSequence = None,
|
|
||||||
offset: IntSequence = None,
|
|
||||||
fill: int = None) -> 'Grid':
|
|
||||||
"""
|
|
||||||
Crop or enlarge/pad grid.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
cells : sequence of int, len (3), optional
|
|
||||||
Number of cells x,y,z direction.
|
|
||||||
offset : sequence of int, len (3), optional
|
|
||||||
Offset (measured in cells) from old to new grid.
|
|
||||||
Defaults to [0,0,0].
|
|
||||||
fill : int, optional
|
|
||||||
Material ID to fill the background.
|
|
||||||
Defaults to material.max() + 1.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
updated : damask.Grid
|
|
||||||
Updated grid-based geometry.
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
Remove lower 1/2 of the microstructure in z-direction.
|
|
||||||
|
|
||||||
>>> import numpy as np
|
|
||||||
>>> import damask
|
|
||||||
>>> g = damask.Grid(np.zeros([32]*3,int),np.ones(3)*1e-4)
|
|
||||||
>>> g.canvas([32,32,16],[0,0,16])
|
|
||||||
cells : 33 x 32 x 16
|
|
||||||
size : 0.0001 x 0.0001 x 5e-05 m³
|
|
||||||
origin: 0.0 0.0 5e-05 m
|
|
||||||
# materials: 1
|
|
||||||
|
|
||||||
"""
|
|
||||||
offset_ = np.array(offset,int) if offset is not None else np.zeros(3,int)
|
|
||||||
cells_ = np.array(cells,int) if cells is not None else self.cells
|
|
||||||
|
|
||||||
canvas = np.full(cells_,np.nanmax(self.material) + 1 if fill is None else fill,self.material.dtype)
|
|
||||||
|
|
||||||
LL = np.clip( offset_, 0,np.minimum(self.cells, cells_+offset_))
|
|
||||||
UR = np.clip( offset_+cells_, 0,np.minimum(self.cells, cells_+offset_))
|
|
||||||
ll = np.clip(-offset_, 0,np.minimum( cells_,self.cells-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]]
|
|
||||||
|
|
||||||
return Grid(material = canvas,
|
|
||||||
size = self.size/self.cells*np.asarray(canvas.shape),
|
|
||||||
origin = self.origin+offset_*self.size/self.cells,
|
|
||||||
comments = self.comments+[util.execution_stamp('Grid','canvas')],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def substitute(self,
|
|
||||||
from_material: Union[int,IntSequence],
|
|
||||||
to_material: Union[int,IntSequence]) -> 'Grid':
|
|
||||||
"""
|
|
||||||
Substitute material indices.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
from_material : int or sequence of int
|
|
||||||
Material indices to be substituted.
|
|
||||||
to_material : int or sequence of int
|
|
||||||
New material indices.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
updated : damask.Grid
|
|
||||||
Updated grid-based geometry.
|
|
||||||
|
|
||||||
"""
|
|
||||||
material = self.material.copy()
|
|
||||||
for f,t in zip(from_material if isinstance(from_material,(Sequence,np.ndarray)) else [from_material],
|
|
||||||
to_material if isinstance(to_material,(Sequence,np.ndarray)) else [to_material]): # ToDo Python 3.10 has strict mode for zip
|
|
||||||
material[self.material==f] = t
|
|
||||||
|
|
||||||
return Grid(material = material,
|
|
||||||
size = self.size,
|
|
||||||
origin = self.origin,
|
|
||||||
comments = self.comments+[util.execution_stamp('Grid','substitute')],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def sort(self) -> 'Grid':
|
|
||||||
"""
|
|
||||||
Sort material indices such that min(material) is located at (0,0,0).
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
updated : damask.Grid
|
|
||||||
Updated grid-based geometry.
|
|
||||||
|
|
||||||
"""
|
|
||||||
a = self.material.flatten(order='F')
|
|
||||||
from_ma = pd.unique(a)
|
|
||||||
sort_idx = np.argsort(from_ma)
|
|
||||||
ma = np.unique(a)[sort_idx][np.searchsorted(from_ma,a,sorter = sort_idx)]
|
|
||||||
|
|
||||||
return Grid(material = ma.reshape(self.cells,order='F'),
|
|
||||||
size = self.size,
|
|
||||||
origin = self.origin,
|
|
||||||
comments = self.comments+[util.execution_stamp('Grid','sort')],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def vicinity_offset(self,
|
def vicinity_offset(self,
|
||||||
distance: float = np.sqrt(3),
|
distance: float = np.sqrt(3),
|
||||||
offset: int = None,
|
offset: int = None,
|
||||||
|
@ -1204,9 +1206,9 @@ class Grid:
|
||||||
Updated grid-based geometry.
|
Updated grid-based geometry.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def tainted_neighborhood(stencil: np.ndarray, selection: set):
|
def tainted_neighborhood(stencil: np.ndarray, selection: Union[None,set]):
|
||||||
me = stencil[stencil.size//2]
|
me = stencil[stencil.size//2]
|
||||||
return np.any(stencil != me if not selection else
|
return np.any(stencil != me if selection is None else
|
||||||
np.in1d(stencil,np.array(list(selection - {me}))))
|
np.in1d(stencil,np.array(list(selection - {me}))))
|
||||||
|
|
||||||
d = np.floor(distance).astype(int)
|
d = np.floor(distance).astype(int)
|
||||||
|
@ -1214,8 +1216,9 @@ class Grid:
|
||||||
xx,yy,zz = np.meshgrid(ext,ext,ext)
|
xx,yy,zz = np.meshgrid(ext,ext,ext)
|
||||||
footprint = xx**2+yy**2+zz**2 <= distance**2+distance*1e-8
|
footprint = xx**2+yy**2+zz**2 <= distance**2+distance*1e-8
|
||||||
offset_ = np.nanmax(self.material)+1 if offset is None else offset
|
offset_ = np.nanmax(self.material)+1 if offset is None else offset
|
||||||
selection_ = set(self.material.flatten()) - set(util.aslist(selection)) if invert_selection else \
|
selection_ = None if selection is None else \
|
||||||
set(util.aslist(selection))
|
set(self.material.flatten()) - set(util.aslist(selection)) if invert_selection else \
|
||||||
|
set(self.material.flatten()) & set(util.aslist(selection))
|
||||||
mask = ndimage.filters.generic_filter(self.material,
|
mask = ndimage.filters.generic_filter(self.material,
|
||||||
tainted_neighborhood,
|
tainted_neighborhood,
|
||||||
footprint=footprint,
|
footprint=footprint,
|
||||||
|
|
|
@ -1621,7 +1621,7 @@ class Result:
|
||||||
for inc in util.show_progress(self.visible['increments']):
|
for inc in util.show_progress(self.visible['increments']):
|
||||||
|
|
||||||
u = _read(f['/'.join([inc,'geometry','u_n' if mode.lower() == 'cell' else 'u_p'])])
|
u = _read(f['/'.join([inc,'geometry','u_n' if mode.lower() == 'cell' else 'u_p'])])
|
||||||
v = v.add(u,'u')
|
v = v.add('u',u)
|
||||||
|
|
||||||
for ty in ['phase','homogenization']:
|
for ty in ['phase','homogenization']:
|
||||||
for field in self.visible['fields']:
|
for field in self.visible['fields']:
|
||||||
|
@ -1648,7 +1648,7 @@ class Result:
|
||||||
outs[out][at_cell_ho[label]] = data[in_data_ho[label]]
|
outs[out][at_cell_ho[label]] = data[in_data_ho[label]]
|
||||||
|
|
||||||
for label,dataset in outs.items():
|
for label,dataset in outs.items():
|
||||||
v = v.add(dataset,' / '.join(['/'.join([ty,field,label]),dataset.dtype.metadata['unit']]))
|
v = v.add(' / '.join(['/'.join([ty,field,label]),dataset.dtype.metadata['unit']]),dataset)
|
||||||
|
|
||||||
v.save(f'{self.fname.stem}_inc{inc[10:].zfill(N_digits)}',parallel=parallel)
|
v.save(f'{self.fname.stem}_inc{inc[10:].zfill(N_digits)}',parallel=parallel)
|
||||||
|
|
||||||
|
|
|
@ -13,26 +13,27 @@ class Table:
|
||||||
"""Manipulate multi-dimensional spreadsheet-like data."""
|
"""Manipulate multi-dimensional spreadsheet-like data."""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
data: np.ndarray,
|
|
||||||
shapes: dict,
|
shapes: dict,
|
||||||
|
data: np.ndarray,
|
||||||
comments: Union[str, list] = None):
|
comments: Union[str, list] = None):
|
||||||
"""
|
"""
|
||||||
New spreadsheet.
|
New spreadsheet.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
data : numpy.ndarray or pandas.DataFrame
|
|
||||||
Data. Column labels from a pandas.DataFrame will be replaced.
|
|
||||||
shapes : dict with str:tuple pairs
|
shapes : dict with str:tuple pairs
|
||||||
Shapes of the columns. Example 'F':(3,3) for a deformation gradient.
|
Shapes of the data columns.
|
||||||
|
For instance, 'F':(3,3) for a deformation gradient, or 'r':(1,) for a scalar.
|
||||||
|
data : numpy.ndarray or pandas.DataFrame
|
||||||
|
Data. Existing column labels of a pandas.DataFrame will be replaced.
|
||||||
comments : str or iterable of str, optional
|
comments : str or iterable of str, optional
|
||||||
Additional, human-readable information.
|
Additional, human-readable information.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
comments_ = [comments] if isinstance(comments,str) else comments
|
comments_ = [comments] if isinstance(comments,str) else comments
|
||||||
self.comments = [] if comments_ is None else [c for c in comments_]
|
self.comments = [] if comments_ is None else [c for c in comments_]
|
||||||
self.data = pd.DataFrame(data=data)
|
|
||||||
self.shapes = { k:(v,) if isinstance(v,(np.int64,np.int32,int)) else v for k,v in shapes.items() }
|
self.shapes = { k:(v,) if isinstance(v,(np.int64,np.int32,int)) else v for k,v in shapes.items() }
|
||||||
|
self.data = pd.DataFrame(data=data)
|
||||||
self._relabel('uniform')
|
self._relabel('uniform')
|
||||||
|
|
||||||
|
|
||||||
|
@ -70,8 +71,8 @@ class Table:
|
||||||
--------
|
--------
|
||||||
>>> import damask
|
>>> import damask
|
||||||
>>> import numpy as np
|
>>> import numpy as np
|
||||||
>>> tbl = damask.Table(data=np.arange(12).reshape((4,3)),
|
>>> tbl = damask.Table(shapes=dict(colA=(1,),colB=(1,),colC=(1,)),
|
||||||
... shapes=dict(colA=(1,),colB=(1,),colC=(1,)))
|
... data=np.arange(12).reshape((4,3)))
|
||||||
>>> tbl['colA','colB']
|
>>> tbl['colA','colB']
|
||||||
colA colB
|
colA colB
|
||||||
0 0 1
|
0 0 1
|
||||||
|
@ -282,7 +283,7 @@ class Table:
|
||||||
|
|
||||||
data = pd.read_csv(f,names=list(range(len(labels))),sep=r'\s+')
|
data = pd.read_csv(f,names=list(range(len(labels))),sep=r'\s+')
|
||||||
|
|
||||||
return Table(data,shapes,comments)
|
return Table(shapes,data,comments)
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -329,7 +330,7 @@ class Table:
|
||||||
if (remainder := data.shape[1]-sum(shapes.values())) > 0:
|
if (remainder := data.shape[1]-sum(shapes.values())) > 0:
|
||||||
shapes['unknown'] = remainder
|
shapes['unknown'] = remainder
|
||||||
|
|
||||||
return Table(data,shapes,comments)
|
return Table(shapes,data,comments)
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -535,7 +536,7 @@ class Table:
|
||||||
raise KeyError('mismatch of shapes or labels or their order')
|
raise KeyError('mismatch of shapes or labels or their order')
|
||||||
|
|
||||||
dup = self.copy()
|
dup = self.copy()
|
||||||
dup.data = dup.data.append(other.data,ignore_index=True)
|
dup.data = pd.concat([dup.data,other.data],ignore_index=True)
|
||||||
return dup
|
return dup
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -402,24 +402,32 @@ class VTK:
|
||||||
|
|
||||||
# Check https://blog.kitware.com/ghost-and-blanking-visibility-changes/ for missing data
|
# Check https://blog.kitware.com/ghost-and-blanking-visibility-changes/ for missing data
|
||||||
def add(self,
|
def add(self,
|
||||||
data: Union[np.ndarray, np.ma.MaskedArray, 'Table'],
|
label: str = None,
|
||||||
label: str = None):
|
data: Union[np.ndarray, np.ma.MaskedArray] = None,
|
||||||
|
*,
|
||||||
|
table: 'Table' = None):
|
||||||
"""
|
"""
|
||||||
Add data to either cells or points.
|
Add data to either cells or points.
|
||||||
|
|
||||||
|
Data can either be a numpy.array, which requires a corresponding label,
|
||||||
|
or a damask.Table.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
data : numpy.ndarray, numpy.ma.MaskedArray, or damask.Table
|
label : str, optional
|
||||||
|
Label of data array.
|
||||||
|
data : numpy.ndarray or numpy.ma.MaskedArray, optional
|
||||||
Data to add. First dimension needs to match either
|
Data to add. First dimension needs to match either
|
||||||
number of cells or number of points.
|
number of cells or number of points.
|
||||||
label : str, optional if data is damask.Table
|
table: damask.Table, optional
|
||||||
Data label.
|
Data to add. Number of rows needs to match either
|
||||||
|
number of cells or number of points.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _add_array(vtk_data,
|
def _add_array(vtk_data,
|
||||||
data: np.ndarray,
|
label: str,
|
||||||
label: str):
|
data: np.ndarray):
|
||||||
|
|
||||||
N_data = data.shape[0]
|
N_data = data.shape[0]
|
||||||
data_ = data.reshape(N_data,-1) \
|
data_ = data.reshape(N_data,-1) \
|
||||||
|
@ -441,17 +449,22 @@ class VTK:
|
||||||
else:
|
else:
|
||||||
raise ValueError(f'data count mismatch ({N_data} ≠ {self.N_points} & {self.N_cells})')
|
raise ValueError(f'data count mismatch ({N_data} ≠ {self.N_points} & {self.N_cells})')
|
||||||
|
|
||||||
|
if data is None and table is None:
|
||||||
|
raise KeyError('no data given')
|
||||||
|
if data is not None and table is not None:
|
||||||
|
raise KeyError('cannot use both, data and table')
|
||||||
|
|
||||||
dup = self.copy()
|
dup = self.copy()
|
||||||
if isinstance(data,np.ndarray):
|
if isinstance(data,np.ndarray):
|
||||||
if label is not None:
|
if label is not None:
|
||||||
_add_array(dup.vtk_data,
|
_add_array(dup.vtk_data,
|
||||||
np.where(data.mask,data.fill_value,data) if isinstance(data,np.ma.MaskedArray) else data,
|
label,
|
||||||
label)
|
np.where(data.mask,data.fill_value,data) if isinstance(data,np.ma.MaskedArray) else data)
|
||||||
else:
|
else:
|
||||||
raise ValueError('no label defined for numpy.ndarray')
|
raise ValueError('no label defined for data')
|
||||||
elif isinstance(data,Table):
|
elif isinstance(table,Table):
|
||||||
for l in data.labels:
|
for l in table.labels:
|
||||||
_add_array(dup.vtk_data,data.get(l),l)
|
_add_array(dup.vtk_data,l,table.get(l))
|
||||||
else:
|
else:
|
||||||
raise TypeError
|
raise TypeError
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,7 @@ class TestConfigMaterial:
|
||||||
np.ones(N*2),np.zeros(N*2),np.ones(N*2),np.ones(N*2),
|
np.ones(N*2),np.zeros(N*2),np.ones(N*2),np.ones(N*2),
|
||||||
np.ones(N*2),
|
np.ones(N*2),
|
||||||
)).T
|
)).T
|
||||||
t = Table(a,{'varying':1,'constant':4,'ones':1})
|
t = Table({'varying':1,'constant':4,'ones':1},a)
|
||||||
c = ConfigMaterial.from_table(t,**{'phase':'varying','O':'constant','homogenization':'ones'})
|
c = ConfigMaterial.from_table(t,**{'phase':'varying','O':'constant','homogenization':'ones'})
|
||||||
assert len(c['material']) == N
|
assert len(c['material']) == N
|
||||||
for i,m in enumerate(c['material']):
|
for i,m in enumerate(c['material']):
|
||||||
|
@ -102,7 +102,7 @@ class TestConfigMaterial:
|
||||||
np.ones(N*2),np.zeros(N*2),np.ones(N*2),np.ones(N*2),
|
np.ones(N*2),np.zeros(N*2),np.ones(N*2),np.ones(N*2),
|
||||||
np.ones(N*2),
|
np.ones(N*2),
|
||||||
)).T
|
)).T
|
||||||
t = Table(a,{'varying':1,'constant':4,'ones':1})
|
t = Table({'varying':1,'constant':4,'ones':1},a)
|
||||||
c = ConfigMaterial.from_table(t,**{'phase':'varying','O':'constant','homogenization':1})
|
c = ConfigMaterial.from_table(t,**{'phase':'varying','O':'constant','homogenization':1})
|
||||||
assert len(c['material']) == N
|
assert len(c['material']) == N
|
||||||
for i,m in enumerate(c['material']):
|
for i,m in enumerate(c['material']):
|
||||||
|
|
|
@ -12,11 +12,6 @@ from damask import seeds
|
||||||
from damask import grid_filters
|
from damask import grid_filters
|
||||||
|
|
||||||
|
|
||||||
def grid_equal(a,b):
|
|
||||||
return np.all(a.material == b.material) and \
|
|
||||||
np.all(a.cells == b.cells) and \
|
|
||||||
np.allclose(a.size, b.size)
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def default():
|
def default():
|
||||||
"""Simple geometry."""
|
"""Simple geometry."""
|
||||||
|
@ -63,10 +58,10 @@ class TestGrid:
|
||||||
def test_repr(self,default):
|
def test_repr(self,default):
|
||||||
print(default)
|
print(default)
|
||||||
|
|
||||||
def test_read_write_vtr(self,default,tmp_path):
|
def test_read_write_vti(self,default,tmp_path):
|
||||||
default.save(tmp_path/'default')
|
default.save(tmp_path/'default')
|
||||||
new = Grid.load(tmp_path/'default.vti')
|
new = Grid.load(tmp_path/'default.vti')
|
||||||
assert grid_equal(new,default)
|
assert new == default
|
||||||
|
|
||||||
def test_invalid_no_material(self,tmp_path):
|
def test_invalid_no_material(self,tmp_path):
|
||||||
v = VTK.from_image_data(np.random.randint(5,10,3)*2,np.random.random(3) + 1.0)
|
v = VTK.from_image_data(np.random.randint(5,10,3)*2,np.random.random(3) + 1.0)
|
||||||
|
@ -90,7 +85,7 @@ class TestGrid:
|
||||||
def test_save_load_ASCII(self,default,tmp_path):
|
def test_save_load_ASCII(self,default,tmp_path):
|
||||||
default.save_ASCII(tmp_path/'ASCII')
|
default.save_ASCII(tmp_path/'ASCII')
|
||||||
default.material -= 1
|
default.material -= 1
|
||||||
assert grid_equal(Grid.load_ASCII(tmp_path/'ASCII'),default)
|
assert Grid.load_ASCII(tmp_path/'ASCII') == default
|
||||||
|
|
||||||
def test_invalid_origin(self,default):
|
def test_invalid_origin(self,default):
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
|
@ -124,8 +119,7 @@ class TestGrid:
|
||||||
tag = f'directions_{"-".join(directions)}+reflect_{reflect}'
|
tag = f'directions_{"-".join(directions)}+reflect_{reflect}'
|
||||||
reference = ref_path/f'mirror_{tag}.vti'
|
reference = ref_path/f'mirror_{tag}.vti'
|
||||||
if update: modified.save(reference)
|
if update: modified.save(reference)
|
||||||
assert grid_equal(Grid.load(reference),
|
assert Grid.load(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]])
|
||||||
|
@ -146,17 +140,16 @@ class TestGrid:
|
||||||
tag = f'directions_{"-".join(directions)}'
|
tag = f'directions_{"-".join(directions)}'
|
||||||
reference = ref_path/f'flip_{tag}.vti'
|
reference = ref_path/f'flip_{tag}.vti'
|
||||||
if update: modified.save(reference)
|
if update: modified.save(reference)
|
||||||
assert grid_equal(Grid.load(reference),
|
assert Grid.load(reference) == modified
|
||||||
modified)
|
|
||||||
|
|
||||||
|
|
||||||
def test_flip_invariant(self,default):
|
def test_flip_invariant(self,default):
|
||||||
assert grid_equal(default,default.flip([]))
|
assert 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 grid_equal(default,default.flip(direction).flip(direction))
|
assert 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]])
|
||||||
|
@ -173,7 +166,7 @@ class TestGrid:
|
||||||
reference = ref_path/f'clean_{distance}_{"+".join(map(str,util.aslist(selection)))}_{periodic}.vti'
|
reference = ref_path/f'clean_{distance}_{"+".join(map(str,util.aslist(selection)))}_{periodic}.vti'
|
||||||
if update:
|
if update:
|
||||||
current.save(reference)
|
current.save(reference)
|
||||||
assert grid_equal(Grid.load(reference),current)
|
assert Grid.load(reference) == current
|
||||||
|
|
||||||
@pytest.mark.parametrize('selection',[list(np.random.randint(1,20,6)),set(np.random.randint(1,20,6)),np.random.randint(1,20,6)])
|
@pytest.mark.parametrize('selection',[list(np.random.randint(1,20,6)),set(np.random.randint(1,20,6)),np.random.randint(1,20,6)])
|
||||||
@pytest.mark.parametrize('invert',[True,False])
|
@pytest.mark.parametrize('invert',[True,False])
|
||||||
|
@ -201,8 +194,7 @@ class TestGrid:
|
||||||
tag = f'grid_{util.srepr(cells,"-")}'
|
tag = f'grid_{util.srepr(cells,"-")}'
|
||||||
reference = ref_path/f'scale_{tag}.vti'
|
reference = ref_path/f'scale_{tag}.vti'
|
||||||
if update: modified.save(reference)
|
if update: modified.save(reference)
|
||||||
assert grid_equal(Grid.load(reference),
|
assert Grid.load(reference) == modified
|
||||||
modified)
|
|
||||||
|
|
||||||
|
|
||||||
def test_renumber(self,default):
|
def test_renumber(self,default):
|
||||||
|
@ -213,9 +205,8 @@ class TestGrid:
|
||||||
modified = Grid(material,
|
modified = Grid(material,
|
||||||
default.size,
|
default.size,
|
||||||
default.origin)
|
default.origin)
|
||||||
assert not grid_equal(modified,default)
|
assert not default == modified
|
||||||
assert grid_equal(default,
|
assert default == modified.renumber()
|
||||||
modified.renumber())
|
|
||||||
|
|
||||||
|
|
||||||
def test_substitute(self,default):
|
def test_substitute(self,default):
|
||||||
|
@ -223,10 +214,9 @@ class TestGrid:
|
||||||
modified = Grid(default.material + offset,
|
modified = Grid(default.material + offset,
|
||||||
default.size,
|
default.size,
|
||||||
default.origin)
|
default.origin)
|
||||||
assert not grid_equal(modified,default)
|
assert not default == modified
|
||||||
assert grid_equal(default,
|
assert default == modified.substitute(np.arange(default.material.max())+1+offset,
|
||||||
modified.substitute(np.arange(default.material.max())+1+offset,
|
np.arange(default.material.max())+1)
|
||||||
np.arange(default.material.max())+1))
|
|
||||||
|
|
||||||
def test_substitute_integer_list(self,random):
|
def test_substitute_integer_list(self,random):
|
||||||
f = np.random.randint(30)
|
f = np.random.randint(30)
|
||||||
|
@ -237,8 +227,8 @@ class TestGrid:
|
||||||
f = np.unique(default.material.flatten())[:np.random.randint(1,default.material.max())]
|
f = np.unique(default.material.flatten())[:np.random.randint(1,default.material.max())]
|
||||||
t = np.random.permutation(f)
|
t = np.random.permutation(f)
|
||||||
modified = default.substitute(f,t)
|
modified = default.substitute(f,t)
|
||||||
assert np.array_equiv(t,f) or (not grid_equal(modified,default))
|
assert np.array_equiv(t,f) or modified != default
|
||||||
assert grid_equal(default, modified.substitute(t,f))
|
assert default == modified.substitute(t,f)
|
||||||
|
|
||||||
def test_sort(self):
|
def test_sort(self):
|
||||||
cells = np.random.randint(5,20,3)
|
cells = np.random.randint(5,20,3)
|
||||||
|
@ -252,7 +242,7 @@ class TestGrid:
|
||||||
modified = default.copy()
|
modified = default.copy()
|
||||||
for i in range(np.rint(360/axis_angle[3]).astype(int)):
|
for i in range(np.rint(360/axis_angle[3]).astype(int)):
|
||||||
modified.rotate(Rotation.from_axis_angle(axis_angle,degrees=True))
|
modified.rotate(Rotation.from_axis_angle(axis_angle,degrees=True))
|
||||||
assert grid_equal(default,modified)
|
assert default == modified
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('Eulers',[[32.0,68.0,21.0],
|
@pytest.mark.parametrize('Eulers',[[32.0,68.0,21.0],
|
||||||
|
@ -262,8 +252,7 @@ class TestGrid:
|
||||||
tag = f'Eulers_{util.srepr(Eulers,"-")}'
|
tag = f'Eulers_{util.srepr(Eulers,"-")}'
|
||||||
reference = ref_path/f'rotate_{tag}.vti'
|
reference = ref_path/f'rotate_{tag}.vti'
|
||||||
if update: modified.save(reference)
|
if update: modified.save(reference)
|
||||||
assert grid_equal(Grid.load(reference),
|
assert Grid.load(reference) == modified
|
||||||
modified)
|
|
||||||
|
|
||||||
|
|
||||||
def test_canvas_extend(self,default):
|
def test_canvas_extend(self,default):
|
||||||
|
@ -321,7 +310,7 @@ class TestGrid:
|
||||||
fill = np.random.randint(10)+2
|
fill = np.random.randint(10)+2
|
||||||
G_1 = Grid(np.ones(g,'i'),s).add_primitive(.3,center,1,fill,inverse=inverse,periodic=periodic)
|
G_1 = Grid(np.ones(g,'i'),s).add_primitive(.3,center,1,fill,inverse=inverse,periodic=periodic)
|
||||||
G_2 = Grid(np.ones(g,'i'),s).add_primitive(.3,center,1,fill,Rotation.from_random(),inverse,periodic=periodic)
|
G_2 = Grid(np.ones(g,'i'),s).add_primitive(.3,center,1,fill,Rotation.from_random(),inverse,periodic=periodic)
|
||||||
assert grid_equal(G_1,G_2)
|
assert G_1 == G_2
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('selection',[1,None])
|
@pytest.mark.parametrize('selection',[1,None])
|
||||||
|
@ -346,11 +335,10 @@ class TestGrid:
|
||||||
|
|
||||||
@pytest.mark.parametrize('selection',[list(np.random.randint(1,20,6)),set(np.random.randint(1,20,6)),np.random.randint(1,20,6)])
|
@pytest.mark.parametrize('selection',[list(np.random.randint(1,20,6)),set(np.random.randint(1,20,6)),np.random.randint(1,20,6)])
|
||||||
@pytest.mark.parametrize('invert',[True,False])
|
@pytest.mark.parametrize('invert',[True,False])
|
||||||
def test_vicinit_offset_invert(self,random,selection,invert):
|
def test_vicinity_offset_invert(self,random,selection,invert):
|
||||||
selection_inverse = set(random.material.flatten()) - set(selection)
|
selection_inverse = set(random.material.flatten()) - set(selection)
|
||||||
assert selection_inverse == set() or \
|
assert random.vicinity_offset(selection=selection ,invert_selection=not invert) == \
|
||||||
(random.vicinity_offset(selection=selection,invert_selection=invert) ==
|
random.vicinity_offset(selection=selection_inverse,invert_selection= invert)
|
||||||
random.vicinity_offset(selection=selection_inverse,invert_selection=not invert))
|
|
||||||
|
|
||||||
def test_vicinity_offset_selection_empty(self,random):
|
def test_vicinity_offset_selection_empty(self,random):
|
||||||
assert random.vicinity_offset(selection=None,invert_selection=False) == random.vicinity_offset() and \
|
assert random.vicinity_offset(selection=None,invert_selection=False) == random.vicinity_offset() and \
|
||||||
|
@ -372,7 +360,7 @@ class TestGrid:
|
||||||
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 = Grid.from_Voronoi_tessellation( cells,size,seeds, np.arange(N_seeds)+5,periodic)
|
Voronoi = Grid.from_Voronoi_tessellation( cells,size,seeds, np.arange(N_seeds)+5,periodic)
|
||||||
Laguerre = Grid.from_Laguerre_tessellation(cells,size,seeds,np.ones(N_seeds),np.arange(N_seeds)+5,periodic)
|
Laguerre = Grid.from_Laguerre_tessellation(cells,size,seeds,np.ones(N_seeds),np.arange(N_seeds)+5,periodic)
|
||||||
assert grid_equal(Laguerre,Voronoi)
|
assert Laguerre == Voronoi
|
||||||
|
|
||||||
|
|
||||||
def test_Laguerre_weights(self):
|
def test_Laguerre_weights(self):
|
||||||
|
@ -447,9 +435,9 @@ class TestGrid:
|
||||||
cells = 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.coordinates0_point(cells,size).reshape(-1,3,order='F')
|
coords = grid_filters.coordinates0_point(cells,size).reshape(-1,3,order='F')
|
||||||
z=np.ones(cells.prod())
|
z = np.ones(cells.prod())
|
||||||
z[cells[:2].prod()*int(cells[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({'coords':3,'z':1},np.column_stack((coords,z)))
|
||||||
t = t.add('indicator',t.get('coords')[:,0])
|
t = t.add('indicator',t.get('coords')[:,0])
|
||||||
g = Grid.from_table(t,'coords',['indicator','z'])
|
g = Grid.from_table(t,'coords',['indicator','z'])
|
||||||
assert g.N_materials == g.cells[0]*2 and (g.material[:,:,-1]-g.material[:,:,0] == cells[0]).all()
|
assert g.N_materials == g.cells[0]*2 and (g.material[:,:,-1]-g.material[:,:,0] == cells[0]).all()
|
||||||
|
@ -461,8 +449,8 @@ class TestGrid:
|
||||||
s = seeds.from_random(size,np.random.randint(60,100))
|
s = seeds.from_random(size,np.random.randint(60,100))
|
||||||
grid = Grid.from_Voronoi_tessellation(cells,size,s)
|
grid = Grid.from_Voronoi_tessellation(cells,size,s)
|
||||||
coords = grid_filters.coordinates0_point(cells,size)
|
coords = grid_filters.coordinates0_point(cells,size)
|
||||||
t = Table(np.column_stack((coords.reshape(-1,3,order='F'),grid.material.flatten(order='F'))),{'c':3,'m':1})
|
t = Table({'c':3,'m':1},np.column_stack((coords.reshape(-1,3,order='F'),grid.material.flatten(order='F'))))
|
||||||
assert grid_equal(grid.sort().renumber(),Grid.from_table(t,'c',['m']))
|
assert grid.sort().renumber() == Grid.from_table(t,'c',['m'])
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('periodic',[True,False])
|
@pytest.mark.parametrize('periodic',[True,False])
|
||||||
|
@ -496,7 +484,7 @@ class TestGrid:
|
||||||
if update:
|
if update:
|
||||||
current.save(ref_path/'measured.vti')
|
current.save(ref_path/'measured.vti')
|
||||||
|
|
||||||
assert grid_equal(current,reference)
|
assert current == reference
|
||||||
|
|
||||||
def test_load_Neper_reference(self,ref_path,update):
|
def test_load_Neper_reference(self,ref_path,update):
|
||||||
current = Grid.load_Neper(ref_path/'n10-id1_scaled.vtk')
|
current = Grid.load_Neper(ref_path/'n10-id1_scaled.vtk')
|
||||||
|
@ -504,4 +492,4 @@ class TestGrid:
|
||||||
if update:
|
if update:
|
||||||
current.save(ref_path/'n10-id1_scaled.vti')
|
current.save(ref_path/'n10-id1_scaled.vti')
|
||||||
|
|
||||||
assert grid_equal(current,reference)
|
assert current == reference
|
||||||
|
|
|
@ -8,7 +8,9 @@ from damask import Table
|
||||||
def default():
|
def default():
|
||||||
"""Simple Table."""
|
"""Simple Table."""
|
||||||
x = np.ones((5,13),dtype=float)
|
x = np.ones((5,13),dtype=float)
|
||||||
return Table(x,{'F':(3,3),'v':(3,),'s':(1,)},['test data','contains five rows of only ones'])
|
return Table({'F':(3,3),'v':(3,),'s':(1,)},
|
||||||
|
x,
|
||||||
|
['test data','contains five rows of only ones'])
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def ref_path(ref_path_base):
|
def ref_path(ref_path_base):
|
||||||
|
@ -22,7 +24,7 @@ class TestTable:
|
||||||
|
|
||||||
@pytest.mark.parametrize('N',[10,40])
|
@pytest.mark.parametrize('N',[10,40])
|
||||||
def test_len(self,N):
|
def test_len(self,N):
|
||||||
assert len(Table(np.random.rand(N,3),{'X':3})) == N
|
assert len(Table({'X':3},np.random.rand(N,3))) == N
|
||||||
|
|
||||||
def test_get_scalar(self,default):
|
def test_get_scalar(self,default):
|
||||||
d = default.get('s')
|
d = default.get('s')
|
||||||
|
@ -110,7 +112,7 @@ class TestTable:
|
||||||
|
|
||||||
def test_rename_equivalent(self):
|
def test_rename_equivalent(self):
|
||||||
x = np.random.random((5,13))
|
x = np.random.random((5,13))
|
||||||
t = Table(x,{'F':(3,3),'v':(3,),'s':(1,)},['random test data'])
|
t = Table({'F':(3,3),'v':(3,),'s':(1,)},x,['random test data'])
|
||||||
s = t.get('s')
|
s = t.get('s')
|
||||||
u = t.rename('s','u').get('u')
|
u = t.rename('s','u').get('u')
|
||||||
assert np.all(s == u)
|
assert np.all(s == u)
|
||||||
|
@ -129,35 +131,35 @@ class TestTable:
|
||||||
|
|
||||||
def test_join(self):
|
def test_join(self):
|
||||||
x = np.random.random((5,13))
|
x = np.random.random((5,13))
|
||||||
a = Table(x,{'F':(3,3),'v':(3,),'s':(1,)},['random test data'])
|
a = Table({'F':(3,3),'v':(3,),'s':(1,)},x,['random test data'])
|
||||||
y = np.random.random((5,3))
|
y = np.random.random((5,3))
|
||||||
b = Table(y,{'u':(3,)},['random test data'])
|
b = Table({'u':(3,)},y,['random test data'])
|
||||||
c = a.join(b)
|
c = a.join(b)
|
||||||
assert np.array_equal(c.get('u'), b.get('u'))
|
assert np.array_equal(c.get('u'), b.get('u'))
|
||||||
|
|
||||||
def test_join_invalid(self):
|
def test_join_invalid(self):
|
||||||
x = np.random.random((5,13))
|
x = np.random.random((5,13))
|
||||||
a = Table(x,{'F':(3,3),'v':(3,),'s':(1,)},['random test data'])
|
a = Table({'F':(3,3),'v':(3,),'s':(1,)},x,['random test data'])
|
||||||
with pytest.raises(KeyError):
|
with pytest.raises(KeyError):
|
||||||
a.join(a)
|
a.join(a)
|
||||||
|
|
||||||
def test_append(self):
|
def test_append(self):
|
||||||
x = np.random.random((5,13))
|
x = np.random.random((5,13))
|
||||||
a = Table(x,{'F':(3,3),'v':(3,),'s':(1,)},['random test data'])
|
a = Table({'F':(3,3),'v':(3,),'s':(1,)},x,['random test data'])
|
||||||
b = a.append(a)
|
b = a.append(a)
|
||||||
assert np.array_equal(b.data[:5].to_numpy(),b.data[5:].to_numpy())
|
assert np.array_equal(b.data[:5].to_numpy(),b.data[5:].to_numpy())
|
||||||
|
|
||||||
def test_append_invalid(self):
|
def test_append_invalid(self):
|
||||||
x = np.random.random((5,13))
|
x = np.random.random((5,13))
|
||||||
a = Table(x,{'F':(3,3),'v':(3,),'s':(1,)},['random test data'])
|
a = Table({'F':(3,3),'v':(3,),'s':(1,)},x,['random test data'])
|
||||||
b = Table(x,{'F':(3,3),'u':(3,),'s':(1,)},['random test data'])
|
b = Table({'F':(3,3),'u':(3,),'s':(1,)},x,['random test data'])
|
||||||
with pytest.raises(KeyError):
|
with pytest.raises(KeyError):
|
||||||
a.append(b)
|
a.append(b)
|
||||||
|
|
||||||
def test_invalid_initialization(self):
|
def test_invalid_initialization(self):
|
||||||
x = np.random.random((5,10))
|
x = np.random.random((5,10))
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
Table(x,{'F':(3,3)})
|
Table({'F':(3,3)},x)
|
||||||
|
|
||||||
def test_invalid_set(self,default):
|
def test_invalid_set(self,default):
|
||||||
x = default.get('v')
|
x = default.get('v')
|
||||||
|
@ -170,27 +172,27 @@ class TestTable:
|
||||||
|
|
||||||
def test_sort_scalar(self):
|
def test_sort_scalar(self):
|
||||||
x = np.random.random((5,13))
|
x = np.random.random((5,13))
|
||||||
t = Table(x,{'F':(3,3),'v':(3,),'s':(1,)},['random test data'])
|
t = Table({'F':(3,3),'v':(3,),'s':(1,)},x,['random test data'])
|
||||||
unsort = t.get('s')
|
unsort = t.get('s')
|
||||||
sort = t.sort_by('s').get('s')
|
sort = t.sort_by('s').get('s')
|
||||||
assert np.all(np.sort(unsort,0)==sort)
|
assert np.all(np.sort(unsort,0)==sort)
|
||||||
|
|
||||||
def test_sort_component(self):
|
def test_sort_component(self):
|
||||||
x = np.random.random((5,12))
|
x = np.random.random((5,12))
|
||||||
t = Table(x,{'F':(3,3),'v':(3,)},['random test data'])
|
t = Table({'F':(3,3),'v':(3,)},x,['random test data'])
|
||||||
unsort = t.get('F')[:,1,0]
|
unsort = t.get('F')[:,1,0]
|
||||||
sort = t.sort_by('F[1,0]').get('F')[:,1,0]
|
sort = t.sort_by('F[1,0]').get('F')[:,1,0]
|
||||||
assert np.all(np.sort(unsort,0)==sort)
|
assert np.all(np.sort(unsort,0)==sort)
|
||||||
|
|
||||||
def test_sort_revert(self):
|
def test_sort_revert(self):
|
||||||
x = np.random.random((5,12))
|
x = np.random.random((5,12))
|
||||||
t = Table(x,{'F':(3,3),'v':(3,)},['random test data'])
|
t = Table({'F':(3,3),'v':(3,)},x,['random test data'])
|
||||||
sort = t.sort_by('F[1,0]',ascending=False).get('F')[:,1,0]
|
sort = t.sort_by('F[1,0]',ascending=False).get('F')[:,1,0]
|
||||||
assert np.all(np.sort(sort,0)==sort[::-1])
|
assert np.all(np.sort(sort,0)==sort[::-1])
|
||||||
|
|
||||||
def test_sort(self):
|
def test_sort(self):
|
||||||
t = Table(np.array([[0,1,],[2,1,]]),
|
t = Table({'v':(2,)},
|
||||||
{'v':(2,)},
|
np.array([[0,1,],[2,1,]]),
|
||||||
['test data'])\
|
['test data'])\
|
||||||
.add('s',np.array(['b','a']))\
|
.add('s',np.array(['b','a']))\
|
||||||
.sort_by('s')
|
.sort_by('s')
|
||||||
|
|
|
@ -147,16 +147,22 @@ class TestVTK:
|
||||||
|
|
||||||
def test_invalid_add_shape(self,default):
|
def test_invalid_add_shape(self,default):
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
default.add(np.ones(3),'valid')
|
default.add('valid',np.ones(3))
|
||||||
|
|
||||||
def test_invalid_add_missing_label(self,default):
|
def test_invalid_add_missing_label(self,default):
|
||||||
data = np.random.randint(9,size=np.prod(np.array(default.vtk_data.GetDimensions())-1))
|
data = np.random.randint(9,size=np.prod(np.array(default.vtk_data.GetDimensions())-1))
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
default.add(data)
|
default.add(data=data)
|
||||||
|
|
||||||
def test_invalid_add_type(self,default):
|
def test_invalid_add_type(self,default):
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
default.add('invalid_type','valid')
|
default.add(label='valid',data='invalid_type')
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
default.add(label='valid',table='invalid_type')
|
||||||
|
|
||||||
|
def test_invalid_add_dual(self,default):
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
default.add(label='valid',data=0,table=0)
|
||||||
|
|
||||||
@pytest.mark.parametrize('data_type,shape',[(float,(3,)),
|
@pytest.mark.parametrize('data_type,shape',[(float,(3,)),
|
||||||
(float,(3,3)),
|
(float,(3,3)),
|
||||||
|
@ -166,7 +172,7 @@ class TestVTK:
|
||||||
@pytest.mark.parametrize('N_values',[5*6*7,6*7*8])
|
@pytest.mark.parametrize('N_values',[5*6*7,6*7*8])
|
||||||
def test_add_get(self,default,data_type,shape,N_values):
|
def test_add_get(self,default,data_type,shape,N_values):
|
||||||
data = np.squeeze(np.random.randint(0,100,(N_values,)+shape)).astype(data_type)
|
data = np.squeeze(np.random.randint(0,100,(N_values,)+shape)).astype(data_type)
|
||||||
new = default.add(data,'data')
|
new = default.add('data',data)
|
||||||
assert (np.squeeze(data.reshape(N_values,-1)) == new.get('data')).all()
|
assert (np.squeeze(data.reshape(N_values,-1)) == new.get('data')).all()
|
||||||
|
|
||||||
|
|
||||||
|
@ -179,7 +185,7 @@ class TestVTK:
|
||||||
for k,s in shapes.items():
|
for k,s in shapes.items():
|
||||||
d[k] = dict(shape = s,
|
d[k] = dict(shape = s,
|
||||||
data = np.random.random(N*np.prod(s)).reshape((N,-1)))
|
data = np.random.random(N*np.prod(s)).reshape((N,-1)))
|
||||||
new = default.add(Table(np.column_stack([d[k]['data'] for k in shapes.keys()]),shapes))
|
new = default.add(table=Table(shapes,np.column_stack([d[k]['data'] for k in shapes.keys()])))
|
||||||
for k,s in shapes.items():
|
for k,s in shapes.items():
|
||||||
assert np.allclose(np.squeeze(d[k]['data']),new.get(k),rtol=1e-7)
|
assert np.allclose(np.squeeze(d[k]['data']),new.get(k),rtol=1e-7)
|
||||||
|
|
||||||
|
@ -187,8 +193,8 @@ class TestVTK:
|
||||||
def test_add_masked(self,default):
|
def test_add_masked(self,default):
|
||||||
data = np.random.rand(5*6*7,3)
|
data = np.random.rand(5*6*7,3)
|
||||||
masked = ma.MaskedArray(data,mask=data<.4,fill_value=42.)
|
masked = ma.MaskedArray(data,mask=data<.4,fill_value=42.)
|
||||||
mask_auto = default.add(masked,'D')
|
mask_auto = default.add('D',masked)
|
||||||
mask_manual = default.add(np.where(masked.mask,masked.fill_value,masked),'D')
|
mask_manual = default.add('D',np.where(masked.mask,masked.fill_value,masked))
|
||||||
assert mask_manual == mask_auto
|
assert mask_manual == mask_auto
|
||||||
|
|
||||||
|
|
||||||
|
@ -202,7 +208,7 @@ class TestVTK:
|
||||||
data = np.squeeze(np.random.randint(0,100,(N_values,)+shape)).astype(data_type)
|
data = np.squeeze(np.random.randint(0,100,(N_values,)+shape)).astype(data_type)
|
||||||
ALPHABET = np.array(list(string.ascii_lowercase + ' '))
|
ALPHABET = np.array(list(string.ascii_lowercase + ' '))
|
||||||
label = ''.join(np.random.choice(ALPHABET, size=10))
|
label = ''.join(np.random.choice(ALPHABET, size=10))
|
||||||
new = default.add(data,label)
|
new = default.add(label,data)
|
||||||
if N_values == default.N_points: assert label in new.labels['Point Data']
|
if N_values == default.N_points: assert label in new.labels['Point Data']
|
||||||
if N_values == default.N_cells: assert label in new.labels['Cell Data']
|
if N_values == default.N_cells: assert label in new.labels['Cell Data']
|
||||||
|
|
||||||
|
@ -217,7 +223,7 @@ class TestVTK:
|
||||||
@pytest.mark.xfail(int(vtk.vtkVersion.GetVTKVersion().split('.')[0])<8, reason='missing METADATA')
|
@pytest.mark.xfail(int(vtk.vtkVersion.GetVTKVersion().split('.')[0])<8, reason='missing METADATA')
|
||||||
def test_compare_reference_polyData(self,update,ref_path,tmp_path):
|
def test_compare_reference_polyData(self,update,ref_path,tmp_path):
|
||||||
points=np.dstack((np.linspace(0.,1.,10),np.linspace(0.,2.,10),np.linspace(-1.,1.,10))).squeeze()
|
points=np.dstack((np.linspace(0.,1.,10),np.linspace(0.,2.,10),np.linspace(-1.,1.,10))).squeeze()
|
||||||
polyData = VTK.from_poly_data(points).add(points,'coordinates')
|
polyData = VTK.from_poly_data(points).add('coordinates',points)
|
||||||
if update:
|
if update:
|
||||||
polyData.save(ref_path/'polyData')
|
polyData.save(ref_path/'polyData')
|
||||||
else:
|
else:
|
||||||
|
@ -234,8 +240,8 @@ class TestVTK:
|
||||||
c = coords[:-1,:-1,:-1,:].reshape(-1,3,order='F')
|
c = coords[:-1,:-1,:-1,:].reshape(-1,3,order='F')
|
||||||
n = coords[:,:,:,:].reshape(-1,3,order='F')
|
n = coords[:,:,:,:].reshape(-1,3,order='F')
|
||||||
rectilinearGrid = VTK.from_rectilinear_grid(grid) \
|
rectilinearGrid = VTK.from_rectilinear_grid(grid) \
|
||||||
.add(np.ascontiguousarray(c),'cell') \
|
.add('cell',np.ascontiguousarray(c)) \
|
||||||
.add(np.ascontiguousarray(n),'node')
|
.add('node',np.ascontiguousarray(n))
|
||||||
if update:
|
if update:
|
||||||
rectilinearGrid.save(ref_path/'rectilinearGrid')
|
rectilinearGrid.save(ref_path/'rectilinearGrid')
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Reference in New Issue