Merge branch 'python-vtk-improvements' into 'development'

Resolve issues in #149

See merge request damask/DAMASK!535
This commit is contained in:
Franz Roters 2022-03-01 08:10:39 +00:00
commit 3bc1a5eda4
13 changed files with 379 additions and 202 deletions

View File

@ -328,7 +328,7 @@ class Colormap(mpl.colors.ListedColormap):
if fname is None: if fname is None:
return open(self.name.replace(' ','_')+suffix, 'w', newline='\n') return open(self.name.replace(' ','_')+suffix, 'w', newline='\n')
elif isinstance(fname, (str, Path)): elif isinstance(fname, (str, Path)):
return open(fname, 'w', newline='\n') return open(Path(fname).expanduser(), 'w', newline='\n')
else: else:
return fname return fname

View File

@ -133,7 +133,7 @@ class Crystal():
def __repr__(self): def __repr__(self):
"""Represent.""" """Give short human-readable summary."""
family = f'Crystal family: {self.family}' family = f'Crystal family: {self.family}'
return family if self.lattice is None else \ return family if self.lattice is None else \
util.srepr([family, util.srepr([family,

View File

@ -17,6 +17,7 @@ from . import util
from . import grid_filters from . import grid_filters
from . import Rotation from . import Rotation
from . import Table from . import Table
from . import Colormap
from ._typehints import FloatSequence, IntSequence from ._typehints import FloatSequence, IntSequence
class Grid: class Grid:
@ -57,7 +58,7 @@ class Grid:
def __repr__(self) -> str: def __repr__(self) -> str:
"""Basic information on grid definition.""" """Give short human-readable summary."""
mat_min = np.nanmin(self.material) mat_min = np.nanmin(self.material)
mat_max = np.nanmax(self.material) mat_max = np.nanmax(self.material)
mat_N = self.N_materials mat_N = self.N_materials
@ -173,8 +174,8 @@ class Grid:
Parameters Parameters
---------- ----------
fname : str or pathlib.Path fname : str or pathlib.Path
Grid file to read. Valid extension is .vti, which will be appended Grid file to read.
if not given. Valid extension is .vti, which will be appended if not given.
Returns Returns
------- -------
@ -183,14 +184,14 @@ class Grid:
""" """
v = VTK.load(fname if str(fname).endswith('.vti') else str(fname)+'.vti') v = VTK.load(fname if str(fname).endswith('.vti') else str(fname)+'.vti')
comments = v.get_comments()
cells = 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
comments = v.comments
return Grid(material = v.get('material').reshape(cells,order='F'), return Grid(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)
@typing. no_type_check @typing. no_type_check
@ -643,9 +644,9 @@ class Grid:
Compress with zlib algorithm. Defaults to True. Compress with zlib algorithm. Defaults to True.
""" """
v = VTK.from_image_data(self.cells,self.size,self.origin) v = VTK.from_image_data(self.cells,self.size,self.origin)\
v.add(self.material.flatten(order='F'),'material') .add(self.material.flatten(order='F'),'material')
v.add_comments(self.comments) v.comments += self.comments
v.save(fname,parallel=False,compress=compress) v.save(fname,parallel=False,compress=compress)
@ -681,9 +682,20 @@ class Grid:
header='\n'.join(header), fmt=format_string, comments='') header='\n'.join(header), fmt=format_string, comments='')
def show(self) -> None: def show(self,
"""Show on screen.""" colormap: Colormap = Colormap.from_predefined('cividis')) -> None:
VTK.from_image_data(self.cells,self.size,self.origin).show() """
Show on screen.
Parameters
----------
colormap : damask.Colormap, optional
Colors used to map material IDs. Defaults to 'cividis'.
"""
VTK.from_image_data(self.cells,self.size,self.origin) \
.add(self.material.flatten('F'),'material') \
.show('material',colormap)
def add_primitive(self, def add_primitive(self,

View File

@ -120,10 +120,11 @@ class Orientation(Rotation,Crystal):
def __repr__(self) -> str: def __repr__(self) -> str:
"""Represent.""" """Give short human-readable summary."""
return '\n'.join([Crystal.__repr__(self), return util.srepr([Crystal.__repr__(self),
Rotation.__repr__(self)]) Rotation.__repr__(self)])
def __copy__(self: MyType, def __copy__(self: MyType,
rotation: Union[FloatSequence, Rotation] = None) -> MyType: rotation: Union[FloatSequence, Rotation] = None) -> MyType:
"""Create deep copy.""" """Create deep copy."""

View File

@ -154,7 +154,7 @@ class Result:
'fields': self.fields, 'fields': self.fields,
} }
self.fname = Path(fname).absolute() self.fname = Path(fname).expanduser().absolute()
self._protected = True self._protected = True
@ -167,7 +167,7 @@ class Result:
def __repr__(self): def __repr__(self):
"""Show summary of file content.""" """Give short human-readable summary."""
with h5py.File(self.fname,'r') as f: with h5py.File(self.fname,'r') as f:
header = [f'Created by {f.attrs["creator"]}', header = [f'Created by {f.attrs["creator"]}',
f' on {f.attrs["created"]}', f' on {f.attrs["created"]}',
@ -1635,7 +1635,7 @@ class Result:
else: else:
raise ValueError(f'invalid mode "{mode}"') raise ValueError(f'invalid mode "{mode}"')
v.set_comments(util.execution_stamp('Result','export_VTK')) v.comments = util.execution_stamp('Result','export_VTK')
N_digits = int(np.floor(np.log10(max(1,int(self.increments[-1][10:])))))+1 N_digits = int(np.floor(np.log10(max(1,int(self.increments[-1][10:])))))+1
@ -1651,12 +1651,12 @@ class Result:
if self.version_minor >= 13: if self.version_minor >= 13:
creator = f.attrs['creator'] if h5py3 else f.attrs['creator'].decode() creator = f.attrs['creator'] if h5py3 else f.attrs['creator'].decode()
created = f.attrs['created'] if h5py3 else f.attrs['created'].decode() created = f.attrs['created'] if h5py3 else f.attrs['created'].decode()
v.add_comments(f'{creator} ({created})') v.comments += f'{creator} ({created})'
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.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']:
@ -1683,7 +1683,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.add(dataset,' / '.join(['/'.join([ty,field,label]),dataset.dtype.metadata['unit']])) v = v.add(dataset,' / '.join(['/'.join([ty,field,label]),dataset.dtype.metadata['unit']]))
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)

View File

@ -88,7 +88,7 @@ class Rotation:
def __repr__(self) -> str: def __repr__(self) -> str:
"""Represent rotation as unit quaternion(s).""" """Give short human-readable summary."""
return f'Quaternion{" " if self.quaternion.shape == (4,) else "s of shape "+str(self.quaternion.shape[:-1])+chr(10)}'\ return f'Quaternion{" " if self.quaternion.shape == (4,) else "s of shape "+str(self.quaternion.shape[:-1])+chr(10)}'\
+ str(self.quaternion) + str(self.quaternion)

View File

@ -37,7 +37,7 @@ class Table:
def __repr__(self) -> str: def __repr__(self) -> str:
"""Brief overview.""" """Give short human-readable summary."""
self._relabel('shapes') self._relabel('shapes')
data_repr = self.data.__repr__() data_repr = self.data.__repr__()
self._relabel('uniform') self._relabel('uniform')
@ -259,7 +259,7 @@ class Table:
Table data from file. Table data from file.
""" """
f = open(fname) if isinstance(fname, (str, Path)) else fname f = open(Path(fname).expanduser()) if isinstance(fname, (str, Path)) else fname
f.seek(0) f.seek(0)
comments = [] comments = []
@ -333,7 +333,7 @@ class Table:
@property @property
def labels(self) -> List[Tuple[int, ...]]: def labels(self) -> List[str]:
return list(self.shapes) return list(self.shapes)
@ -589,7 +589,7 @@ class Table:
labels += [f'{util.srepr(self.shapes[l],"x")}:{i+1}_{l}' \ labels += [f'{util.srepr(self.shapes[l],"x")}:{i+1}_{l}' \
for i in range(np.prod(self.shapes[l]))] for i in range(np.prod(self.shapes[l]))]
f = open(fname,'w',newline='\n') if isinstance(fname, (str, Path)) else fname f = open(Path(fname).expanduser(),'w',newline='\n') if isinstance(fname, (str, Path)) else fname
f.write('\n'.join([f'# {c}' for c in self.comments] + [' '.join(labels)])+'\n') f.write('\n'.join([f'# {c}' for c in self.comments] + [' '.join(labels)])+'\n')
self.data.to_csv(f,sep=' ',na_rep='nan',index=False,header=False) self.data.to_csv(f,sep=' ',na_rep='nan',index=False,header=False)

View File

@ -1,8 +1,7 @@
import os import os
import warnings
import multiprocessing as mp import multiprocessing as mp
from pathlib import Path from pathlib import Path
from typing import Union, Literal, List from typing import Union, Literal, List, Sequence
import numpy as np import numpy as np
import vtk import vtk
@ -13,6 +12,7 @@ from vtk.util.numpy_support import vtk_to_numpy as vtk_to_np
from ._typehints import FloatSequence, IntSequence from ._typehints import FloatSequence, IntSequence
from . import util from . import util
from . import Table from . import Table
from . import Colormap
class VTK: class VTK:
@ -38,6 +38,110 @@ class VTK:
self.vtk_data = vtk_data self.vtk_data = vtk_data
def __repr__(self) -> str:
"""Give short human-readable summary."""
info = [self.vtk_data.__vtkname__]
for data in ['Cell Data', 'Point Data']:
if data == 'Cell Data': info.append(f'\n# cells: {self.N_cells}')
if data == 'Point Data': info.append(f'\n# points: {self.N_points}')
if data in self.labels:
info += [f' - {l}' for l in self.labels[data]]
return util.srepr(info)
def __eq__(self,
other: object) -> bool:
"""
Equal to other.
Parameters
----------
other : damask.VTK
VTK to check for equality.
"""
if not isinstance(other, VTK):
return NotImplemented
return self.as_ASCII() == other.as_ASCII()
def copy(self):
if isinstance(self.vtk_data,vtk.vtkImageData):
dup = vtk.vtkImageData()
elif isinstance(self.vtk_data,vtk.vtkUnstructuredGrid):
dup = vtk.vtkUnstructuredGrid()
elif isinstance(self.vtk_data,vtk.vtkPolyData):
dup = vtk.vtkPolyData()
elif isinstance(self.vtk_data,vtk.vtkRectilinearGrid):
dup = vtk.vtkRectilinearGrid()
else:
raise TypeError
dup.DeepCopy(self.vtk_data)
return VTK(dup)
@property
def comments(self) -> List[str]:
"""Return the comments."""
fielddata = self.vtk_data.GetFieldData()
for a in range(fielddata.GetNumberOfArrays()):
if fielddata.GetArrayName(a) == 'comments':
comments = fielddata.GetAbstractArray(a)
return [comments.GetValue(i) for i in range(comments.GetNumberOfValues())]
return []
@comments.setter
def comments(self,
comments: Union[str, Sequence[str]]):
"""
Set comments.
Parameters
----------
comments : str or list of str
Comments.
"""
s = vtk.vtkStringArray()
s.SetName('comments')
for c in util.tail_repack(comments,self.comments):
s.InsertNextValue(c)
self.vtk_data.GetFieldData().AddArray(s)
@property
def N_points(self) -> int:
"""Number of points in vtkdata."""
return self.vtk_data.GetNumberOfPoints()
@property
def N_cells(self) -> int:
"""Number of cells in vtkdata."""
return self.vtk_data.GetNumberOfCells()
@property
def labels(self):
"""Labels of datasets."""
labels = {}
cell_data = self.vtk_data.GetCellData()
if c := [cell_data.GetArrayName(a) for a in range(cell_data.GetNumberOfArrays())]:
labels['Cell Data'] = c
point_data = self.vtk_data.GetPointData()
if p := [point_data.GetArrayName(a) for a in range(point_data.GetNumberOfArrays())]:
labels['Point Data'] = p
return labels
@staticmethod @staticmethod
def from_image_data(cells: IntSequence, def from_image_data(cells: IntSequence,
size: FloatSequence, size: FloatSequence,
@ -70,40 +174,6 @@ class VTK:
return VTK(vtk_data) return VTK(vtk_data)
@staticmethod
def from_rectilinear_grid(grid: np.ndarray,
size: FloatSequence,
origin: FloatSequence = np.zeros(3)) -> 'VTK':
"""
Create VTK of type vtk.vtkRectilinearGrid.
Parameters
----------
grid : iterable of int, len (3)
Number of cells along each dimension.
size : iterable of float, len (3)
Physical length along each dimension.
origin : iterable of float, len (3), optional
Coordinates of grid origin.
Returns
-------
new : damask.VTK
VTK-based geometry without nodal or cell data.
"""
warnings.warn('Support for vtr files will be removed in DAMASK 3.1.0', DeprecationWarning,2)
vtk_data = vtk.vtkRectilinearGrid()
vtk_data.SetDimensions(*(np.array(grid)+1))
coord = [np_to_vtk(np.linspace(origin[i],origin[i]+size[i],grid[i]+1),deep=True) for i in [0,1,2]]
[coord[i].SetName(n) for i,n in enumerate(['x','y','z'])]
vtk_data.SetXCoordinates(coord[0])
vtk_data.SetYCoordinates(coord[1])
vtk_data.SetZCoordinates(coord[2])
return VTK(vtk_data)
@staticmethod @staticmethod
def from_unstructured_grid(nodes: np.ndarray, def from_unstructured_grid(nodes: np.ndarray,
connectivity: np.ndarray, connectivity: np.ndarray,
@ -115,7 +185,7 @@ class VTK:
Parameters Parameters
---------- ----------
nodes : numpy.ndarray of shape (:,3) nodes : numpy.ndarray, shape (:,3)
Spatial position of the nodes. Spatial position of the nodes.
connectivity : numpy.ndarray of np.dtype = int connectivity : numpy.ndarray of np.dtype = int
Cell connectivity (0-based), first dimension determines #Cells, Cell connectivity (0-based), first dimension determines #Cells,
@ -155,7 +225,7 @@ class VTK:
Parameters Parameters
---------- ----------
points : numpy.ndarray of shape (:,3) points : numpy.ndarray, shape (:,3)
Spatial position of the points. Spatial position of the points.
Returns Returns
@ -180,9 +250,36 @@ class VTK:
return VTK(vtk_data) return VTK(vtk_data)
@staticmethod
def from_rectilinear_grid(grid: FloatSequence) -> 'VTK':
"""
Create VTK of type vtk.vtkRectilinearGrid.
Parameters
----------
grid : iterables of floats, len (3)
Grid coordinates along x, y, and z directions.
Returns
-------
new : damask.VTK
VTK-based geometry without nodal or cell data.
"""
vtk_data = vtk.vtkRectilinearGrid()
vtk_data.SetDimensions(*map(len,grid))
coord = [np_to_vtk(np.array(grid[i]),deep=True) for i in [0,1,2]]
[coord[i].SetName(n) for i,n in enumerate(['x','y','z'])]
vtk_data.SetXCoordinates(coord[0])
vtk_data.SetYCoordinates(coord[1])
vtk_data.SetZCoordinates(coord[2])
return VTK(vtk_data)
@staticmethod @staticmethod
def load(fname: Union[str, Path], def load(fname: Union[str, Path],
dataset_type: Literal['ImageData', 'UnstructuredGrid', 'PolyData'] = None) -> 'VTK': dataset_type: Literal['ImageData', 'UnstructuredGrid', 'PolyData', 'RectilinearGrid'] = None) -> 'VTK':
""" """
Load from VTK file. Load from VTK file.
@ -190,8 +287,8 @@ class VTK:
---------- ----------
fname : str or pathlib.Path fname : str or pathlib.Path
Filename for reading. Filename for reading.
Valid extensions are .vti, .vtr, .vtu, .vtp, and .vtk. Valid extensions are .vti, .vtu, .vtp, .vtr, and .vtk.
dataset_type : {'ImageData', 'UnstructuredGrid', 'PolyData'}, optional dataset_type : {'ImageData', 'UnstructuredGrid', 'PolyData', 'RectilinearGrid'}, optional
Name of the vtk.vtkDataSet subclass when opening a .vtk file. Name of the vtk.vtkDataSet subclass when opening a .vtk file.
Returns Returns
@ -200,40 +297,40 @@ class VTK:
VTK-based geometry from file. VTK-based geometry from file.
""" """
if not os.path.isfile(fname): # vtk has a strange error handling if not Path(fname).expanduser().is_file(): # vtk has a strange error handling
raise FileNotFoundError(f'file "{fname}" not found') raise FileNotFoundError(f'file "{fname}" not found')
if (ext := Path(fname).suffix) == '.vtk' or dataset_type is not None: if (ext := Path(fname).suffix) == '.vtk' or dataset_type is not None:
reader = vtk.vtkGenericDataObjectReader() reader = vtk.vtkGenericDataObjectReader()
reader.SetFileName(str(fname)) reader.SetFileName(str(Path(fname).expanduser()))
if dataset_type is None: if dataset_type is None:
raise TypeError('dataset type for *.vtk file not given') raise TypeError('dataset type for *.vtk file not given')
elif dataset_type.lower().endswith(('imagedata','image_data')): elif dataset_type.lower().endswith(('imagedata','image_data')):
reader.Update() reader.Update()
vtk_data = reader.GetStructuredPointsOutput() vtk_data = reader.GetStructuredPointsOutput()
elif dataset_type.lower().endswith(('rectilineargrid','rectilinear_grid')):
reader.Update()
vtk_data = reader.GetRectilinearGridOutput()
elif dataset_type.lower().endswith(('unstructuredgrid','unstructured_grid')): elif dataset_type.lower().endswith(('unstructuredgrid','unstructured_grid')):
reader.Update() reader.Update()
vtk_data = reader.GetUnstructuredGridOutput() vtk_data = reader.GetUnstructuredGridOutput()
elif dataset_type.lower().endswith(('polydata','poly_data')): elif dataset_type.lower().endswith(('polydata','poly_data')):
reader.Update() reader.Update()
vtk_data = reader.GetPolyDataOutput() vtk_data = reader.GetPolyDataOutput()
elif dataset_type.lower().endswith(('rectilineargrid','rectilinear_grid')):
reader.Update()
vtk_data = reader.GetRectilinearGridOutput()
else: else:
raise TypeError(f'unknown dataset type "{dataset_type}" for vtk file') raise TypeError(f'unknown dataset type "{dataset_type}" for vtk file')
else: else:
if ext == '.vti': if ext == '.vti':
reader = vtk.vtkXMLImageDataReader() reader = vtk.vtkXMLImageDataReader()
elif ext == '.vtr':
reader = vtk.vtkXMLRectilinearGridReader()
elif ext == '.vtu': elif ext == '.vtu':
reader = vtk.vtkXMLUnstructuredGridReader() reader = vtk.vtkXMLUnstructuredGridReader()
elif ext == '.vtp': elif ext == '.vtp':
reader = vtk.vtkXMLPolyDataReader() reader = vtk.vtkXMLPolyDataReader()
elif ext == '.vtr':
reader = vtk.vtkXMLRectilinearGridReader()
else: else:
raise TypeError(f'unknown file extension "{ext}"') raise TypeError(f'unknown file extension "{ext}"')
reader.SetFileName(str(fname)) reader.SetFileName(str(Path(fname).expanduser()))
reader.Update() reader.Update()
vtk_data = reader.GetOutput() vtk_data = reader.GetOutput()
@ -245,6 +342,17 @@ class VTK:
"""Wrapper for parallel writing.""" """Wrapper for parallel writing."""
writer.Write() writer.Write()
def as_ASCII(self) -> str:
"""ASCII representation of the VTK data."""
writer = vtk.vtkDataSetWriter()
writer.SetHeader(f'# {util.execution_stamp("VTK")}')
writer.WriteToOutputStringOn()
writer.SetInputData(self.vtk_data)
writer.Write()
return writer.GetOutputString()
def save(self, def save(self,
fname: Union[str, Path], fname: Union[str, Path],
parallel: bool = True, parallel: bool = True,
@ -264,16 +372,16 @@ class VTK:
""" """
if isinstance(self.vtk_data,vtk.vtkImageData): if isinstance(self.vtk_data,vtk.vtkImageData):
writer = vtk.vtkXMLImageDataWriter() writer = vtk.vtkXMLImageDataWriter()
elif isinstance(self.vtk_data,vtk.vtkRectilinearGrid):
writer = vtk.vtkXMLRectilinearGridWriter()
elif isinstance(self.vtk_data,vtk.vtkUnstructuredGrid): elif isinstance(self.vtk_data,vtk.vtkUnstructuredGrid):
writer = vtk.vtkXMLUnstructuredGridWriter() writer = vtk.vtkXMLUnstructuredGridWriter()
elif isinstance(self.vtk_data,vtk.vtkPolyData): elif isinstance(self.vtk_data,vtk.vtkPolyData):
writer = vtk.vtkXMLPolyDataWriter() writer = vtk.vtkXMLPolyDataWriter()
elif isinstance(self.vtk_data,vtk.vtkRectilinearGrid):
writer = vtk.vtkXMLRectilinearGridWriter()
default_ext = '.'+writer.GetDefaultFileExtension() default_ext = '.'+writer.GetDefaultFileExtension()
ext = Path(fname).suffix ext = Path(fname).suffix
writer.SetFileName(str(fname)+(default_ext if default_ext != ext else '')) writer.SetFileName(str(Path(fname).expanduser())+(default_ext if default_ext != ext else ''))
if compress: if compress:
writer.SetCompressorTypeToZLib() writer.SetCompressorTypeToZLib()
@ -293,36 +401,31 @@ 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
# Needs support for damask.Table
def add(self, def add(self,
data: Union[np.ndarray, np.ma.MaskedArray], data: Union[np.ndarray, np.ma.MaskedArray, 'Table'],
label: str = None): label: str = None):
""" """
Add data to either cells or points. Add data to either cells or points.
Parameters Parameters
---------- ----------
data : numpy.ndarray or numpy.ma.MaskedArray data : numpy.ndarray, numpy.ma.MaskedArray, or damask.Table
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 label : str, optional if data is damask.Table
Data label. Data label.
""" """
N_points = self.vtk_data.GetNumberOfPoints()
N_cells = self.vtk_data.GetNumberOfCells()
if isinstance(data,np.ndarray): def _add_array(vtk_data,
if label is None: data: np.ndarray,
raise ValueError('no label defined for numpy.ndarray') label: str):
N_data = data.shape[0] N_data = data.shape[0]
data_ = (data if not isinstance(data,np.ma.MaskedArray) else data_ = data.reshape(N_data,-1) \
np.where(data.mask,data.fill_value,data)).reshape(N_data,-1) .astype(np.single if data.dtype in [np.double,np.longdouble] else data.dtype)
if data_.dtype in [np.double,np.longdouble]: if data.dtype.type is np.str_:
d = np_to_vtk(data_.astype(np.single),deep=True) # avoid large files
elif data_.dtype.type is np.str_:
d = vtk.vtkStringArray() d = vtk.vtkStringArray()
for s in np.squeeze(data_): for s in np.squeeze(data_):
d.InsertNextValue(s) d.InsertNextValue(s)
@ -331,17 +434,29 @@ class VTK:
d.SetName(label) d.SetName(label)
if N_data == N_points: if N_data == vtk_data.GetNumberOfPoints():
self.vtk_data.GetPointData().AddArray(d) vtk_data.GetPointData().AddArray(d)
elif N_data == N_cells: elif N_data == vtk_data.GetNumberOfCells():
self.vtk_data.GetCellData().AddArray(d) vtk_data.GetCellData().AddArray(d)
else: else:
raise ValueError(f'cell / point count ({N_cells} / {N_points}) differs from data ({N_data})') raise ValueError(f'data count mismatch ({N_data}{self.N_points} & {self.N_cells})')
dup = self.copy()
if isinstance(data,np.ndarray):
if label is not None:
_add_array(dup.vtk_data,
np.where(data.mask,data.fill_value,data) if isinstance(data,np.ma.MaskedArray) else data,
label)
else:
raise ValueError('no label defined for numpy.ndarray')
elif isinstance(data,Table): elif isinstance(data,Table):
raise NotImplementedError('damask.Table') for l in data.labels:
_add_array(dup.vtk_data,data.get(l),l)
else: else:
raise TypeError raise TypeError
return dup
def get(self, def get(self,
label: str) -> np.ndarray: label: str) -> np.ndarray:
@ -386,59 +501,9 @@ class VTK:
raise ValueError(f'array "{label}" not found') raise ValueError(f'array "{label}" not found')
def get_comments(self) -> List[str]: def show(self,
"""Return the comments.""" label: str = None,
fielddata = self.vtk_data.GetFieldData() colormap: Colormap = Colormap.from_predefined('cividis')):
for a in range(fielddata.GetNumberOfArrays()):
if fielddata.GetArrayName(a) == 'comments':
comments = fielddata.GetAbstractArray(a)
return [comments.GetValue(i) for i in range(comments.GetNumberOfValues())]
return []
def set_comments(self,
comments: Union[str, List[str]]):
"""
Set comments.
Parameters
----------
comments : str or list of str
Comments.
"""
s = vtk.vtkStringArray()
s.SetName('comments')
for c in [comments] if isinstance(comments,str) else comments:
s.InsertNextValue(c)
self.vtk_data.GetFieldData().AddArray(s)
def add_comments(self,
comments: Union[str, List[str]]):
"""
Add comments.
Parameters
----------
comments : str or list of str
Comments to add.
"""
self.set_comments(self.get_comments() + ([comments] if isinstance(comments,str) else comments))
def __repr__(self) -> str:
"""ASCII representation of the VTK data."""
writer = vtk.vtkDataSetWriter()
writer.SetHeader(f'# {util.execution_stamp("VTK")}')
writer.WriteToOutputStringOn()
writer.SetInputData(self.vtk_data)
writer.Write()
return writer.GetOutputString()
def show(self):
""" """
Render. Render.
@ -459,8 +524,17 @@ class VTK:
width = 1024 width = 1024
height = 768 height = 768
lut = vtk.vtkLookupTable()
lut.SetNumberOfTableValues(len(colormap.colors))
for i,c in enumerate(colormap.colors):
lut.SetTableValue(i,c if len(c)==4 else np.append(c,1.0))
lut.Build()
self.vtk_data.GetCellData().SetActiveScalars(label)
mapper = vtk.vtkDataSetMapper() mapper = vtk.vtkDataSetMapper()
mapper.SetInputData(self.vtk_data) mapper.SetInputData(self.vtk_data)
mapper.SetLookupTable(lut)
mapper.SetScalarRange(self.vtk_data.GetScalarRange())
actor = vtk.vtkActor() actor = vtk.vtkActor()
actor.SetMapper(mapper) actor.SetMapper(mapper)
@ -468,7 +542,15 @@ class VTK:
ren = vtk.vtkRenderer() ren = vtk.vtkRenderer()
ren.AddActor(actor) ren.AddActor(actor)
if label is None:
ren.SetBackground(67/255,128/255,208/255) ren.SetBackground(67/255,128/255,208/255)
else:
colormap = vtk.vtkScalarBarActor()
colormap.SetLookupTable(lut)
colormap.SetTitle(label)
colormap.SetMaximumWidthInPixels(width//100)
ren.AddActor2D(colormap)
ren.SetBackground(0.3,0.3,0.3)
window = vtk.vtkRenderWindow() window = vtk.vtkRenderWindow()
window.AddRenderer(ren) window.AddRenderer(ren)

View File

@ -9,7 +9,7 @@ import re
import fractions import fractions
from collections import abc from collections import abc
from functools import reduce from functools import reduce
from typing import Union, Tuple, Iterable, Callable, Dict, List, Any, Literal from typing import Callable, Union, Iterable, Sequence, Dict, List, Tuple, Literal, Any
from pathlib import Path from pathlib import Path
import numpy as np import numpy as np
@ -33,7 +33,8 @@ __all__=[
'extend_docstring', 'extended_docstring', 'extend_docstring', 'extended_docstring',
'Bravais_to_Miller', 'Miller_to_Bravais', 'Bravais_to_Miller', 'Miller_to_Bravais',
'DREAM3D_base_group', 'DREAM3D_cell_data_group', 'DREAM3D_base_group', 'DREAM3D_cell_data_group',
'dict_prune', 'dict_flatten' 'dict_prune', 'dict_flatten',
'tail_repack',
] ]
# https://svn.blender.org/svnroot/bf-blender/trunk/blender/build_files/scons/tools/bcolors.py # https://svn.blender.org/svnroot/bf-blender/trunk/blender/build_files/scons/tools/bcolors.py
@ -722,6 +723,36 @@ def dict_flatten(d: Dict) -> Dict:
def tail_repack(extended: Union[str, Sequence[str]],
existing: List[str] = []) -> List[str]:
"""
Repack tailing characters into single string if all are new.
Parameters
----------
extended : str or list of str
Extended string list with potentially autosplitted tailing string relative to `existing`.
existing : list of str
Base string list.
Returns
-------
repacked : list of str
Repacked version of `extended`.
Examples
--------
>>> tail_repack(['a','new','e','n','t','r','y'],['a','new'])
['a','new','entry']
>>> tail_repack(['a','new','shiny','e','n','t','r','y'],['a','new'])
['a','new','shiny','e','n','t','r','y']
"""
return [extended] if isinstance(extended,str) else existing + \
([''.join(extended[len(existing):])] if np.prod([len(i) for i in extended[len(existing):]]) == 1 else
list(extended[len(existing):]))
#################################################################################################### ####################################################################################################
# Classes # Classes
#################################################################################################### ####################################################################################################

View File

@ -1,42 +1,42 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<VTKFile type="RectilinearGrid" version="0.1" byte_order="LittleEndian" header_type="UInt32" compressor="vtkZLibDataCompressor"> <VTKFile type="RectilinearGrid" version="0.1" byte_order="LittleEndian" header_type="UInt32" compressor="vtkZLibDataCompressor">
<RectilinearGrid WholeExtent="0 5 0 6 0 7"> <RectilinearGrid WholeExtent="0 3 0 4 0 5">
<Piece Extent="0 5 0 6 0 7"> <Piece Extent="0 3 0 4 0 5">
<PointData> <PointData>
<DataArray type="Float32" Name="node" NumberOfComponents="3" format="binary" RangeMin="0" RangeMax="1.268857765318962"> <DataArray type="Float32" Name="node" NumberOfComponents="3" format="binary" RangeMin="0" RangeMax="31.016124838541646">
AQAAAACAAADADwAAFQMAAA==eF5Vl7GtFEEQBTeG8wgAhxxux8bEOIsYwDu/M0A6D0JofMAmjU0BF5+d7anXjzP+L6NV4l3pj8S29efL77/35ucO//nwS3zeiL99fTM2+3zPd/u6uTc/d3h67EY8PXB50jxpnjRPmifNk/Kcn4Gn+do18NiNeO3StvPfJk/ztUseuxGvXfI8Hg95mp87PD12I54euD5hu0IeuHbpRly74r9mb9+/7nQvru6T6b5uxHSfPH/PdniaqzseuxHTvT1pnjRPmifNk+ZJec7PsF3Ddg3bxY2Y7rZLnubqbrvkgemOZ7bD01zd8diNmO69K2xXyAPTvXeF7QrzzHa3vbtPpvtt7+7Xjbi73/b1/ex4muleHrsRd3c8aZ40T5onzZPmSXm2q512Dds1bBc34u6uXfI001275IG7e3mqXXma6V4euxF3d3aF7Qp54O7OrrBdYZ5t+/npo7oXV/fJdF83YrpPXt/Pjqe5uuOxGzHd25PmSfOkedI8aZ6U5/wM2zVs17Bd3Ijpbrvkaa7utksemO54Zjs8zdUdj92I6d67wnaFPDDde1fYrjDP9Vare7Heeft7f6n7ZHvn7e/9pe54YHvn1R0PXJ40T5onzZPmSfOkPFu91epuu4bt4kZs77z9vWuXPLC98+puu+RZb7W644HtnVd3PHDNCtsV8sD2zqt77wrzbNvn44e6F1f3yXRfN2K6T17fz46nubrjsRsx3duT5knzpHnSPGmelOf8DNs1bNewXdyI6W675Gmu7rZLHpjueGY7PM3VHY/diOneu8J2hTww3XtX2K4wz3yrD3Uv5p0/7J0/1H1yv/OHuuNp5p0/7J0/1B0PXJ40T5onzZPmSfOkPNv1VmvXsF3DdnEj7ndeu+Rp5p3XLnngfucPdcfTzDt/2Dt/qDseuGaF7Qp54H7n2RW2K8xT3xHdi5/67ui+bsR0nzx/zHZ4mqs7HrsR0709aZ40T5onzZPmSXnWb3YNPDDd8cB0t13yNFd32yUPTHc8sx2eZv0/btAdD0z33hW2K+SB6d67wnYV/wMyiXC6 AQAAAACAAACgBQAA+QAAAA==eF5tkoENwjAMBDsCG/XZzON1LJSIf58Dlaoc4mr7k1wXn7rBan69hxZvrWE1L/9fre2b1bx9te9+yw+rea2cqeAX/IJf8OMyh5rZn+541ey8514MT83Oe+7XqKVm5019+AW/4Bd8zhRWM/d4r0c+zs65zrMcLt7k/frMz//Myev68At+wS/46cH9RW/eo13/OMNzPp7D9gVf8DVnZ5+fs4TLd9eHH1az89r3N+N+aNYy75X3hnvKfjd8wTdrzmRej+cbd1FzbnPmRc/Uh2/283z9sJqd93H9u/2wmp03vuALvuCr/fXbfljNzpv68MNqdt7n/QEgMrwB
<InformationKey name="L2_NORM_RANGE" location="vtkDataArray" length="2"> <InformationKey name="L2_NORM_RANGE" location="vtkDataArray" length="2">
<Value index="0"> <Value index="0">
0 0
</Value> </Value>
<Value index="1"> <Value index="1">
1.2688577653 31.016124839
</Value> </Value>
</InformationKey> </InformationKey>
</DataArray> </DataArray>
</PointData> </PointData>
<CellData> <CellData>
<DataArray type="Float32" Name="cell" NumberOfComponents="3" format="binary" RangeMin="0.10871961651677305" RangeMax="1.1607924233069467"> <DataArray type="Float32" Name="cell" NumberOfComponents="3" format="binary" RangeMin="0" RangeMax="18.788294228055936">
AQAAAACAAADYCQAA8QEAAA==eF5N0CGOFlEQAOHVK0nmBnAFgnn/PLsSsZd4CSQIPDfYZN2iUbMePI4DEMR/BbgDNJ2ve1yZSir18P3jeD6O8eruxfj99s0Ff356Kh63v4o/jNsdP/xzb24+Xbg4XBwuDheHe3//s1wcLg4Xh4vT3fZ2k9NNTjc53eRsnuXibJ7l4mye5T4fq1ycr1a5OF+tk3uMb++u9TnY52Cfg30O9pmLfeZin7nxjYt95mKf2932dpN9bjfZ526e5WKfu3mWi33uV6tc7HO/Wif3GO+vry8+B/sc7HOwz8E+c7HPXOwzN75xsc9c7HO7295uss/tJvvczbNc7HM3z3Kxz/1qlYt97lfr5B7/f/kc7HOwz8E+B/vMxT5zsc/c+MbFPnOxz+1ue7vJPreb7HM3z3Kxz908y8U+96tVLva5X62Te4y7xy/1OdjnYJ+DfQ72mYt95mKfufGNi33mYp/b3fZ2k31uN9nnbp7lYp+7eZaLfe5Xq1zsc79aJ/cYjy9/1Odgn4N9DvY52Gcu9pmLfebGNy72mYt9bnfb2032ud1kn7t5lot97uZZLva5X61ysc/9ap3cY1y//qnPwT4H+xzsc7DPXOwzF/vMjW9c7DMX+9zutreb7HO7yT538ywX+9zNs1zsc79a5WKf+1XyX6K10A4= AQAAAACAAADQAgAAigAAAA==eF5tkQEOgDAIA32CP7L+bE83oMBVJVnWhGMt2bax1gEta3Uv7tb6n0mmtPyt/RymtUZXmQ/fe+Wwo/981pPnDt9iWmt0FWe403tn89NozlkfJyp9hTxCnodpjjtgPsr2Ob7vJiMw8OB/xF1Ma42uWsizkGchTzGtNToZgREYDZP+yFPMrS+Grlfp
<InformationKey name="L2_NORM_RANGE" location="vtkDataArray" length="2"> <InformationKey name="L2_NORM_RANGE" location="vtkDataArray" length="2">
<Value index="0"> <Value index="0">
0.10871961652 0
</Value> </Value>
<Value index="1"> <Value index="1">
1.1607924233 18.788294228
</Value> </Value>
</InformationKey> </InformationKey>
</DataArray> </DataArray>
</CellData> </CellData>
<Coordinates> <Coordinates>
<DataArray type="Float64" Name="x" format="binary" RangeMin="0" RangeMax="0.6"> <DataArray type="Float64" Name="x" format="binary" RangeMin="0" RangeMax="9">
AQAAAACAAAAwAAAAJwAAAA==eF5jYICAHXKtrwN37LOH0Ofsua4vLrDlug7l37M3BoPH9gCdQxK6 AQAAAACAAAAgAAAAFQAAAA==eF5jYEAGH+whtIADhFZyAAAYkwHi
</DataArray> </DataArray>
<DataArray type="Float64" Name="y" format="binary" RangeMin="0" RangeMax="1"> <DataArray type="Float64" Name="y" format="binary" RangeMin="0" RangeMax="16">
AQAAAACAAAA4AAAAIgAAAA==eF5jYICAUDA4ag+hr9pDRB9A+U/tV4HBK6j4B3sAk7wQqg== AQAAAACAAAAoAAAAFwAAAA==eF5jYEAGH+whtIADhFaC0gYOAChDAlI=
</DataArray> </DataArray>
<DataArray type="Float64" Name="z" format="binary" RangeMin="0" RangeMax="0.5"> <DataArray type="Float64" Name="z" format="binary" RangeMin="0" RangeMax="25">
AQAAAACAAABAAAAALAAAAA==eF5jYICASSqeQLTJHkIfsr+9LReITkP5l+zB3NvXoOK37SG6HtgDANusGUo= AQAAAACAAAAwAAAAGgAAAA==eF5jYEAGH+whtIADhFaC0gZQ2tIBADuFAss=
</DataArray> </DataArray>
</Coordinates> </Coordinates>
</Piece> </Piece>

View File

@ -42,6 +42,10 @@ class TestGrid:
print('patched datetime.datetime.now') print('patched datetime.datetime.now')
def test_show(sef,default,monkeypatch):
monkeypatch.delenv('DISPLAY',raising=False)
default.show()
def test_equal(self,default): def test_equal(self,default):
assert default == default assert default == default
assert not default == 42 assert not default == 42

View File

@ -385,7 +385,7 @@ class TestResult:
result.export_VTK(output,parallel=False) result.export_VTK(output,parallel=False)
fname = fname.split('.')[0]+f'_inc{(inc if type(inc) == int else inc[0]):0>2}.vti' fname = fname.split('.')[0]+f'_inc{(inc if type(inc) == int else inc[0]):0>2}.vti'
v = VTK.load(tmp_path/fname) v = VTK.load(tmp_path/fname)
v.set_comments('n/a') v.comments = 'n/a'
v.save(tmp_path/fname,parallel=False) v.save(tmp_path/fname,parallel=False)
with open(fname) as f: with open(fname) as f:
cur = hashlib.md5(f.read().encode()).hexdigest() cur = hashlib.md5(f.read().encode()).hexdigest()

View File

@ -1,6 +1,7 @@
import os import os
import filecmp import filecmp
import time import time
import string
import pytest import pytest
import numpy as np import numpy as np
@ -8,7 +9,7 @@ import numpy.ma as ma
import vtk import vtk
from damask import VTK from damask import VTK
from damask import grid_filters from damask import Table
@pytest.fixture @pytest.fixture
def ref_path(ref_path_base): def ref_path(ref_path_base):
@ -32,29 +33,44 @@ class TestVTK:
monkeypatch.delenv('DISPLAY',raising=False) monkeypatch.delenv('DISPLAY',raising=False)
default.show() default.show()
def test_imageData(self,tmp_path):
cells = np.random.randint(5,10,3)
size = np.random.random(3) + 0.1
origin = np.random.random(3) - 0.5
v = VTK.from_image_data(cells,size,origin)
string = str(v)
string = v.as_ASCII()
v.save(tmp_path/'imageData',False)
vtr = VTK.load(tmp_path/'imageData.vti')
with open(tmp_path/'imageData.vtk','w') as f:
f.write(string)
vtk = VTK.load(tmp_path/'imageData.vtk','VTK_imageData')
assert (string == vtr.as_ASCII() == vtk.as_ASCII())
def test_rectilinearGrid(self,tmp_path): def test_rectilinearGrid(self,tmp_path):
cells = np.random.randint(5,10,3)*2 grid = np.sort(np.random.random((3,10)))
size = np.random.random(3) + 1.0 v = VTK.from_rectilinear_grid(grid)
origin = np.random.random(3) string = str(v)
v = VTK.from_rectilinear_grid(cells,size,origin) string = v.as_ASCII()
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')
with open(tmp_path/'rectilinearGrid.vtk','w') as f: with open(tmp_path/'rectilinearGrid.vtk','w') as f:
f.write(string) f.write(string)
vtk = VTK.load(tmp_path/'rectilinearGrid.vtk','VTK_rectilinearGrid') vtk = VTK.load(tmp_path/'rectilinearGrid.vtk','VTK_rectilinearGrid')
assert(string == vtr.__repr__() == vtk.__repr__()) assert (string == vtr.as_ASCII() == vtk.as_ASCII())
def test_polyData(self,tmp_path): def test_polyData(self,tmp_path):
points = np.random.rand(100,3) points = np.random.rand(100,3)
v = VTK.from_poly_data(points) v = VTK.from_poly_data(points)
string = v.__repr__() string = str(v)
string = v.as_ASCII()
v.save(tmp_path/'polyData',False) v.save(tmp_path/'polyData',False)
vtp = VTK.load(tmp_path/'polyData.vtp') vtp = VTK.load(tmp_path/'polyData.vtp')
with open(tmp_path/'polyData.vtk','w') as f: with open(tmp_path/'polyData.vtk','w') as f:
f.write(string) f.write(string)
vtk = VTK.load(tmp_path/'polyData.vtk','polyData') vtk = VTK.load(tmp_path/'polyData.vtk','polyData')
assert(string == vtp.__repr__() == vtk.__repr__()) assert(string == vtp.as_ASCII() == vtk.as_ASCII())
@pytest.mark.parametrize('cell_type,n',[ @pytest.mark.parametrize('cell_type,n',[
('VTK_hexahedron',8), ('VTK_hexahedron',8),
@ -67,13 +83,14 @@ class TestVTK:
nodes = np.random.rand(n,3) nodes = np.random.rand(n,3)
connectivity = np.random.choice(np.arange(n),n,False).reshape(-1,n) connectivity = np.random.choice(np.arange(n),n,False).reshape(-1,n)
v = VTK.from_unstructured_grid(nodes,connectivity,cell_type) v = VTK.from_unstructured_grid(nodes,connectivity,cell_type)
string = v.__repr__() string = str(v)
string = v.as_ASCII()
v.save(tmp_path/'unstructuredGrid',False) v.save(tmp_path/'unstructuredGrid',False)
vtu = VTK.load(tmp_path/'unstructuredGrid.vtu') vtu = VTK.load(tmp_path/'unstructuredGrid.vtu')
with open(tmp_path/'unstructuredGrid.vtk','w') as f: with open(tmp_path/'unstructuredGrid.vtk','w') as f:
f.write(string) f.write(string)
vtk = VTK.load(tmp_path/'unstructuredGrid.vtk','unstructuredgrid') vtk = VTK.load(tmp_path/'unstructuredGrid.vtk','unstructuredgrid')
assert(string == vtu.__repr__() == vtk.__repr__()) assert(string == vtu.as_ASCII() == vtk.as_ASCII())
def test_parallel_out(self,tmp_path): def test_parallel_out(self,tmp_path):
@ -97,7 +114,7 @@ class TestVTK:
fname_p = tmp_path/'plain.vtp' fname_p = tmp_path/'plain.vtp'
v.save(fname_c,parallel=False,compress=False) v.save(fname_c,parallel=False,compress=False)
v.save(fname_p,parallel=False,compress=True) v.save(fname_p,parallel=False,compress=True)
assert(VTK.load(fname_c).__repr__() == VTK.load(fname_p).__repr__()) assert(VTK.load(fname_c).as_ASCII() == VTK.load(fname_p).as_ASCII())
@pytest.mark.parametrize('fname',['a','a.vtp','a.b','a.b.vtp']) @pytest.mark.parametrize('fname',['a','a.vtp','a.b','a.b.vtp'])
@ -148,49 +165,79 @@ 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)
default.add(data,'data') new = default.add(data,'data')
assert (np.squeeze(data.reshape(N_values,-1)) == default.get('data')).all() assert (np.squeeze(data.reshape(N_values,-1)) == new.get('data')).all()
@pytest.mark.parametrize('shapes',[{'scalar':(1,),'vector':(3,),'tensor':(3,3)},
{'vector':(6,),'tensor':(3,3)},
{'tensor':(3,3),'scalar':(1,)}])
def test_add_table(self,default,shapes):
N = np.random.choice([default.N_points,default.N_cells])
d = dict()
for k,s in shapes.items():
d[k] = dict(shape = s,
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))
for k,s in shapes.items():
assert np.allclose(np.squeeze(d[k]['data']),new.get(k),rtol=1e-7)
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.)
default.add(masked,'D') mask_auto = default.add(masked,'D')
result_masked = str(default) mask_manual = default.add(np.where(masked.mask,masked.fill_value,masked),'D')
default.add(np.where(masked.mask,masked.fill_value,masked),'D') assert mask_manual == mask_auto
assert result_masked == str(default)
@pytest.mark.parametrize('data_type,shape',[(float,(3,)),
(float,(3,3)),
(float,(1,)),
(int,(4,)),
(str,(1,))])
@pytest.mark.parametrize('N_values',[5*6*7,6*7*8])
def test_labels(self,default,data_type,shape,N_values):
data = np.squeeze(np.random.randint(0,100,(N_values,)+shape)).astype(data_type)
ALPHABET = np.array(list(string.ascii_lowercase + ' '))
label = ''.join(np.random.choice(ALPHABET, size=10))
new = default.add(data,label)
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']
def test_comments(self,tmp_path,default): def test_comments(self,tmp_path,default):
default.add_comments(['this is a comment']) default.comments += 'this is a comment'
default.save(tmp_path/'with_comments',parallel=False) default.save(tmp_path/'with_comments',parallel=False)
new = VTK.load(tmp_path/'with_comments.vti') new = VTK.load(tmp_path/'with_comments.vti')
assert new.get_comments() == ['this is a comment'] assert new.comments == ['this is a comment']
@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) polyData = VTK.from_poly_data(points).add(points,'coordinates')
polyData.add(points,'coordinates')
if update: if update:
polyData.save(ref_path/'polyData') polyData.save(ref_path/'polyData')
else: else:
reference = VTK.load(ref_path/'polyData.vtp') reference = VTK.load(ref_path/'polyData.vtp')
assert polyData.__repr__() == reference.__repr__() and \ assert polyData.as_ASCII() == reference.as_ASCII() and \
np.allclose(polyData.get('coordinates'),points) np.allclose(polyData.get('coordinates'),points)
@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_rectilinearGrid(self,update,ref_path,tmp_path): def test_compare_reference_rectilinearGrid(self,update,ref_path,tmp_path):
cells = np.array([5,6,7],int) grid = [np.arange(4)**2.,
size = np.array([.6,1.,.5]) np.arange(5)**2.,
rectilinearGrid = VTK.from_rectilinear_grid(cells,size) np.arange(6)**2.] # ParaView renders tetrahedral meshing unless using float coordinates!
c = grid_filters.coordinates0_point(cells,size).reshape(-1,3,order='F') coords = np.stack(np.meshgrid(*grid,indexing='ij'),axis=-1)
n = grid_filters.coordinates0_node(cells,size).reshape(-1,3,order='F') c = coords[:-1,:-1,:-1,:].reshape(-1,3,order='F')
rectilinearGrid.add(np.ascontiguousarray(c),'cell') n = coords[:,:,:,:].reshape(-1,3,order='F')
rectilinearGrid.add(np.ascontiguousarray(n),'node') rectilinearGrid = VTK.from_rectilinear_grid(grid) \
.add(np.ascontiguousarray(c),'cell') \
.add(np.ascontiguousarray(n),'node')
if update: if update:
rectilinearGrid.save(ref_path/'rectilinearGrid') rectilinearGrid.save(ref_path/'rectilinearGrid')
else: else:
reference = VTK.load(ref_path/'rectilinearGrid.vtr') reference = VTK.load(ref_path/'rectilinearGrid.vtr')
assert rectilinearGrid.__repr__() == reference.__repr__() and \ assert rectilinearGrid.as_ASCII() == reference.as_ASCII() and \
np.allclose(rectilinearGrid.get('cell'),c) np.allclose(rectilinearGrid.get('cell'),c)