From 2ce464c48ea1263dca3e387e2b5e9014b789b3ab Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Mon, 14 Feb 2022 09:19:09 -0500 Subject: [PATCH 01/26] vtk.comments as directly accessed property --- python/damask/_grid.py | 10 +++++----- python/damask/_result.py | 4 ++-- python/damask/_vtk.py | 30 +++++++++++------------------- python/tests/test_Result.py | 2 +- python/tests/test_VTK.py | 4 ++-- 5 files changed, 21 insertions(+), 29 deletions(-) diff --git a/python/damask/_grid.py b/python/damask/_grid.py index 1bf8ff386..ec4a3854c 100644 --- a/python/damask/_grid.py +++ b/python/damask/_grid.py @@ -173,8 +173,8 @@ class Grid: Parameters ---------- fname : str or pathlib.Path - Grid file to read. Valid extension is .vti, which will be appended - if not given. + Grid file to read. + Valid extension is .vti, which will be appended if not given. Returns ------- @@ -183,14 +183,14 @@ class Grid: """ v = VTK.load(fname if str(fname).endswith(('.vti','.vtr')) else str(fname)+'.vti') # compatibility hack - comments = v.get_comments() cells = np.array(v.vtk_data.GetDimensions())-1 bbox = np.array(v.vtk_data.GetBounds()).reshape(3,2).T + comments = v.comments return Grid(material = v.get('material').reshape(cells,order='F'), size = bbox[1] - bbox[0], origin = bbox[0], - comments=comments) + comments = comments) @typing. no_type_check @@ -645,7 +645,7 @@ class Grid: """ v = VTK.from_image_data(self.cells,self.size,self.origin) v.add(self.material.flatten(order='F'),'material') - v.add_comments(self.comments) + v.comments += self.comments v.save(fname,parallel=False,compress=compress) diff --git a/python/damask/_result.py b/python/damask/_result.py index de51eb611..9b18f5483 100644 --- a/python/damask/_result.py +++ b/python/damask/_result.py @@ -1623,7 +1623,7 @@ class Result: else: 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 @@ -1639,7 +1639,7 @@ class Result: if self.version_minor >= 13: creator = f.attrs['creator'] if h5py3 else f.attrs['creator'].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']): diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index 3d9e5011c..49c7d5d79 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -2,7 +2,7 @@ import os import warnings import multiprocessing as mp from pathlib import Path -from typing import Union, Literal, List +from typing import Union, Literal, List, Sequence import numpy as np import vtk @@ -386,7 +386,8 @@ class VTK: raise ValueError(f'Array "{label}" not found.') - def get_comments(self) -> List[str]: + @property + def comments(self) -> List[str]: """Return the comments.""" fielddata = self.vtk_data.GetFieldData() for a in range(fielddata.GetNumberOfArrays()): @@ -395,9 +396,9 @@ class VTK: return [comments.GetValue(i) for i in range(comments.GetNumberOfValues())] return [] - - def set_comments(self, - comments: Union[str, List[str]]): + @comments.setter + def comments(self, + comments: Union[str, Sequence[str]]): """ Set comments. @@ -407,6 +408,11 @@ class VTK: Comments. """ + if isinstance(comments,list): + i = 0 + while -i < len(comments) and len(comments[i-1]) == 1: i -= 1 # repack any trailing characters + comments = comments[:i] + [''.join(comments[i:])] # that resulted from autosplitting of str to list + s = vtk.vtkStringArray() s.SetName('comments') for c in [comments] if isinstance(comments,str) else comments: @@ -414,20 +420,6 @@ class VTK: 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() diff --git a/python/tests/test_Result.py b/python/tests/test_Result.py index 71a4ce802..18e09513f 100644 --- a/python/tests/test_Result.py +++ b/python/tests/test_Result.py @@ -385,7 +385,7 @@ class TestResult: result.export_VTK(output,parallel=False) fname = fname.split('.')[0]+f'_inc{(inc if type(inc) == int else inc[0]):0>2}.vti' v = VTK.load(tmp_path/fname) - v.set_comments('n/a') + v.comments = 'n/a' v.save(tmp_path/fname,parallel=False) with open(fname) as f: cur = hashlib.md5(f.read().encode()).hexdigest() diff --git a/python/tests/test_VTK.py b/python/tests/test_VTK.py index 9f0dcc7cf..8f1dc4d3d 100644 --- a/python/tests/test_VTK.py +++ b/python/tests/test_VTK.py @@ -162,10 +162,10 @@ class TestVTK: 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) 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') def test_compare_reference_polyData(self,update,ref_path,tmp_path): From 18f9ac7d44bd2b9b2ce80177b4ac071669a00e6e Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Mon, 14 Feb 2022 12:11:01 -0500 Subject: [PATCH 02/26] outsource tail-repacking of characters into string (util.tail_repack) --- python/damask/_vtk.py | 7 +------ python/damask/util.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index 49c7d5d79..16b72e98d 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -408,14 +408,9 @@ class VTK: Comments. """ - if isinstance(comments,list): - i = 0 - while -i < len(comments) and len(comments[i-1]) == 1: i -= 1 # repack any trailing characters - comments = comments[:i] + [''.join(comments[i:])] # that resulted from autosplitting of str to list - s = vtk.vtkStringArray() s.SetName('comments') - for c in [comments] if isinstance(comments,str) else comments: + for c in util.tail_repack(comments,self.comments): s.InsertNextValue(c) self.vtk_data.GetFieldData().AddArray(s) diff --git a/python/damask/util.py b/python/damask/util.py index fc3f8ea8f..96c6f46a6 100644 --- a/python/damask/util.py +++ b/python/damask/util.py @@ -33,7 +33,8 @@ __all__=[ 'extend_docstring', 'extended_docstring', 'Bravais_to_Miller', 'Miller_to_Bravais', '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 @@ -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 #################################################################################################### From 6916160ed1977979bf89b8b8c7182cb0b236172b Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Tue, 15 Feb 2022 16:38:02 -0500 Subject: [PATCH 03/26] implemented VTK.add(damask.Table) --- python/damask/_table.py | 2 +- python/damask/_vtk.py | 53 +++++++++++++++++++++++++++------------- python/tests/test_VTK.py | 18 ++++++++++++++ 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/python/damask/_table.py b/python/damask/_table.py index 1572c4f76..e592a5c7c 100644 --- a/python/damask/_table.py +++ b/python/damask/_table.py @@ -326,7 +326,7 @@ class Table: @property - def labels(self) -> List[Tuple[int, ...]]: + def labels(self) -> List[str]: return list(self.shapes) diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index 16b72e98d..56ad9f35c 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -38,6 +38,18 @@ class VTK: self.vtk_data = vtk_data + @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() + + @staticmethod def from_image_data(cells: IntSequence, size: FloatSequence, @@ -295,7 +307,7 @@ class VTK: # Check https://blog.kitware.com/ghost-and-blanking-visibility-changes/ for missing data # Needs support for damask.Table def add(self, - data: Union[np.ndarray, np.ma.MaskedArray], + data: Union[np.ndarray, np.ma.MaskedArray, 'Table'], label: str = None): """ Add data to either cells or points. @@ -309,20 +321,17 @@ class VTK: Data label. """ - N_points = self.vtk_data.GetNumberOfPoints() - N_cells = self.vtk_data.GetNumberOfCells() - if isinstance(data,np.ndarray): - if label is None: - raise ValueError('No label defined for numpy.ndarray') + def _add_array(self, + data: np.ndarray, + label: str): - N_data = data.shape[0] - data_ = (data if not isinstance(data,np.ma.MaskedArray) else - np.where(data.mask,data.fill_value,data)).reshape(N_data,-1) + N_data = data.shape[0] - if data_.dtype in [np.double,np.longdouble]: - d = np_to_vtk(data_.astype(np.single),deep=True) # avoid large files - elif data_.dtype.type is np.str_: + data_ = data.reshape(N_data,-1)\ + .astype(np.single if data.dtype in [np.double,np.longdouble] else data.dtype) + + if data.dtype.type is np.str_: d = vtk.vtkStringArray() for s in np.squeeze(data_): d.InsertNextValue(s) @@ -331,14 +340,24 @@ class VTK: d.SetName(label) - if N_data == N_points: + if N_data == self.N_points: self.vtk_data.GetPointData().AddArray(d) - elif N_data == N_cells: + elif N_data == self.N_cells: self.vtk_data.GetCellData().AddArray(d) 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})') + + + if isinstance(data,np.ndarray): + if label is not None: + _add_array(self, + 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): - raise NotImplementedError('damask.Table') + for l in data.labels: + _add_array(self,data.get(l),l) else: raise TypeError @@ -383,7 +402,7 @@ class VTK: # string array return np.array([vtk_array.GetValue(i) for i in range(vtk_array.GetNumberOfValues())]).astype(str) except UnboundLocalError: - raise ValueError(f'Array "{label}" not found.') + raise ValueError(f'Array "{label}" not found') @property diff --git a/python/tests/test_VTK.py b/python/tests/test_VTK.py index 8f1dc4d3d..40bfbef23 100644 --- a/python/tests/test_VTK.py +++ b/python/tests/test_VTK.py @@ -7,7 +7,9 @@ import numpy as np import numpy.ma as ma import vtk +from collections import defaultdict from damask import VTK +from damask import Table from damask import grid_filters @pytest.fixture @@ -152,6 +154,22 @@ class TestVTK: assert (np.squeeze(data.reshape(N_values,-1)) == default.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 = defaultdict(dict) + for k,s in shapes.items(): + d[k]['shape'] = s + d[k]['data'] = np.random.random(N*np.prod(s)).reshape((N,-1)) + t = Table(np.column_stack([d[k]['data'] for k in shapes.keys()]),shapes) + default.add(t) + for k,s in shapes.items(): + assert np.allclose(default.get(k).reshape((N,-1)),d[k]['data'], + rtol=1e-7) + + def test_add_masked(self,default): data = np.random.rand(5*6*7,3) masked = ma.MaskedArray(data,mask=data<.4,fill_value=42.) From e2d1ffe7b823eb18a05bd838b1bad074556a9072 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Tue, 15 Feb 2022 16:42:17 -0500 Subject: [PATCH 04/26] condensed add_table test --- python/tests/test_VTK.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/python/tests/test_VTK.py b/python/tests/test_VTK.py index 40bfbef23..be3e8d6f3 100644 --- a/python/tests/test_VTK.py +++ b/python/tests/test_VTK.py @@ -7,7 +7,6 @@ import numpy as np import numpy.ma as ma import vtk -from collections import defaultdict from damask import VTK from damask import Table from damask import grid_filters @@ -159,12 +158,11 @@ class TestVTK: {'tensor':(3,3),'scalar':(1,)}]) def test_add_table(self,default,shapes): N = np.random.choice([default.N_points,default.N_cells]) - d = defaultdict(dict) + d = dict() for k,s in shapes.items(): - d[k]['shape'] = s - d[k]['data'] = np.random.random(N*np.prod(s)).reshape((N,-1)) - t = Table(np.column_stack([d[k]['data'] for k in shapes.keys()]),shapes) - default.add(t) + d[k] = dict(shape = s, + data = np.random.random(N*np.prod(s)).reshape((N,-1))) + default.add(Table(np.column_stack([d[k]['data'] for k in shapes.keys()]),shapes)) for k,s in shapes.items(): assert np.allclose(default.get(k).reshape((N,-1)),d[k]['data'], rtol=1e-7) From a8898163cec9e737ac8c7e809de73726356e4702 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Tue, 15 Feb 2022 16:48:24 -0500 Subject: [PATCH 05/26] polishing of error message styles --- python/damask/_vtk.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index 56ad9f35c..eef1d774c 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -218,7 +218,7 @@ class VTK: reader = vtk.vtkGenericDataObjectReader() reader.SetFileName(str(fname)) 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')): reader.Update() vtk_data = reader.GetStructuredPointsOutput() @@ -232,7 +232,7 @@ class VTK: reader.Update() vtk_data = reader.GetPolyDataOutput() else: - raise TypeError(f'Unknown dataset type {dataset_type} for vtk file') + raise TypeError(f'Unknown dataset type "{dataset_type}" for vtk file') else: if ext == '.vti': reader = vtk.vtkXMLImageDataReader() @@ -243,7 +243,7 @@ class VTK: elif ext == '.vtp': reader = vtk.vtkXMLPolyDataReader() else: - raise TypeError(f'Unknown file extension {ext}') + raise TypeError(f'Unknown file extension "{ext}"') reader.SetFileName(str(fname)) reader.Update() From c5f286512155bac7aefe5750f642c0d8becb0b3f Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Tue, 15 Feb 2022 18:04:21 -0500 Subject: [PATCH 06/26] make apparent that vtk.get automatically "squeezes" --- python/tests/test_VTK.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/tests/test_VTK.py b/python/tests/test_VTK.py index be3e8d6f3..69119ff12 100644 --- a/python/tests/test_VTK.py +++ b/python/tests/test_VTK.py @@ -164,8 +164,7 @@ class TestVTK: data = np.random.random(N*np.prod(s)).reshape((N,-1))) default.add(Table(np.column_stack([d[k]['data'] for k in shapes.keys()]),shapes)) for k,s in shapes.items(): - assert np.allclose(default.get(k).reshape((N,-1)),d[k]['data'], - rtol=1e-7) + assert np.allclose(np.squeeze(d[k]['data']),default.get(k),rtol=1e-7) def test_add_masked(self,default): From 38ac791b58e6de5517ea5cf2afb157b5c060559c Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Thu, 17 Feb 2022 17:54:26 -0500 Subject: [PATCH 07/26] proper understanding of a rectiliniearGrid --- python/damask/_vtk.py | 16 ++++------ .../tests/reference/VTK/rectilinearGrid.vtr | 30 +++++++++---------- python/tests/test_VTK.py | 19 ++++++------ 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index eef1d774c..b46aca542 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -83,20 +83,14 @@ class VTK: @staticmethod - def from_rectilinear_grid(grid: np.ndarray, - size: FloatSequence, - origin: FloatSequence = np.zeros(3)) -> 'VTK': + def from_rectilinear_grid(grid: FloatSequence) -> '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. + grid : iterables of floats, len (3) + Grid coordinates along x, y, and z directions. Returns ------- @@ -106,8 +100,8 @@ class VTK: """ 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]] + 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]) diff --git a/python/tests/reference/VTK/rectilinearGrid.vtr b/python/tests/reference/VTK/rectilinearGrid.vtr index 8d39f6e16..9931748a8 100644 --- a/python/tests/reference/VTK/rectilinearGrid.vtr +++ b/python/tests/reference/VTK/rectilinearGrid.vtr @@ -1,42 +1,42 @@ - - + + - - 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 0 - 1.2688577653 + 31.016124839 - - 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 - 0.10871961652 + 0 - 1.1607924233 + 18.788294228 - - AQAAAACAAAAwAAAAJwAAAA==eF5jYICAHXKtrwN37LOH0Ofsua4vLrDlug7l37M3BoPH9gCdQxK6 + + AQAAAACAAAAgAAAAFQAAAA==eF5jYEAGH+whtIADhFZyAAAYkwHi - - AQAAAACAAAA4AAAAIgAAAA==eF5jYICAUDA4ag+hr9pDRB9A+U/tV4HBK6j4B3sAk7wQqg== + + AQAAAACAAAAoAAAAFwAAAA==eF5jYEAGH+whtIADhFaC0gYOAChDAlI= - - AQAAAACAAABAAAAALAAAAA==eF5jYICASSqeQLTJHkIfsr+9LReITkP5l+zB3NvXoOK37SG6HtgDANusGUo= + + AQAAAACAAAAwAAAAGgAAAA==eF5jYEAGH+whtIADhFaC0gZQ2tIBADuFAss= diff --git a/python/tests/test_VTK.py b/python/tests/test_VTK.py index 69119ff12..b3dfc6cd4 100644 --- a/python/tests/test_VTK.py +++ b/python/tests/test_VTK.py @@ -34,10 +34,9 @@ class TestVTK: default.show() def test_rectilinearGrid(self,tmp_path): - cells = np.random.randint(5,10,3)*2 - size = np.random.random(3) + 1.0 - origin = np.random.random(3) - v = VTK.from_rectilinear_grid(cells,size,origin) + grid = np.sort(np.random.random((3,10))) + print(grid) + v = VTK.from_rectilinear_grid(grid) string = v.__repr__() v.save(tmp_path/'rectilinearGrid',False) vtr = VTK.load(tmp_path/'rectilinearGrid.vtr') @@ -196,11 +195,13 @@ class TestVTK: @pytest.mark.xfail(int(vtk.vtkVersion.GetVTKVersion().split('.')[0])<8, reason='missing METADATA') def test_compare_reference_rectilinearGrid(self,update,ref_path,tmp_path): - cells = np.array([5,6,7],int) - size = np.array([.6,1.,.5]) - rectilinearGrid = VTK.from_rectilinear_grid(cells,size) - c = grid_filters.coordinates0_point(cells,size).reshape(-1,3,order='F') - n = grid_filters.coordinates0_node(cells,size).reshape(-1,3,order='F') + grid = [np.arange(4)**2., + np.arange(5)**2., + np.arange(6)**2.] # ParaView renders tetrahedral meshing unless using float coordinates! + rectilinearGrid = VTK.from_rectilinear_grid(grid) + coords = np.stack(np.meshgrid(*grid,indexing='ij'),axis=-1) + c = coords[:-1,:-1,:-1,:].reshape(-1,3,order='F') + n = coords[:,:,:,:].reshape(-1,3,order='F') rectilinearGrid.add(np.ascontiguousarray(c),'cell') rectilinearGrid.add(np.ascontiguousarray(n),'node') if update: From 8e19e93b39c551969b19fa15a49b8ed49fdf9186 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Thu, 17 Feb 2022 18:49:45 -0500 Subject: [PATCH 08/26] polishing of doc strings --- python/damask/_vtk.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index b46aca542..d8cdbab00 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -121,7 +121,7 @@ class VTK: Parameters ---------- - nodes : numpy.ndarray of shape (:,3) + nodes : numpy.ndarray, shape (:,3) Spatial position of the nodes. connectivity : numpy.ndarray of np.dtype = int Cell connectivity (0-based), first dimension determines #Cells, @@ -161,7 +161,7 @@ class VTK: Parameters ---------- - points : numpy.ndarray of shape (:,3) + points : numpy.ndarray, shape (:,3) Spatial position of the points. Returns @@ -308,10 +308,10 @@ class VTK: 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 number of cells or number of points. - label : str + label : str, optional if data is damask.Table Data label. """ From e602653b3b0ab80be335904ee95f953925a3869d Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Thu, 17 Feb 2022 18:50:53 -0500 Subject: [PATCH 09/26] remove obsolete grid_filter import --- python/tests/test_VTK.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/tests/test_VTK.py b/python/tests/test_VTK.py index b3dfc6cd4..0836c3efd 100644 --- a/python/tests/test_VTK.py +++ b/python/tests/test_VTK.py @@ -9,7 +9,6 @@ import vtk from damask import VTK from damask import Table -from damask import grid_filters @pytest.fixture def ref_path(ref_path_base): From 75f535b60cc81163a6590662273d64d941be40fd Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Fri, 18 Feb 2022 11:45:21 -0500 Subject: [PATCH 10/26] welcome back rectilineargrid... --- python/damask/_vtk.py | 76 +++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index d8cdbab00..d32751e8d 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -1,5 +1,4 @@ import os -import warnings import multiprocessing as mp from pathlib import Path from typing import Union, Literal, List, Sequence @@ -82,34 +81,6 @@ class VTK: 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. - - """ - warnings.warn('Support for vtr files will be removed in DAMASK 3.1.0', DeprecationWarning,2) - 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 def from_unstructured_grid(nodes: np.ndarray, connectivity: np.ndarray, @@ -186,9 +157,36 @@ class VTK: 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 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. @@ -196,8 +194,8 @@ class VTK: ---------- fname : str or pathlib.Path Filename for reading. - Valid extensions are .vti, .vtr, .vtu, .vtp, and .vtk. - dataset_type : {'ImageData', 'UnstructuredGrid', 'PolyData'}, optional + Valid extensions are .vti, .vtu, .vtp, .vtr, and .vtk. + dataset_type : {'ImageData', 'UnstructuredGrid', 'PolyData', 'RectilinearGrid'}, optional Name of the vtk.vtkDataSet subclass when opening a .vtk file. Returns @@ -216,26 +214,26 @@ class VTK: elif dataset_type.lower().endswith(('imagedata','image_data')): reader.Update() 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')): reader.Update() vtk_data = reader.GetUnstructuredGridOutput() elif dataset_type.lower().endswith(('polydata','poly_data')): reader.Update() vtk_data = reader.GetPolyDataOutput() + elif dataset_type.lower().endswith(('rectilineargrid','rectilinear_grid')): + reader.Update() + vtk_data = reader.GetRectilinearGridOutput() else: raise TypeError(f'Unknown dataset type "{dataset_type}" for vtk file') else: if ext == '.vti': reader = vtk.vtkXMLImageDataReader() - elif ext == '.vtr': - reader = vtk.vtkXMLRectilinearGridReader() elif ext == '.vtu': reader = vtk.vtkXMLUnstructuredGridReader() elif ext == '.vtp': reader = vtk.vtkXMLPolyDataReader() + elif ext == '.vtr': + reader = vtk.vtkXMLRectilinearGridReader() else: raise TypeError(f'Unknown file extension "{ext}"') @@ -270,12 +268,12 @@ class VTK: """ if isinstance(self.vtk_data,vtk.vtkImageData): writer = vtk.vtkXMLImageDataWriter() - elif isinstance(self.vtk_data,vtk.vtkRectilinearGrid): - writer = vtk.vtkXMLRectilinearGridWriter() elif isinstance(self.vtk_data,vtk.vtkUnstructuredGrid): writer = vtk.vtkXMLUnstructuredGridWriter() elif isinstance(self.vtk_data,vtk.vtkPolyData): writer = vtk.vtkXMLPolyDataWriter() + elif isinstance(self.vtk_data,vtk.vtkRectilinearGrid): + writer = vtk.vtkXMLRectilinearGridWriter() default_ext = '.'+writer.GetDefaultFileExtension() ext = Path(fname).suffix From fe2ab947eccebb3b077b0eccead3ff6f2b3c0c2b Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Fri, 18 Feb 2022 14:50:28 -0500 Subject: [PATCH 11/26] somehow "Sequence" was missing from import --- python/damask/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/damask/util.py b/python/damask/util.py index ecfbd1a83..c6550edde 100644 --- a/python/damask/util.py +++ b/python/damask/util.py @@ -9,7 +9,7 @@ import re import fractions from collections import abc 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 import numpy as np From 9fe51686e58a9e217d183cd4a7344fa93513cf65 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 21 Feb 2022 07:13:19 +0100 Subject: [PATCH 12/26] allow to use '~' in path not sure how to test, i.e. how to create a fixture to have a fake home --- python/damask/_colormap.py | 2 +- python/damask/_result.py | 2 +- python/damask/_vtk.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index 005cf80d6..83e092c19 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -328,7 +328,7 @@ class Colormap(mpl.colors.ListedColormap): if fname is None: return open(self.name.replace(' ','_')+suffix, 'w', newline='\n') elif isinstance(fname, (str, Path)): - return open(fname, 'w', newline='\n') + return open(Path(fname).expanduser(), 'w', newline='\n') else: return fname diff --git a/python/damask/_result.py b/python/damask/_result.py index fc98e4dac..6d0bf2ac3 100644 --- a/python/damask/_result.py +++ b/python/damask/_result.py @@ -154,7 +154,7 @@ class Result: 'fields': self.fields, } - self.fname = Path(fname).absolute() + self.fname = Path(fname).expanduser().absolute() self._protected = True diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index d32751e8d..c7a9dd47f 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -208,7 +208,7 @@ class VTK: raise FileNotFoundError(f'No such file: {fname}') if (ext := Path(fname).suffix) == '.vtk' or dataset_type is not None: reader = vtk.vtkGenericDataObjectReader() - reader.SetFileName(str(fname)) + reader.SetFileName(str(Path(fname).expanduser())) if dataset_type is None: raise TypeError('Dataset type for *.vtk file not given') elif dataset_type.lower().endswith(('imagedata','image_data')): @@ -237,7 +237,7 @@ class VTK: else: raise TypeError(f'Unknown file extension "{ext}"') - reader.SetFileName(str(fname)) + reader.SetFileName(str(Path(fname).expanduser())) reader.Update() vtk_data = reader.GetOutput() @@ -277,7 +277,7 @@ class VTK: default_ext = '.'+writer.GetDefaultFileExtension() 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: writer.SetCompressorTypeToZLib() From 45dc9cf7a6c288d1cc5cddef8074d63ec5b62401 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 21 Feb 2022 07:27:35 +0100 Subject: [PATCH 13/26] show scalar data, use to show material of grid --- python/damask/_grid.py | 4 +++- python/damask/_vtk.py | 12 +++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/python/damask/_grid.py b/python/damask/_grid.py index 07acc27e7..68e93d81c 100644 --- a/python/damask/_grid.py +++ b/python/damask/_grid.py @@ -683,7 +683,9 @@ class Grid: def show(self) -> None: """Show on screen.""" - VTK.from_image_data(self.cells,self.size,self.origin).show() + v = VTK.from_image_data(self.cells,self.size,self.origin) + v.add(self.material.flatten(),'material') + v.show('material') def add_primitive(self, diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index c7a9dd47f..ff989ff1a 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -12,6 +12,7 @@ from vtk.util.numpy_support import vtk_to_numpy as vtk_to_np from ._typehints import FloatSequence, IntSequence from . import util from . import Table +from . import Colormap class VTK: @@ -436,7 +437,7 @@ class VTK: return writer.GetOutputString() - def show(self): + def show(self,label=None,colormap=Colormap.from_predefined('strain')): """ Render. @@ -457,8 +458,17 @@ class VTK: width = 1024 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.SetInputData(self.vtk_data) + mapper.SetLookupTable(lut) + mapper.SetScalarRange(self.vtk_data.GetScalarRange()) actor = vtk.vtkActor() actor.SetMapper(mapper) From 22fa9c9772a8442b478cac6346924c9d22d393ad Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 21 Feb 2022 11:19:53 +0100 Subject: [PATCH 14/26] add scalebar --- python/damask/_grid.py | 16 +++++++++++++--- python/damask/_vtk.py | 16 +++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/python/damask/_grid.py b/python/damask/_grid.py index 68e93d81c..7fef8be6f 100644 --- a/python/damask/_grid.py +++ b/python/damask/_grid.py @@ -17,6 +17,7 @@ from . import util from . import grid_filters from . import Rotation from . import Table +from . import Colormap from ._typehints import FloatSequence, IntSequence class Grid: @@ -681,11 +682,20 @@ class Grid: header='\n'.join(header), fmt=format_string, comments='') - def show(self) -> None: - """Show on screen.""" + def show(self, + colormap: Colormap = Colormap.from_predefined('viridis')) -> None: + """ + Show on screen. + + Parameters + ---------- + colormap : damask.Colormap + Colors used to map material IDs. + + """ v = VTK.from_image_data(self.cells,self.size,self.origin) v.add(self.material.flatten(),'material') - v.show('material') + v.show('material',colormap) def add_primitive(self, diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index ff989ff1a..ff04560ac 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -205,7 +205,7 @@ class VTK: VTK-based geometry from file. """ - if not os.path.isfile(fname): # vtk has a strange error handling + if not os.path.isfile(os.path.expanduser(fname)): # vtk has a strange error handling raise FileNotFoundError(f'No such file: {fname}') if (ext := Path(fname).suffix) == '.vtk' or dataset_type is not None: reader = vtk.vtkGenericDataObjectReader() @@ -437,7 +437,9 @@ class VTK: return writer.GetOutputString() - def show(self,label=None,colormap=Colormap.from_predefined('strain')): + def show(self, + label: str = None, + colormap: Colormap = Colormap.from_predefined('viridis')): """ Render. @@ -476,7 +478,15 @@ class VTK: ren = vtk.vtkRenderer() ren.AddActor(actor) - ren.SetBackground(67/255,128/255,208/255) + if label is None: + 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.AddRenderer(ren) From c975a5b437abd6629c4efa6f99bbf60d7bc2d3b9 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 21 Feb 2022 12:12:17 +0100 Subject: [PATCH 15/26] needed for out of place behavior pickle/deepcopy does not work --- python/damask/_vtk.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index ff04560ac..d9d8c8f1b 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -38,6 +38,23 @@ class VTK: self.vtk_data = vtk_data + 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 N_points(self) -> int: """Number of points in vtkdata.""" From e8a4f06634c9c0df69cb9ceee7daffb6677675d1 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 21 Feb 2022 12:17:00 +0100 Subject: [PATCH 16/26] out of place behavior for VTK --- python/damask/_grid.py | 8 ++++---- python/damask/_result.py | 4 ++-- python/damask/_table.py | 2 +- python/damask/_vtk.py | 27 ++++++++++++++------------- python/tests/test_VTK.py | 24 +++++++++++------------- 5 files changed, 32 insertions(+), 33 deletions(-) diff --git a/python/damask/_grid.py b/python/damask/_grid.py index 7fef8be6f..b4c0f0857 100644 --- a/python/damask/_grid.py +++ b/python/damask/_grid.py @@ -644,8 +644,8 @@ class Grid: Compress with zlib algorithm. Defaults to True. """ - v = VTK.from_image_data(self.cells,self.size,self.origin) - v.add(self.material.flatten(order='F'),'material') + v = VTK.from_image_data(self.cells,self.size,self.origin)\ + .add(self.material.flatten(order='F'),'material') v.comments += self.comments v.save(fname,parallel=False,compress=compress) @@ -693,8 +693,8 @@ class Grid: Colors used to map material IDs. """ - v = VTK.from_image_data(self.cells,self.size,self.origin) - v.add(self.material.flatten(),'material') + v = VTK.from_image_data(self.cells,self.size,self.origin) \ + .add(self.material.flatten(),'material') v.show('material',colormap) diff --git a/python/damask/_result.py b/python/damask/_result.py index 6d0bf2ac3..6ea41863e 100644 --- a/python/damask/_result.py +++ b/python/damask/_result.py @@ -1656,7 +1656,7 @@ class Result: for inc in util.show_progress(self.visible['increments']): 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 field in self.visible['fields']: @@ -1683,7 +1683,7 @@ class Result: outs[out][at_cell_ho[label]] = data[in_data_ho[label]] 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) diff --git a/python/damask/_table.py b/python/damask/_table.py index 89f98c442..9ba48596e 100644 --- a/python/damask/_table.py +++ b/python/damask/_table.py @@ -589,7 +589,7 @@ class Table: labels += [f'{util.srepr(self.shapes[l],"x")}:{i+1}_{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') self.data.to_csv(f,sep=' ',na_rep='nan',index=False,header=False) diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index d9d8c8f1b..1336cc996 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -42,11 +42,11 @@ class VTK: if isinstance(self.vtk_data,vtk.vtkImageData): dup = vtk.vtkImageData() elif isinstance(self.vtk_data,vtk.vtkUnstructuredGrid): - dup = vtk.vtkUnstructuredGrid + dup = vtk.vtkUnstructuredGrid() elif isinstance(self.vtk_data,vtk.vtkPolyData): - dup = vtk.vtkPolyData + dup = vtk.vtkPolyData() elif isinstance(self.vtk_data,vtk.vtkRectilinearGrid): - dup = vtk.vtkRectilinearGrid + dup = vtk.vtkRectilinearGrid() else: raise TypeError @@ -222,7 +222,7 @@ class VTK: VTK-based geometry from file. """ - if not os.path.isfile(os.path.expanduser(fname)): # vtk has a strange error handling + if not Path(fname).expanduser().is_file(): # vtk has a strange error handling raise FileNotFoundError(f'No such file: {fname}') if (ext := Path(fname).suffix) == '.vtk' or dataset_type is not None: reader = vtk.vtkGenericDataObjectReader() @@ -315,7 +315,6 @@ class VTK: # Check https://blog.kitware.com/ghost-and-blanking-visibility-changes/ for missing data - # Needs support for damask.Table def add(self, data: Union[np.ndarray, np.ma.MaskedArray, 'Table'], label: str = None): @@ -332,7 +331,7 @@ class VTK: """ - def _add_array(self, + def _add_array(vtk_data, data: np.ndarray, label: str): @@ -350,27 +349,29 @@ class VTK: d.SetName(label) - if N_data == self.N_points: - self.vtk_data.GetPointData().AddArray(d) - elif N_data == self.N_cells: - self.vtk_data.GetCellData().AddArray(d) + if N_data == vtk_data.GetNumberOfPoints(): + vtk_data.GetPointData().AddArray(d) + elif N_data == vtk_data.GetNumberOfCells(): + vtk_data.GetCellData().AddArray(d) else: 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(self, + _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): for l in data.labels: - _add_array(self,data.get(l),l) + _add_array(dup.vtk_data,data.get(l),l) else: raise TypeError + return dup + def get(self, label: str) -> np.ndarray: diff --git a/python/tests/test_VTK.py b/python/tests/test_VTK.py index 0836c3efd..9613a483b 100644 --- a/python/tests/test_VTK.py +++ b/python/tests/test_VTK.py @@ -147,8 +147,8 @@ class TestVTK: @pytest.mark.parametrize('N_values',[5*6*7,6*7*8]) 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) - default.add(data,'data') - assert (np.squeeze(data.reshape(N_values,-1)) == default.get('data')).all() + new = default.add(data,'data') + assert (np.squeeze(data.reshape(N_values,-1)) == new.get('data')).all() @pytest.mark.parametrize('shapes',[{'scalar':(1,),'vector':(3,),'tensor':(3,3)}, @@ -160,18 +160,17 @@ class TestVTK: for k,s in shapes.items(): d[k] = dict(shape = s, data = np.random.random(N*np.prod(s)).reshape((N,-1))) - default.add(Table(np.column_stack([d[k]['data'] for k in shapes.keys()]),shapes)) + 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']),default.get(k),rtol=1e-7) + assert np.allclose(np.squeeze(d[k]['data']),new.get(k),rtol=1e-7) def test_add_masked(self,default): data = np.random.rand(5*6*7,3) masked = ma.MaskedArray(data,mask=data<.4,fill_value=42.) - default.add(masked,'D') - result_masked = str(default) - default.add(np.where(masked.mask,masked.fill_value,masked),'D') - assert result_masked == str(default) + mask_auto = default.add(masked,'D') + mask_manual = default.add(np.where(masked.mask,masked.fill_value,masked),'D') + assert str(mask_manual) == str(mask_auto) def test_comments(self,tmp_path,default): @@ -183,8 +182,7 @@ class TestVTK: @pytest.mark.xfail(int(vtk.vtkVersion.GetVTKVersion().split('.')[0])<8, reason='missing METADATA') 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() - polyData = VTK.from_poly_data(points) - polyData.add(points,'coordinates') + polyData = VTK.from_poly_data(points).add(points,'coordinates') if update: polyData.save(ref_path/'polyData') else: @@ -197,12 +195,12 @@ class TestVTK: grid = [np.arange(4)**2., np.arange(5)**2., np.arange(6)**2.] # ParaView renders tetrahedral meshing unless using float coordinates! - rectilinearGrid = VTK.from_rectilinear_grid(grid) coords = np.stack(np.meshgrid(*grid,indexing='ij'),axis=-1) c = coords[:-1,:-1,:-1,:].reshape(-1,3,order='F') n = coords[:,:,:,:].reshape(-1,3,order='F') - rectilinearGrid.add(np.ascontiguousarray(c),'cell') - rectilinearGrid.add(np.ascontiguousarray(n),'node') + rectilinearGrid = VTK.from_rectilinear_grid(grid) \ + .add(np.ascontiguousarray(c),'cell') \ + .add(np.ascontiguousarray(n),'node') if update: rectilinearGrid.save(ref_path/'rectilinearGrid') else: From f6de182eb6fb0a96ba7b5d78c940227a987db622 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 22 Feb 2022 08:08:41 +0100 Subject: [PATCH 17/26] get information about dataset labels --- python/damask/_table.py | 2 +- python/damask/_vtk.py | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/python/damask/_table.py b/python/damask/_table.py index 9ba48596e..cdbecac93 100644 --- a/python/damask/_table.py +++ b/python/damask/_table.py @@ -259,7 +259,7 @@ class Table: 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) comments = [] diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index 1336cc996..80284582c 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -60,12 +60,25 @@ class VTK: """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 def from_image_data(cells: IntSequence, From be11da2284008cc56d22f2f02a0641d5ada148c3 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 22 Feb 2022 14:00:36 +0100 Subject: [PATCH 18/26] testing new functionality --- python/tests/test_Grid.py | 4 ++++ python/tests/test_VTK.py | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/python/tests/test_Grid.py b/python/tests/test_Grid.py index 6dd94f4bb..c453b6c74 100644 --- a/python/tests/test_Grid.py +++ b/python/tests/test_Grid.py @@ -42,6 +42,10 @@ class TestGrid: print('patched datetime.datetime.now') + def test_show(sef,default,monkeypatch): + monkeypatch.delenv('DISPLAY',raising=False) + default.show() + def test_equal(self,default): assert default == default assert not default == 42 diff --git a/python/tests/test_VTK.py b/python/tests/test_VTK.py index 9613a483b..885c9bd95 100644 --- a/python/tests/test_VTK.py +++ b/python/tests/test_VTK.py @@ -1,6 +1,7 @@ import os import filecmp import time +import string import pytest import numpy as np @@ -173,6 +174,22 @@ class TestVTK: assert str(mask_manual) == str(mask_auto) + @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): default.comments += 'this is a comment' default.save(tmp_path/'with_comments',parallel=False) From 661b7fb602e6b422b2c3cecdb8bbc3c600b8ef30 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Tue, 22 Feb 2022 10:56:12 -0500 Subject: [PATCH 19/26] slight polish --- python/damask/_vtk.py | 14 +++++++------- python/tests/test_VTK.py | 1 - 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index 80284582c..aa321cbab 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -236,12 +236,12 @@ class VTK: """ if not Path(fname).expanduser().is_file(): # vtk has a strange error handling - raise FileNotFoundError(f'No such file: {fname}') + raise FileNotFoundError(f'file "{fname}" not found') if (ext := Path(fname).suffix) == '.vtk' or dataset_type is not None: reader = vtk.vtkGenericDataObjectReader() reader.SetFileName(str(Path(fname).expanduser())) 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')): reader.Update() vtk_data = reader.GetStructuredPointsOutput() @@ -255,7 +255,7 @@ class VTK: reader.Update() vtk_data = reader.GetRectilinearGridOutput() else: - raise TypeError(f'Unknown dataset type "{dataset_type}" for vtk file') + raise TypeError(f'unknown dataset type "{dataset_type}" for vtk file') else: if ext == '.vti': reader = vtk.vtkXMLImageDataReader() @@ -266,7 +266,7 @@ class VTK: elif ext == '.vtr': reader = vtk.vtkXMLRectilinearGridReader() else: - raise TypeError(f'Unknown file extension "{ext}"') + raise TypeError(f'unknown file extension "{ext}"') reader.SetFileName(str(Path(fname).expanduser())) reader.Update() @@ -367,7 +367,7 @@ class VTK: elif N_data == vtk_data.GetNumberOfCells(): vtk_data.GetCellData().AddArray(d) 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})') dup = self.copy() if isinstance(data,np.ndarray): @@ -376,7 +376,7 @@ class VTK: 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') + raise ValueError('no label defined for numpy.ndarray') elif isinstance(data,Table): for l in data.labels: _add_array(dup.vtk_data,data.get(l),l) @@ -426,7 +426,7 @@ class VTK: # string array return np.array([vtk_array.GetValue(i) for i in range(vtk_array.GetNumberOfValues())]).astype(str) except UnboundLocalError: - raise ValueError(f'Array "{label}" not found') + raise ValueError(f'array "{label}" not found') @property diff --git a/python/tests/test_VTK.py b/python/tests/test_VTK.py index 885c9bd95..18bcea585 100644 --- a/python/tests/test_VTK.py +++ b/python/tests/test_VTK.py @@ -35,7 +35,6 @@ class TestVTK: def test_rectilinearGrid(self,tmp_path): grid = np.sort(np.random.random((3,10))) - print(grid) v = VTK.from_rectilinear_grid(grid) string = v.__repr__() v.save(tmp_path/'rectilinearGrid',False) From ae2aa096fb34c9d1c480ece680694ac88ed36ffe Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Tue, 22 Feb 2022 12:23:29 -0500 Subject: [PATCH 20/26] fixed flatten order=F; cividis default. --- python/damask/_grid.py | 12 ++++++------ python/damask/_vtk.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/python/damask/_grid.py b/python/damask/_grid.py index b4c0f0857..6ad1f5847 100644 --- a/python/damask/_grid.py +++ b/python/damask/_grid.py @@ -683,19 +683,19 @@ class Grid: def show(self, - colormap: Colormap = Colormap.from_predefined('viridis')) -> None: + colormap: Colormap = Colormap.from_predefined('cividis')) -> None: """ Show on screen. Parameters ---------- - colormap : damask.Colormap - Colors used to map material IDs. + colormap : damask.Colormap, optional + Colors used to map material IDs. Defaults to 'cividis'. """ - v = VTK.from_image_data(self.cells,self.size,self.origin) \ - .add(self.material.flatten(),'material') - v.show('material',colormap) + VTK.from_image_data(self.cells,self.size,self.origin) \ + .add(self.material.flatten('F'),'material') \ + .show('material',colormap) def add_primitive(self, diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index aa321cbab..56d53c2b6 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -470,7 +470,7 @@ class VTK: def show(self, label: str = None, - colormap: Colormap = Colormap.from_predefined('viridis')): + colormap: Colormap = Colormap.from_predefined('cividis')): """ Render. From 59755c67ce4fc432e44081eb55880696a80bc126 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 23 Feb 2022 06:49:38 +0100 Subject: [PATCH 21/26] human-readable reporting --- python/damask/_vtk.py | 37 +++++++++++++++++++++++++++++++++++++ python/tests/test_VTK.py | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index 56d53c2b6..f77fa31dd 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -459,6 +459,43 @@ class VTK: def __repr__(self) -> str: + info = [] + if isinstance(self.vtk_data,vtk.vtkImageData): + info.append('vtkImageData') + elif isinstance(self.vtk_data,vtk.vtkUnstructuredGrid): + info.append('vtkUnstructuredGrid') + elif isinstance(self.vtk_data,vtk.vtkPolyData): + info.append('vtkPolyData') + elif isinstance(self.vtk_data,vtk.vtkRectilinearGrid): + info.append('vtkRectilinearGrid') + + 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.append(f'with {data}:') + info += self.labels[data] + + return util.srepr(info) + + + def __eq__(self, + other: object) -> bool: + """ + Test equality of other. + + Parameters + ---------- + other : damask.VTK + VTK to compare self against. + + """ + if not isinstance(other, VTK): + return NotImplemented + return self.as_ASCII() == other.as_ASCII() + + + def as_ASCII(self) -> str: """ASCII representation of the VTK data.""" writer = vtk.vtkDataSetWriter() writer.SetHeader(f'# {util.execution_stamp("VTK")}') diff --git a/python/tests/test_VTK.py b/python/tests/test_VTK.py index 18bcea585..0681cffe3 100644 --- a/python/tests/test_VTK.py +++ b/python/tests/test_VTK.py @@ -33,27 +33,44 @@ class TestVTK: monkeypatch.delenv('DISPLAY',raising=False) 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 = 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') + print(vtk) + assert (string == vtr.as_ASCII() == vtk.as_ASCII()) + def test_rectilinearGrid(self,tmp_path): grid = np.sort(np.random.random((3,10))) v = VTK.from_rectilinear_grid(grid) - string = v.__repr__() + string = v.as_ASCII() v.save(tmp_path/'rectilinearGrid',False) vtr = VTK.load(tmp_path/'rectilinearGrid.vtr') with open(tmp_path/'rectilinearGrid.vtk','w') as f: f.write(string) vtk = VTK.load(tmp_path/'rectilinearGrid.vtk','VTK_rectilinearGrid') - assert(string == vtr.__repr__() == vtk.__repr__()) + print(vtk) + assert (string == vtr.as_ASCII() == vtk.as_ASCII()) def test_polyData(self,tmp_path): points = np.random.rand(100,3) v = VTK.from_poly_data(points) - string = v.__repr__() + string = v.as_ASCII() v.save(tmp_path/'polyData',False) vtp = VTK.load(tmp_path/'polyData.vtp') with open(tmp_path/'polyData.vtk','w') as f: f.write(string) vtk = VTK.load(tmp_path/'polyData.vtk','polyData') - assert(string == vtp.__repr__() == vtk.__repr__()) + print(vtk) + assert(string == vtp.as_ASCII() == vtk.as_ASCII()) @pytest.mark.parametrize('cell_type,n',[ ('VTK_hexahedron',8), @@ -66,13 +83,14 @@ class TestVTK: nodes = np.random.rand(n,3) connectivity = np.random.choice(np.arange(n),n,False).reshape(-1,n) v = VTK.from_unstructured_grid(nodes,connectivity,cell_type) - string = v.__repr__() + string = v.as_ASCII() v.save(tmp_path/'unstructuredGrid',False) vtu = VTK.load(tmp_path/'unstructuredGrid.vtu') with open(tmp_path/'unstructuredGrid.vtk','w') as f: f.write(string) vtk = VTK.load(tmp_path/'unstructuredGrid.vtk','unstructuredgrid') - assert(string == vtu.__repr__() == vtk.__repr__()) + print(vtk) + assert(string == vtu.as_ASCII() == vtk.as_ASCII()) def test_parallel_out(self,tmp_path): @@ -96,7 +114,7 @@ class TestVTK: fname_p = tmp_path/'plain.vtp' v.save(fname_c,parallel=False,compress=False) 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']) @@ -170,7 +188,7 @@ class TestVTK: masked = ma.MaskedArray(data,mask=data<.4,fill_value=42.) mask_auto = default.add(masked,'D') mask_manual = default.add(np.where(masked.mask,masked.fill_value,masked),'D') - assert str(mask_manual) == str(mask_auto) + assert mask_manual == mask_auto @pytest.mark.parametrize('data_type,shape',[(float,(3,)), @@ -203,7 +221,7 @@ class TestVTK: polyData.save(ref_path/'polyData') else: 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) @pytest.mark.xfail(int(vtk.vtkVersion.GetVTKVersion().split('.')[0])<8, reason='missing METADATA') @@ -221,5 +239,5 @@ class TestVTK: rectilinearGrid.save(ref_path/'rectilinearGrid') else: 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) From 77e57a268c8283c29f132262ee17077ed8402d0a Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 23 Feb 2022 07:20:27 +0100 Subject: [PATCH 22/26] unified. the default python description "Return repr(self)." is pretty useless, so better agree on a DAMASK standard --- python/damask/_crystal.py | 2 +- python/damask/_grid.py | 2 +- python/damask/_orientation.py | 2 +- python/damask/_result.py | 2 +- python/damask/_rotation.py | 2 +- python/damask/_table.py | 2 +- python/damask/_vtk.py | 1 + 7 files changed, 7 insertions(+), 6 deletions(-) diff --git a/python/damask/_crystal.py b/python/damask/_crystal.py index 35977af28..3f89597f6 100644 --- a/python/damask/_crystal.py +++ b/python/damask/_crystal.py @@ -111,7 +111,7 @@ class Crystal(): def __repr__(self): - """Represent.""" + """Give short human-readable summary.""" family = f'Crystal family: {self.family}' return family if self.lattice is None else \ '\n'.join([family, diff --git a/python/damask/_grid.py b/python/damask/_grid.py index 6ad1f5847..6d0538f67 100644 --- a/python/damask/_grid.py +++ b/python/damask/_grid.py @@ -58,7 +58,7 @@ class Grid: def __repr__(self) -> str: - """Basic information on grid definition.""" + """Give short human-readable summary.""" mat_min = np.nanmin(self.material) mat_max = np.nanmax(self.material) mat_N = self.N_materials diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index c7fbbbb85..ea3bbba8b 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -120,7 +120,7 @@ class Orientation(Rotation,Crystal): def __repr__(self) -> str: - """Represent.""" + """Give short human readable summary.""" return '\n'.join([Crystal.__repr__(self), Rotation.__repr__(self)]) diff --git a/python/damask/_result.py b/python/damask/_result.py index 6ea41863e..1089ac315 100644 --- a/python/damask/_result.py +++ b/python/damask/_result.py @@ -167,7 +167,7 @@ class Result: def __repr__(self): - """Show summary of file content.""" + """Give short human-readable summary.""" with h5py.File(self.fname,'r') as f: header = [f'Created by {f.attrs["creator"]}', f' on {f.attrs["created"]}', diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index e99d2c1b5..71d7a837c 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -88,7 +88,7 @@ class Rotation: 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)}'\ + str(self.quaternion) diff --git a/python/damask/_table.py b/python/damask/_table.py index cdbecac93..6715ecc9d 100644 --- a/python/damask/_table.py +++ b/python/damask/_table.py @@ -37,7 +37,7 @@ class Table: def __repr__(self) -> str: - """Brief overview.""" + """Give short human-readable summary.""" self._relabel('shapes') data_repr = self.data.__repr__() self._relabel('uniform') diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index f77fa31dd..3a2355640 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -459,6 +459,7 @@ class VTK: def __repr__(self) -> str: + """Give short human-readable summary.""" info = [] if isinstance(self.vtk_data,vtk.vtkImageData): info.append('vtkImageData') From 5882a8560c234ff92797dfedd56205639ea9bd61 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Wed, 23 Feb 2022 08:37:23 -0500 Subject: [PATCH 23/26] shortened __repr__ code --- python/damask/_orientation.py | 7 +- python/damask/_rotation.py | 2 +- python/damask/_vtk.py | 149 ++++++++++++++++------------------ 3 files changed, 76 insertions(+), 82 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index ea3bbba8b..f3d3f84b7 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -120,9 +120,10 @@ class Orientation(Rotation,Crystal): def __repr__(self) -> str: - """Give short human readable summary.""" - return '\n'.join([Crystal.__repr__(self), - Rotation.__repr__(self)]) + """Give short human-readable summary.""" + return util.srepr([Crystal.__repr__(self), + Rotation.__repr__(self)]) + def __copy__(self: MyType, rotation: Union[FloatSequence, Rotation] = None) -> MyType: diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 71d7a837c..64393737b 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -88,7 +88,7 @@ class Rotation: def __repr__(self) -> str: - """Give short human readable summary.""" + """Give short human-readable summary.""" return f'Quaternion{" " if self.quaternion.shape == (4,) else "s of shape "+str(self.quaternion.shape[:-1])+chr(10)}'\ + str(self.quaternion) diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index 3a2355640..9f73633d5 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -38,6 +38,33 @@ class VTK: 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 compare self against. + + """ + return self.as_ASCII() == other.as_ASCII() if isinstance(other, VTK) else NotImplemented + + def copy(self): if isinstance(self.vtk_data,vtk.vtkImageData): dup = vtk.vtkImageData() @@ -55,16 +82,47 @@ class VTK: 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.""" @@ -80,6 +138,7 @@ class VTK: return labels + @staticmethod def from_image_data(cells: IntSequence, size: FloatSequence, @@ -280,6 +339,17 @@ class VTK: """Wrapper for parallel writing.""" 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, fname: Union[str, Path], parallel: bool = True, @@ -350,7 +420,7 @@ class VTK: N_data = data.shape[0] - data_ = data.reshape(N_data,-1)\ + data_ = data.reshape(N_data,-1) \ .astype(np.single if data.dtype in [np.double,np.longdouble] else data.dtype) if data.dtype.type is np.str_: @@ -429,83 +499,6 @@ class VTK: raise ValueError(f'array "{label}" not found') - @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) - - - def __repr__(self) -> str: - """Give short human-readable summary.""" - info = [] - if isinstance(self.vtk_data,vtk.vtkImageData): - info.append('vtkImageData') - elif isinstance(self.vtk_data,vtk.vtkUnstructuredGrid): - info.append('vtkUnstructuredGrid') - elif isinstance(self.vtk_data,vtk.vtkPolyData): - info.append('vtkPolyData') - elif isinstance(self.vtk_data,vtk.vtkRectilinearGrid): - info.append('vtkRectilinearGrid') - - 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.append(f'with {data}:') - info += self.labels[data] - - return util.srepr(info) - - - def __eq__(self, - other: object) -> bool: - """ - Test equality of other. - - Parameters - ---------- - other : damask.VTK - VTK to compare self against. - - """ - if not isinstance(other, VTK): - return NotImplemented - return self.as_ASCII() == other.as_ASCII() - - - 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 show(self, label: str = None, colormap: Colormap = Colormap.from_predefined('cividis')): From 08a96f64f58e5ebfc44991f6c28b65183a98a7a2 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Wed, 23 Feb 2022 09:12:55 -0500 Subject: [PATCH 24/26] make mypy happy... --- python/damask/_vtk.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index 9f73633d5..735d3bf57 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -59,10 +59,13 @@ class VTK: Parameters ---------- other : damask.VTK - VTK to compare self against. + VTK to check for equality. """ - return self.as_ASCII() == other.as_ASCII() if isinstance(other, VTK) else NotImplemented + if not isinstance(other, VTK): + return NotImplemented + return self.as_ASCII() == other.as_ASCII() + def copy(self): From fd52c18488c3b01be3f052d90c99b14ac8cad916 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Thu, 24 Feb 2022 18:35:48 -0500 Subject: [PATCH 25/26] remove debug print statements in tests --- python/tests/test_VTK.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/python/tests/test_VTK.py b/python/tests/test_VTK.py index 0681cffe3..02f09d391 100644 --- a/python/tests/test_VTK.py +++ b/python/tests/test_VTK.py @@ -45,7 +45,6 @@ class TestVTK: with open(tmp_path/'imageData.vtk','w') as f: f.write(string) vtk = VTK.load(tmp_path/'imageData.vtk','VTK_imageData') - print(vtk) assert (string == vtr.as_ASCII() == vtk.as_ASCII()) def test_rectilinearGrid(self,tmp_path): @@ -57,7 +56,6 @@ class TestVTK: with open(tmp_path/'rectilinearGrid.vtk','w') as f: f.write(string) vtk = VTK.load(tmp_path/'rectilinearGrid.vtk','VTK_rectilinearGrid') - print(vtk) assert (string == vtr.as_ASCII() == vtk.as_ASCII()) def test_polyData(self,tmp_path): @@ -69,7 +67,6 @@ class TestVTK: with open(tmp_path/'polyData.vtk','w') as f: f.write(string) vtk = VTK.load(tmp_path/'polyData.vtk','polyData') - print(vtk) assert(string == vtp.as_ASCII() == vtk.as_ASCII()) @pytest.mark.parametrize('cell_type,n',[ @@ -89,7 +86,6 @@ class TestVTK: with open(tmp_path/'unstructuredGrid.vtk','w') as f: f.write(string) vtk = VTK.load(tmp_path/'unstructuredGrid.vtk','unstructuredgrid') - print(vtk) assert(string == vtu.as_ASCII() == vtk.as_ASCII()) From bee3f08d77a565f2d2c363f81023cb5ace098093 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Thu, 24 Feb 2022 19:15:28 -0500 Subject: [PATCH 26/26] test __repr__ functionality with str() --- python/tests/test_VTK.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/tests/test_VTK.py b/python/tests/test_VTK.py index 02f09d391..fc2ca179b 100644 --- a/python/tests/test_VTK.py +++ b/python/tests/test_VTK.py @@ -39,6 +39,7 @@ class TestVTK: 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') @@ -50,6 +51,7 @@ class TestVTK: def test_rectilinearGrid(self,tmp_path): grid = np.sort(np.random.random((3,10))) v = VTK.from_rectilinear_grid(grid) + string = str(v) string = v.as_ASCII() v.save(tmp_path/'rectilinearGrid',False) vtr = VTK.load(tmp_path/'rectilinearGrid.vtr') @@ -61,6 +63,7 @@ class TestVTK: def test_polyData(self,tmp_path): points = np.random.rand(100,3) v = VTK.from_poly_data(points) + string = str(v) string = v.as_ASCII() v.save(tmp_path/'polyData',False) vtp = VTK.load(tmp_path/'polyData.vtp') @@ -80,6 +83,7 @@ class TestVTK: nodes = np.random.rand(n,3) connectivity = np.random.choice(np.arange(n),n,False).reshape(-1,n) v = VTK.from_unstructured_grid(nodes,connectivity,cell_type) + string = str(v) string = v.as_ASCII() v.save(tmp_path/'unstructuredGrid',False) vtu = VTK.load(tmp_path/'unstructuredGrid.vtu')