From 06201da5e2a3baee881c2061f360adf79c8cc7bd Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Tue, 21 Feb 2023 15:27:06 +0000 Subject: [PATCH] Improvements to Python docstrings --- python/damask/_colormap.py | 23 ++--- python/damask/_crystal.py | 8 +- python/damask/_grid.py | 152 ++++++++++++++++++++++------------ python/damask/_orientation.py | 50 ++++++----- python/damask/_result.py | 91 ++++++++++---------- python/damask/_rotation.py | 8 +- python/damask/_table.py | 16 ++-- python/damask/_vtk.py | 15 ++-- python/damask/grid_filters.py | 2 +- python/damask/mechanics.py | 26 ++++-- python/damask/seeds.py | 29 ++++++- python/damask/solver/_marc.py | 7 +- python/tests/test_Grid.py | 40 ++++----- 13 files changed, 280 insertions(+), 187 deletions(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index 50ff910e6..0d33a08ae 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -28,10 +28,10 @@ _REF_WHITE = np.array([.95047, 1.00000, 1.08883]) class Colormap(mpl.colors.ListedColormap): """ - Enhance matplotlib colormap functionality to be used within DAMASK. + Enhance matplotlib colormap functionality for use within DAMASK. Colors are internally stored as R(ed) G(green) B(lue) values. - The colormap can be used in matplotlib, seaborn, etc., or can + A colormap can be used in matplotlib, seaborn, etc., or can be exported to file for external use. References @@ -153,12 +153,12 @@ class Colormap(mpl.colors.ListedColormap): - 'hsl': Hue Saturation Luminance. - 'xyz': CIE Xyz. - 'lab': CIE Lab. - - 'msh': Msh (for perceptual uniform interpolation). + - 'msh': Msh (for perceptually uniform interpolation). Returns ------- new : damask.Colormap - Colormap within given bounds. + Colormap spanning given bounds. Examples -------- @@ -288,6 +288,7 @@ class Colormap(mpl.colors.ListedColormap): Value range (left,right) spanned by colormap. gap : field.dtype, optional Transparent value. NaN will always be rendered transparent. + Defaults to None. Returns ------- @@ -334,6 +335,7 @@ class Colormap(mpl.colors.ListedColormap): -------- >>> import damask >>> damask.Colormap.from_predefined('stress').reversed() + Colormap: stress_r """ rev = super().reversed(name) @@ -353,6 +355,7 @@ class Colormap(mpl.colors.ListedColormap): If None, colormap name + suffix. suffix: str, optional Extension to use for colormap file. + Defaults to empty. Returns ------- @@ -452,8 +455,8 @@ class Colormap(mpl.colors.ListedColormap): References ---------- - https://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf - https://www.kennethmoreland.com/color-maps/diverging_map.py + | https://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf + | https://www.kennethmoreland.com/color-maps/diverging_map.py """ def rad_diff(a,b): @@ -735,8 +738,8 @@ class Colormap(mpl.colors.ListedColormap): References ---------- - https://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf - https://www.kennethmoreland.com/color-maps/diverging_map.py + | https://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf + | https://www.kennethmoreland.com/color-maps/diverging_map.py """ M = np.linalg.norm(lab) @@ -763,8 +766,8 @@ class Colormap(mpl.colors.ListedColormap): References ---------- - https://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf - https://www.kennethmoreland.com/color-maps/diverging_map.py + | https://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf + | https://www.kennethmoreland.com/color-maps/diverging_map.py """ return np.array([ diff --git a/python/damask/_crystal.py b/python/damask/_crystal.py index ed694fb12..9434515f5 100644 --- a/python/damask/_crystal.py +++ b/python/damask/_crystal.py @@ -307,15 +307,13 @@ class Crystal(): Cubic crystal family: >>> import damask - >>> cubic = damask.Crystal(family='cubic') - >>> cubic + >>> (cubic := damask.Crystal(family='cubic')) Crystal family: cubic Body-centered cubic Bravais lattice with parameters of iron: >>> import damask - >>> Fe = damask.Crystal(lattice='cI', a=287e-12) - >>> Fe + >>> (Fe := damask.Crystal(lattice='cI', a=287e-12)) Crystal family: cubic Bravais lattice: cI a=2.87e-10 m, b=2.87e-10 m, c=2.87e-10 m @@ -406,7 +404,7 @@ class Crystal(): """ Return repr(self). - Give short human-readable summary. + Give short, human-readable summary. """ family = f'Crystal family: {self.family}' diff --git a/python/damask/_grid.py b/python/damask/_grid.py index 5f58a9c75..558484bbf 100644 --- a/python/damask/_grid.py +++ b/python/damask/_grid.py @@ -32,10 +32,10 @@ class Grid: """ Geometry definition for grid solvers. - Create and manipulate geometry definitions for storage as VTK - image data files ('.vti' extension). A grid contains the - material ID (referring to the entry in 'material.yaml') and - the physical size. + Create and manipulate geometry definitions for storage as VTK ImageData + files ('.vti' extension). A grid has a physical size, a coordinate origin, + and contains the material ID (indexing an entry in 'material.yaml') + as well as initial condition fields. """ def __init__(self, @@ -57,7 +57,7 @@ class Grid: origin : sequence of float, len (3), optional Coordinates of grid origin in meter. Defaults to [0.0,0.0,0.0]. initial_conditions : dictionary, optional - Labels and values of the inital conditions at each material point. + Initial condition label and field values at each grid point. comments : (sequence of) str, optional Additional, human-readable information, e.g. history of operations. @@ -74,7 +74,7 @@ class Grid: """ Return repr(self). - Give short human-readable summary. + Give short, human-readable summary. """ mat_min = np.nanmin(self.material) @@ -144,7 +144,7 @@ class Grid: @property def size(self) -> np.ndarray: - """Physical size of grid in meter.""" + """Edge lengths of grid in meter.""" return self._size @size.setter @@ -157,7 +157,7 @@ class Grid: @property def origin(self) -> np.ndarray: - """Coordinates of grid origin in meter.""" + """Vector to grid origin in meter.""" return self._origin @origin.setter @@ -186,7 +186,7 @@ class Grid: @property def cells(self) -> np.ndarray: - """Number of cells in x,y,z direction.""" + """Cell counts along x,y,z direction.""" return np.asarray(self.material.shape) @@ -199,7 +199,7 @@ class Grid: @staticmethod def load(fname: Union[str, Path]) -> 'Grid': """ - Load from VTK image data file. + Load from VTK ImageData file. Parameters ---------- @@ -470,9 +470,9 @@ class Grid: Parameters ---------- cells : sequence of int, len (3) - Number of cells in x,y,z direction. + Cell counts along x,y,z direction. size : sequence of float, len (3) - Physical size of the grid in meter. + Edge lengths of the grid in meter. seeds : numpy.ndarray of float, shape (:,3) Position of the seed points in meter. All points need to lay within the box. weights : sequence of float, len (seeds.shape[0]) @@ -527,9 +527,9 @@ class Grid: Parameters ---------- cells : sequence of int, len (3) - Number of cells in x,y,z direction. + Cell counts along x,y,z direction. size : sequence of float, len (3) - Physical size of the grid in meter. + Edge lengths of the grid in meter. seeds : numpy.ndarray of float, shape (:,3) Position of the seed points in meter. All points need to lay within the box. material : sequence of int, len (seeds.shape[0]), optional @@ -608,14 +608,14 @@ class Grid: periods: int = 1, materials: IntSequence = (0,1)) -> 'Grid': """ - Create grid from definition of triply periodic minimal surface. + Create grid from definition of triply-periodic minimal surface. Parameters ---------- cells : sequence of int, len (3) - Number of cells in x,y,z direction. + Cell counts along x,y,z direction. size : sequence of float, len (3) - Physical size of the grid in meter. + Edge lengths of the grid in meter. surface : str Type of the minimal surface. See notes for details. threshold : float, optional. @@ -664,19 +664,19 @@ class Grid: >>> import numpy as np >>> import damask >>> damask.Grid.from_minimal_surface([64]*3,np.ones(3)*1.e-4,'Gyroid') - cells : 64 x 64 x 64 - size : 0.0001 x 0.0001 x 0.0001 m³ + cells : 64 × 64 × 64 + size : 0.0001 × 0.0001 × 0.0001 m³ origin: 0.0 0.0 0.0 m # materials: 2 - Minimal surface of 'Neovius' type with non-default material IDs. + Minimal surface of 'Neovius' type with specific material IDs. >>> import numpy as np >>> import damask >>> damask.Grid.from_minimal_surface([80]*3,np.ones(3)*5.e-4, ... 'Neovius',materials=(1,5)) - cells : 80 x 80 x 80 - size : 0.0005 x 0.0005 x 0.0005 m³ + cells : 80 × 80 × 80 + size : 0.0005 × 0.0005 × 0.0005 m³ origin: 0.0 0.0 0.0 m # materials: 2 (min: 1, max: 5) @@ -695,12 +695,13 @@ class Grid: fname: Union[str, Path], compress: bool = True): """ - Save as VTK image data file. + Save as VTK ImageData file. Parameters ---------- fname : str or pathlib.Path - Filename to write. Valid extension is .vti, it will be appended if not given. + Filename to write. + Valid extension is .vti, which will be appended if not given. compress : bool, optional Compress with zlib algorithm. Defaults to True. @@ -727,7 +728,7 @@ class Grid: fname : str or file handle Geometry file to write with extension '.geom'. compress : bool, optional - Compress geometry with 'x of y' and 'a to b'. + Compress geometry using 'x of y' and 'a to b'. """ warnings.warn('Support for ASCII-based geom format will be removed in DAMASK 3.0.0', DeprecationWarning,2) @@ -771,13 +772,13 @@ class Grid: Parameters ---------- cells : sequence of int, len (3), optional - Number of cells x,y,z direction. + Cell counts along x,y,z direction. offset : sequence of int, len (3), optional Offset (measured in cells) from old to new grid. Defaults to [0,0,0]. fill : int, optional Material ID to fill the background. - Defaults to material.max() + 1. + Defaults to material.max()+1. Returns ------- @@ -790,11 +791,11 @@ class Grid: >>> import numpy as np >>> import damask - >>> g = damask.Grid(np.zeros([32]*3,int),np.ones(3)*1e-4) + >>> g = damask.Grid(np.zeros([32]*3,int),np.ones(3)*1e-3) >>> g.canvas([32,32,16],[0,0,16]) - cells : 33 x 32 x 16 - size : 0.0001 x 0.0001 x 5e-05 m³ - origin: 0.0 0.0 5e-05 m + cells: 32 × 32 × 16 + size: 0.001 × 0.001 × 0.0005 m³ + origin: 0.0 0.0 0.0005 m # materials: 1 """ @@ -837,16 +838,33 @@ class Grid: Examples -------- - Mirror along x- and y-direction. + Mirror along y-direction. >>> import numpy as np >>> import damask - >>> g = damask.Grid(np.zeros([32]*3,int),np.ones(3)*1e-4) - >>> g.mirror('xy',True) - cells : 64 x 64 x 32 - size : 0.0002 x 0.0002 x 0.0001 m³ + >>> (g := damask.Grid(np.arange(4*5*6).reshape([4,5,6]),np.ones(3))) + cells: 4 × 5 × 6 + size: 1.0 × 1.0 × 1.0 m³ origin: 0.0 0.0 0.0 m - # materials: 1 + # materials: 120 + >>> g.mirror('y') + cells: 4 × 8 × 6 + size: 1.0 × 1.6 × 1.0 m³ + origin: 0.0 0.0 0.0 m + # materials: 120 + + Reflect along x- and y-direction. + + >>> g.mirror('xy',reflect=True) + cells: 8 × 10 × 6 + size: 2.0 × 2.0 × 1.0 m³ + origin: 0.0 0.0 0.0 m + # materials: 120 + + Independence of mirroring order. + + >>> g.mirror('xy') == g.mirror(['y','x']) + True """ if not set(directions).issubset(valid := ['x', 'y', 'z']): @@ -884,11 +902,29 @@ class Grid: updated : damask.Grid Updated grid-based geometry. + Examples + -------- + Invariance of flipping order. + + >>> import numpy as np + >>> import damask + >>> (g := damask.Grid(np.arange(4*5*6).reshape([4,5,6]),np.ones(3))) + cells: 4 × 5 × 6 + size: 1.0 × 1.0 × 1.0 m³ + origin: 0.0 0.0 0.0 m + # materials: 120 + >>> g.flip('xyz') == g.flip(['x','z','y']) + True + + Invariance of flipping a (fully) mirrored grid. + + >>> g.mirror('x',True) == g.mirror('x',True).flip('x') + True + """ if not set(directions).issubset(valid := ['x', 'y', 'z']): raise ValueError(f'invalid direction "{set(directions).difference(valid)}" specified') - mat = np.flip(self.material, [valid.index(d) for d in directions if d in valid]) return Grid(material = mat, @@ -902,7 +938,7 @@ class Grid: R: Rotation, fill: Optional[int] = None) -> 'Grid': """ - Rotate grid (and pad if required). + Rotate grid (possibly extending its bounding box). Parameters ---------- @@ -910,13 +946,27 @@ class Grid: Rotation to apply to the grid. fill : int, optional Material ID to fill enlarged bounding box. - Defaults to material.max() + 1. + Defaults to material.max()+1. Returns ------- updated : damask.Grid Updated grid-based geometry. + Examples + -------- + Rotation by 180° (π) is equivalent to twice flipping. + + >>> import numpy as np + >>> import damask + >>> (g := damask.Grid(np.arange(4*5*6).reshape([4,5,6]),np.ones(3))) + cells: 4 × 5 × 6 + size: 1.0 × 1.0 × 1.0 m³ + origin: 0.0 0.0 0.0 m + # materials: 120 + >>> g.rotate(damask.Rotation.from_axis_angle([0,0,1,180],degrees=True)) == g.flip('xy') + True + """ material = self.material # These rotations are always applied in the reference coordinate system, i.e. (z,x,z) not (z,x',z'') @@ -941,12 +991,12 @@ class Grid: def scale(self, cells: IntSequence) -> 'Grid': """ - Scale grid to new cell count. + Scale grid to new cell counts. Parameters ---------- cells : sequence of int, len (3) - Number of cells in x,y,z direction. + Cell counts along x,y,z direction. Returns ------- @@ -955,7 +1005,7 @@ class Grid: Examples -------- - Double resolution. + Double grid resolution. >>> import numpy as np >>> import damask @@ -965,8 +1015,8 @@ class Grid: origin: 0.0 0.0 0.0 m # materials: 1 >>> g.scale(g.cells*2) - cells : 64 x 64 x 64 - size : 0.0001 x 0.0001 x 0.0001 m³ + cells : 64 × 64 × 64 + size : 0.0001 × 0.0001 × 0.0001 m³ origin: 0.0 0.0 0.0 m # materials: 1 @@ -1069,7 +1119,7 @@ class Grid: def sort(self) -> 'Grid': """ - Sort material indices such that min(material) is located at (0,0,0). + Sort material indices such that min(material ID) is located at (0,0,0). Returns ------- @@ -1186,7 +1236,7 @@ class Grid: fill : int, optional Fill value for primitive. Defaults to material.max()+1. R : damask.Rotation, optional - Rotation of primitive. Defaults to no rotation. + Rotation of the primitive. Defaults to no rotation. inverse : bool, optional Retain original materials within primitive and fill outside. Defaults to False. @@ -1206,8 +1256,8 @@ class Grid: >>> import damask >>> g = damask.Grid(np.zeros([64]*3,int), np.ones(3)*1e-4) >>> g.add_primitive(np.ones(3)*5e-5,np.ones(3)*5e-5,1) - cells : 64 x 64 x 64 - size : 0.0001 x 0.0001 x 0.0001 m³ + cells : 64 × 64 × 64 + size : 0.0001 × 0.0001 × 0.0001 m³ origin: 0.0 0.0 0.0 m # materials: 2 @@ -1217,8 +1267,8 @@ class Grid: >>> import damask >>> g = damask.Grid(np.zeros([64]*3,int), np.ones(3)*1e-4) >>> g.add_primitive(np.ones(3,int)*32,np.zeros(3),np.inf) - cells : 64 x 64 x 64 - size : 0.0001 x 0.0001 x 0.0001 m³ + cells : 64 × 64 × 64 + size : 0.0001 × 0.0001 × 0.0001 m³ origin: 0.0 0.0 0.0 m # materials: 2 diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 2206d5b2a..306ee99dc 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -108,7 +108,7 @@ class Orientation(Rotation,Crystal): Parameters ---------- - rotation : list, numpy.ndarray, Rotation, optional + rotation : list, numpy.ndarray, or Rotation, optional Unit quaternion in positive real hemisphere. Use .from_quaternion to perform a sanity check. Defaults to no rotation. @@ -123,7 +123,7 @@ class Orientation(Rotation,Crystal): """ Return repr(self). - Give short human-readable summary. + Give short, human-readable summary. """ return util.srepr([Crystal.__repr__(self), @@ -467,24 +467,24 @@ class Orientation(Rotation,Crystal): if self.family == 'cubic': return (np.prod(np.sqrt(2)-1. >= rho_abs,axis=-1) * (1. >= np.sum(rho_abs,axis=-1))).astype(bool) - elif self.family == 'hexagonal': + if self.family == 'hexagonal': return (np.prod(1. >= rho_abs,axis=-1) * (2. >= np.sqrt(3)*rho_abs[...,0] + rho_abs[...,1]) * (2. >= np.sqrt(3)*rho_abs[...,1] + rho_abs[...,0]) * (2. >= np.sqrt(3) + rho_abs[...,2])).astype(bool) - elif self.family == 'tetragonal': + if self.family == 'tetragonal': return (np.prod(1. >= rho_abs[...,:2],axis=-1) * (np.sqrt(2) >= rho_abs[...,0] + rho_abs[...,1]) * (np.sqrt(2) >= rho_abs[...,2] + 1.)).astype(bool) - elif self.family == 'orthorhombic': + if self.family == 'orthorhombic': return (np.prod(1. >= rho_abs,axis=-1)).astype(bool) - elif self.family == 'monoclinic': + if self.family == 'monoclinic': return np.logical_or( 1. >= rho_abs[...,1], np.isnan(rho_abs[...,1])) - elif self.family == 'triclinic': + if self.family == 'triclinic': return np.ones(rho_abs.shape[:-1]).astype(bool) - else: - raise TypeError(f'unknown symmetry "{self.family}"') + + raise TypeError(f'unknown symmetry "{self.family}"') @property @@ -510,38 +510,40 @@ class Orientation(Rotation,Crystal): return ((rho[...,0] >= rho[...,1]) & (rho[...,1] >= rho[...,2]) & (rho[...,2] >= 0)).astype(bool) - elif self.family == 'hexagonal': + if self.family == 'hexagonal': return ((rho[...,0] >= rho[...,1]*np.sqrt(3)) & (rho[...,1] >= 0) & (rho[...,2] >= 0)).astype(bool) - elif self.family == 'tetragonal': + if self.family == 'tetragonal': return ((rho[...,0] >= rho[...,1]) & (rho[...,1] >= 0) & (rho[...,2] >= 0)).astype(bool) - elif self.family == 'orthorhombic': + if self.family == 'orthorhombic': return ((rho[...,0] >= 0) & (rho[...,1] >= 0) & (rho[...,2] >= 0)).astype(bool) - elif self.family == 'monoclinic': + if self.family == 'monoclinic': return ((rho[...,1] >= 0) & (rho[...,2] >= 0)).astype(bool) - else: - return np.ones_like(rho[...,0],dtype=bool) + + return np.ones_like(rho[...,0],dtype=bool) def disorientation(self, other: 'Orientation', return_operators: bool = False) -> object: """ - Calculate disorientation between myself and given other orientation. + Calculate disorientation between self and given other orientation. Parameters ---------- other : Orientation Orientation to calculate disorientation for. Shape of other blends with shape of own rotation array. - For example, shapes of (2,3) for own rotations and (3,2) for other's result in (2,3,2) disorientations. + For example, shapes of (2,3) for own rotations + and (3,2) for other's result in (2,3,2) disorientations. return_operators : bool, optional - Return index pair of symmetrically equivalent orientations that result in disorientation axis falling into FZ. + Return index pair of symmetrically equivalent orientations + that result in disorientation axis falling into FZ. Defaults to False. Returns @@ -578,8 +580,8 @@ class Orientation(Rotation,Crystal): >>> N = 10000 >>> a = damask.Orientation.from_random(shape=N,family='cubic') >>> b = damask.Orientation.from_random(shape=N,family='cubic') - >>> d = a.disorientation(b).as_axis_angle(degrees=True,pair=True)[1] - >>> plt.hist(d,25) + >>> n,omega = a.disorientation(b).as_axis_angle(degrees=True,pair=True) + >>> plt.hist(omega,25) >>> plt.show() """ @@ -626,6 +628,7 @@ class Orientation(Rotation,Crystal): ---------- weights : numpy.ndarray, shape (self.shape), optional Relative weights of orientations. + Defaults to equal weights. return_cloud : bool, optional Return the specific (symmetrically equivalent) orientations that were averaged. Defaults to False. @@ -895,8 +898,8 @@ class Orientation(Rotation,Crystal): Schmid matrix (in lab frame) of first octahedral slip system of a face-centered cubic crystal in "Goss" orientation. - >>> import damask >>> import numpy as np + >>> import damask >>> np.set_printoptions(3,suppress=True,floatmode='fixed') >>> O = damask.Orientation.from_Euler_angles(phi=[0,45,0],degrees=True,lattice='cF') >>> O.Schmid(N_slip=[1]) @@ -936,8 +939,9 @@ class Orientation(Rotation,Crystal): Returns ------- - Orientations related to self following the selected - model for the orientation relationship. + rel : Orientation, shape (:,self.shape) + Orientations related to self according to the selected + model for the orientation relationship. Examples -------- diff --git a/python/damask/_result.py b/python/damask/_result.py index 83c1e8298..f2fd06091 100644 --- a/python/damask/_result.py +++ b/python/damask/_result.py @@ -65,9 +65,9 @@ def _empty_like(dataset: np.ma.core.MaskedArray, class Result: """ - Add data to and export data from a DADF5 file. + Add data to and export data from a DADF5 (DAMASK HDF5) file. - A DADF5 (DAMASK HDF5) file contains DAMASK results. + A DADF5 file contains DAMASK results. Its group/folder structure reflects the layout in material.yaml. This class provides a customizable view on the DADF5 file. @@ -93,7 +93,7 @@ class Result: def __init__(self, fname: Union[str, Path]): """ - New result view bound to a HDF5 file. + New result view bound to a DADF5 file. Parameters ---------- @@ -167,7 +167,7 @@ class Result: """ Return repr(self). - Give short human-readable summary. + Give short, human-readable summary. """ with h5py.File(self.fname,'r') as f: @@ -195,7 +195,7 @@ class Result: homogenizations: Union[None, str, Sequence[str], bool] = None, fields: Union[None, str, Sequence[str], bool] = None) -> "Result": """ - Manages the visibility of the groups. + Manage the visibility of the groups. Parameters ---------- @@ -319,15 +319,15 @@ class Result: Parameters ---------- increments: (list of) int, (list of) str, or bool, optional. - Number(s) of increments to select. + Numbers of increments to select. times: (list of) float, (list of) str, or bool, optional. - Simulation time(s) of increments to select. + Simulation times of increments to select. phases: (list of) str, or bool, optional. - Name(s) of phases to select. + Names of phases to select. homogenizations: (list of) str, or bool, optional. - Name(s) of homogenizations to select. + Names of homogenizations to select. fields: (list of) str, or bool, optional. - Name(s) of fields to select. + Names of fields to select. protected: bool, optional. Protection status of existing data. @@ -375,15 +375,15 @@ class Result: Parameters ---------- increments: (list of) int, (list of) str, or bool, optional. - Number(s) of increments to select. + Numbers of increments to select. times: (list of) float, (list of) str, or bool, optional. - Simulation time(s) of increments to select. + Simulation times of increments to select. phases: (list of) str, or bool, optional. - Name(s) of phases to select. + Names of phases to select. homogenizations: (list of) str, or bool, optional. - Name(s) of homogenizations to select. + Names of homogenizations to select. fields: (list of) str, or bool, optional. - Name(s) of fields to select. + Names of fields to select. Returns ------- @@ -418,15 +418,15 @@ class Result: Parameters ---------- increments: (list of) int, (list of) str, or bool, optional. - Number(s) of increments to select. + Numbers of increments to select. times: (list of) float, (list of) str, or bool, optional. - Simulation time(s) of increments to select. + Simulation times of increments to select. phases: (list of) str, or bool, optional. - Name(s) of phases to select. + Names of phases to select. homogenizations: (list of) str, or bool, optional. - Name(s) of homogenizations to select. + Names of homogenizations to select. fields: (list of) str, or bool, optional. - Name(s) of fields to select. + Names of fields to select. Returns ------- @@ -721,9 +721,11 @@ class Result: Parameters ---------- P : str, optional - Name of the dataset containing the first Piola-Kirchhoff stress. Defaults to 'P'. + Name of the dataset containing the first Piola-Kirchhoff stress. + Defaults to 'P'. F : str, optional - Name of the dataset containing the deformation gradient. Defaults to 'F'. + Name of the dataset containing the deformation gradient. + Defaults to 'F'. """ self._add_generic_pointwise(self._add_stress_Cauchy,{'P':P,'F':F}) @@ -1023,14 +1025,14 @@ class Result: x: str, ord: Union[None, int, float, Literal['fro', 'nuc']] = None): """ - Add the norm of vector or tensor. + Add the norm of a vector or tensor. Parameters ---------- x : str Name of vector or tensor dataset. ord : {non-zero int, inf, -inf, 'fro', 'nuc'}, optional - Order of the norm. inf means NumPy’s inf object. For details refer to numpy.linalg.norm. + Order of the norm. inf means NumPy's inf object. For details refer to numpy.linalg.norm. """ self._add_generic_pointwise(self._add_norm,{'x':x},{'ord':ord}) @@ -1052,7 +1054,7 @@ class Result: def add_stress_second_Piola_Kirchhoff(self, P: str = 'P', F: str = 'F'): - """ + r""" Add second Piola-Kirchhoff stress calculated from first Piola-Kirchhoff stress and deformation gradient. Parameters @@ -1064,9 +1066,10 @@ class Result: Notes ----- - The definition of the second Piola-Kirchhoff stress (S = [F^-1 P]_sym) + The definition of the second Piola-Kirchhoff stress + :math:`\vb{S} = \left(\vb{F}^{-1} \vb{P}\right)_\text{sym}` follows the standard definition in nonlinear continuum mechanics. - As such, no intermediate configuration, for instance that reached by F_p, + As such, no intermediate configuration, for instance that reached by :math:`\vb{F}_\text{p}`, is taken into account. """ @@ -1240,10 +1243,11 @@ class Result: Notes ----- - The incoporation of rotational parts into the elastic and plastic - deformation gradient requires it to use material/Lagragian strain measures - (based on 'U') for plastic strains and spatial/Eulerian strain measures - (based on 'V') for elastic strains when calculating averages. + The presence of rotational parts in the elastic and plastic deformation gradient + calls for the use of + material/Lagragian strain measures (based on 'U') for plastic strains and + spatial/Eulerian strain measures (based on 'V') for elastic strains + when calculating averages. """ self._add_generic_pointwise(self._add_strain,{'F':F},{'t':t,'m':m}) @@ -1302,7 +1306,7 @@ class Result: Notes ----- This function is only available for structured grids, - i.e. results from the grid solver. + i.e. fields resulting from the grid solver. """ self._add_generic_grid(self._add_curl,{'f':f},{'size':self.size}) @@ -1331,7 +1335,7 @@ class Result: Notes ----- This function is only available for structured grids, - i.e. results from the grid solver. + i.e. fields resulting from the grid solver. """ self._add_generic_grid(self._add_divergence,{'f':f},{'size':self.size}) @@ -1361,7 +1365,7 @@ class Result: Notes ----- This function is only available for structured grids, - i.e. results from the grid solver. + i.e. fields resulting from the grid solver. """ self._add_generic_grid(self._add_gradient,{'f':f},{'size':self.size}) @@ -1379,10 +1383,10 @@ class Result: ---------- func : function Callback function that calculates a new dataset from one or - more datasets per HDF5 group. + more datasets per DADF5 group. datasets : dictionary Details of the datasets to be used: - {arg (name to which the data is passed in func): label (in HDF5 file)}. + {arg (name to which the data is passed in func): label (in DADF5 file)}. args : dictionary, optional Arguments parsed to func. @@ -1462,10 +1466,10 @@ class Result: ---------- callback : function Callback function that calculates a new dataset from one or - more datasets per HDF5 group. + more datasets per DADF5 group. datasets : dictionary Details of the datasets to be used: - {arg (name to which the data is passed in func): label (in HDF5 file)}. + {arg (name to which the data is passed in func): label (in DADF5 file)}. args : dictionary, optional Arguments parsed to func. @@ -1500,7 +1504,7 @@ class Result: dataset.attrs['overwritten'] = True else: shape = result['data'].shape - if compress := (result['data'].size >= chunk_size*2): + if compress := result['data'].size >= chunk_size*2: chunks = (chunk_size//np.prod(shape[1:]),)+shape[1:] else: chunks = shape @@ -1828,9 +1832,10 @@ class Result: Export to VTK cell/point data. One VTK file per visible increment is created. - For point data, the VTK format is poly data (.vtp). - For cell data, either an image (.vti) or unstructured (.vtu) dataset - is written for grid-based or mesh-based simulations, respectively. + For point data, the VTK format is PolyData (.vtp). + For cell data, the file format is either ImageData (.vti) + or UnstructuredGrid (.vtu) for grid-based or mesh-based simulations, + respectively. Parameters ---------- @@ -1961,7 +1966,7 @@ class Result: for field in _match(self.visible['fields'],f_in['/'.join([inc,ty,label])].keys()): p = '/'.join([inc,ty,label,field]) for out in _match(output,f_in[p].keys()): - f_in[p].copy(out,f_out[p]) + f_in[p].copy(out,f_out[p]) def export_simulation_setup(self, diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index f986a50b4..118234bf1 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -35,8 +35,8 @@ class Rotation: Rotate vector 'a' (defined in coordinate system 'A') to coordinates 'b' expressed in system 'B': - >>> import damask >>> import numpy as np + >>> import damask >>> Q = damask.Rotation.from_random() >>> a = np.random.rand(3) >>> b = Q @ a @@ -45,8 +45,8 @@ class Rotation: Compound rotations R1 (first) and R2 (second): - >>> import damask >>> import numpy as np + >>> import damask >>> R1 = damask.Rotation.from_random() >>> R2 = damask.Rotation.from_random() >>> R = R2 * R1 @@ -69,7 +69,7 @@ class Rotation: Parameters ---------- - rotation : list, numpy.ndarray, Rotation, optional + rotation : list, numpy.ndarray, or Rotation, optional Unit quaternion in positive real hemisphere. Use .from_quaternion to perform a sanity check. Defaults to no rotation. @@ -88,7 +88,7 @@ class Rotation: """ Return repr(self). - 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)}'\ diff --git a/python/damask/_table.py b/python/damask/_table.py index e213243a4..16841964e 100644 --- a/python/damask/_table.py +++ b/python/damask/_table.py @@ -41,7 +41,7 @@ class Table: """ Return repr(self). - Give short human-readable summary. + Give short, human-readable summary. """ self._relabel('shapes') @@ -255,8 +255,8 @@ class Table: """ Load from ASCII table file. - Initial comments are marked by '#', the first non-comment line - containing the column labels. + Initial comments are marked by '#'. + The first non-comment line contains the column labels. - Vector data column labels are indicated by '1_v, 2_v, ..., n_v'. - Tensor data column labels are indicated by '3x3:1_T, 3x3:2_T, ..., 3x3:9_T'. @@ -264,7 +264,7 @@ class Table: Parameters ---------- fname : file, str, or pathlib.Path - Filename or file for reading. + Filename or file to read. Returns ------- @@ -458,9 +458,9 @@ class Table: Parameters ---------- label_old : (iterable of) str - Old column label(s). + Old column labels. label_new : (iterable of) str - New column label(s). + New column labels. Returns ------- @@ -488,7 +488,7 @@ class Table: label : str or list Column labels for sorting. ascending : bool or list, optional - Set sort order. + Set sort order. Defaults to True. Returns ------- @@ -574,7 +574,7 @@ class Table: Parameters ---------- fname : file, str, or pathlib.Path - Filename or file for writing. + Filename or file to write. with_labels : bool, optional Write column labels. Defaults to True. diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index 95eee8bd3..c6d88f0b4 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -42,7 +42,7 @@ class VTK: """ Return repr(self). - Give short human-readable summary. + Give short, human-readable summary. """ info = [self.vtk_data.__vtkname__] @@ -163,7 +163,7 @@ class VTK: cells : sequence of int, len (3) Number of cells along each dimension. size : sequence of float, len (3) - Physical length along each dimension. + Edge length along each dimension. origin : sequence of float, len (3), optional Coordinates of grid origin. @@ -293,7 +293,7 @@ class VTK: Parameters ---------- fname : str or pathlib.Path - Filename for reading. + Filename to read. 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. @@ -370,7 +370,7 @@ class VTK: Parameters ---------- fname : str or pathlib.Path - Filename for writing. + Filename to write. parallel : bool, optional Write data in parallel background process. Defaults to True. compress : bool, optional @@ -433,6 +433,11 @@ class VTK: Data to add or replace. Each table label is individually considered. Number of rows needs to match either number of cells or number of points. + Returns + ------- + updated : damask.VTK + Updated VTK-based geometry. + Notes ----- If the number of cells equals the number of points, the data is added to both. @@ -548,7 +553,7 @@ class VTK: Notes ----- The first component is shown when visualizing vector datasets - (this includes tensor datasets because they are flattened). + (this includes tensor datasets as they are flattened). """ # See http://compilatrix.com/article/vtk-1 for possible improvements. diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index 9f7c6c1d4..462621e53 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -402,7 +402,7 @@ def displacement_node(size: _FloatSequence, Returns ------- - u_p : numpy.ndarray, shape (:,:,:,3) + u_n : numpy.ndarray, shape (:,:,:,3) Nodal displacements. """ diff --git a/python/damask/mechanics.py b/python/damask/mechanics.py index 3af45d495..2d6078680 100644 --- a/python/damask/mechanics.py +++ b/python/damask/mechanics.py @@ -5,7 +5,7 @@ All routines operate on numpy.ndarrays of shape (...,3,3). """ -from typing import Sequence as _Sequence +from typing import Sequence as _Sequence#, Literal as _Literal import numpy as _np @@ -81,7 +81,7 @@ def equivalent_strain_Mises(epsilon: _np.ndarray) -> _np.ndarray: .. math:: - \epsilon_\text{vM} = \sqrt{2/3 \epsilon^\prime_{ij} \epsilon^\prime_{ij}} + \epsilon_\text{vM} = \sqrt{\frac{2}{3}\,\epsilon^\prime_{ij} \epsilon^\prime_{ij}} where :math:`\vb*{\epsilon}^\prime` is the deviatoric part of the strain tensor. @@ -110,7 +110,7 @@ def equivalent_stress_Mises(sigma: _np.ndarray) -> _np.ndarray: .. math:: - \sigma_\text{vM} = \sqrt{3/2 \sigma^\prime_{ij} \sigma^\prime_{ij}} + \sigma_\text{vM} = \sqrt{\frac{3}{2}\,\sigma^\prime_{ij} \sigma^\prime_{ij}} where :math:`\vb*{\sigma}^\prime` is the deviatoric part of the stress tensor. @@ -168,9 +168,10 @@ def rotation(T: _np.ndarray) -> _rotation.Rotation: def strain(F: _np.ndarray, + #t: _Literal['V', 'U'], should work, but rejected by SC t: str, m: float) -> _np.ndarray: - """ + r""" Calculate strain tensor (Seth–Hill family). Parameters @@ -188,10 +189,19 @@ def strain(F: _np.ndarray, epsilon : numpy.ndarray, shape (...,3,3) Strain of F. + Notes + ----- + The strain is defined as: + + .. math:: + + \vb*{\epsilon}_V^{(m)} = \frac{1}{2m} (\vb{V}^{2m} - \vb{I}) \\\\ + \vb*{\epsilon}_U^{(m)} = \frac{1}{2m} (\vb{U}^{2m} - \vb{I}) + References ---------- - https://en.wikipedia.org/wiki/Finite_strain_theory - https://de.wikipedia.org/wiki/Verzerrungstensor + | https://en.wikipedia.org/wiki/Finite_strain_theory + | https://de.wikipedia.org/wiki/Verzerrungstensor """ if t not in ['V', 'U']: raise ValueError('polar decomposition type not in {V, U}') @@ -315,8 +325,8 @@ def _polar_decomposition(T: _np.ndarray, T : numpy.ndarray, shape (...,3,3) Tensor of which the singular values are computed. requested : sequence of {'R', 'U', 'V'} - Requested outputs: ‘R’ for the rotation tensor, - ‘V’ for left stretch tensor, and ‘U’ for right stretch tensor. + Requested outputs: 'R' for the rotation tensor, + 'V' for left stretch tensor, and 'U' for right stretch tensor. Returns ------- diff --git a/python/damask/seeds.py b/python/damask/seeds.py index f8cb19995..808ad3c1b 100644 --- a/python/damask/seeds.py +++ b/python/damask/seeds.py @@ -21,7 +21,7 @@ def from_random(size: _FloatSequence, Parameters ---------- size : sequence of float, len (3) - Physical size of the seeding domain. + Edge lengths of the seeding domain. N_seeds : int Number of seeds. cells : sequence of int, len (3), optional. @@ -56,12 +56,12 @@ def from_Poisson_disc(size: _FloatSequence, periodic: bool = True, rng_seed: _Optional[_NumpyRngSeed] = None) -> _np.ndarray: """ - Place seeds according to a Poisson disc distribution. + Place seeds following a Poisson disc distribution. Parameters ---------- size : sequence of float, len (3) - Physical size of the seeding domain. + Edge lengths of the seeding domain. N_seeds : int Number of seeds. N_candidates : int @@ -70,6 +70,7 @@ def from_Poisson_disc(size: _FloatSequence, Minimum acceptable distance to other seeds. periodic : bool, optional Calculate minimum distance for periodically repeated grid. + Defaults to True. rng_seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional A seed to initialize the BitGenerator. Defaults to None. If None, then fresh, unpredictable entropy will be pulled from the OS. @@ -123,14 +124,36 @@ def from_grid(grid, Consider all material IDs except those in selection. Defaults to False. average : bool, optional Seed corresponds to center of gravity of material ID cloud. + Defaults to False. periodic : bool, optional Center of gravity accounts for periodic boundaries. + Defaults to True. Returns ------- coords, materials : numpy.ndarray, shape (:,3); numpy.ndarray, shape (:) Seed coordinates in 3D space, material IDs. + Examples + -------- + Recreate seeds from Voronoi tessellation. + + >>> import numpy as np + >>> import scipy.spatial + >>> import damask + >>> seeds = damask.seeds.from_random(np.ones(3),29,[128]*3) + >>> (g := damask.Grid.from_Voronoi_tessellation([128]*3,np.ones(3),seeds)) + cells: 128 × 128 × 128 + size: 1.0 × 1.0 × 1.0 m³ + origin: 0.0 0.0 0.0 m + # materials: 29 + >>> COG,matID = damask.seeds.from_grid(g,average=True) + >>> distance,ID = scipy.spatial.KDTree(COG,boxsize=g.size).query(seeds) + >>> np.max(distance) / np.linalg.norm(g.size/g.cells) + 7.8057356746350415 + >>> (ID == matID).all() + True + """ material = grid.material.reshape((-1,1),order='F') mask = _np.full(grid.cells.prod(),True,dtype=bool) if selection is None else \ diff --git a/python/damask/solver/_marc.py b/python/damask/solver/_marc.py index 1afb5ddc6..18bd56e49 100644 --- a/python/damask/solver/_marc.py +++ b/python/damask/solver/_marc.py @@ -2,6 +2,7 @@ import subprocess import shlex import re from pathlib import Path +from typing import Literal _marc_version = '2022.4' _marc_root = '/opt/msc' @@ -54,7 +55,7 @@ class Marc: def submit_job(self, model: str, job: str, compile: bool = False, - optimization: str = '', + optimization: Literal['', 'l', 'h'] = '', env = None): """ Assemble command line arguments and call Marc executable. @@ -68,8 +69,8 @@ class Marc: compile : bool, optional Compile DAMASK_Marc user subroutine (and save for future use). Defaults to False. - optimization : str, optional - Optimization level '' (-O0), 'l' (-O1), or 'h' (-O3). + optimization : {'', 'l', 'h'}, optional + Optimization level '': -O0, 'l': -O1, or 'h': -O3. Defaults to ''. env : dict, optional Environment for execution. diff --git a/python/tests/test_Grid.py b/python/tests/test_Grid.py index 43207b1e4..f81def323 100644 --- a/python/tests/test_Grid.py +++ b/python/tests/test_Grid.py @@ -98,20 +98,17 @@ class TestGrid: size=np.ones(3), origin=np.ones(4)) - def test_invalid_materials_shape(self,default): material = np.ones((3,3)) with pytest.raises(ValueError): Grid(material, size=np.ones(3)) - def test_invalid_materials_type(self,default): material = np.random.randint(1,300,(3,4,5))==1 with pytest.raises(TypeError): Grid(material) - @pytest.mark.parametrize('directions,reflect',[ (['x'], False), (['x','y','z'],True), @@ -126,12 +123,16 @@ class TestGrid: if update: modified.save(reference) assert Grid.load(reference) == modified - @pytest.mark.parametrize('directions',[(1,2,'y'),('a','b','x'),[1]]) def test_mirror_invalid(self,default,directions): with pytest.raises(ValueError): default.mirror(directions) + @pytest.mark.parametrize('reflect',[True,False]) + def test_mirror_order_invariant(self,default,reflect): + direction = np.array(['x','y','z']) + assert default.mirror(np.random.permutation(direction),reflect=reflect) \ + == default.mirror(np.random.permutation(direction),reflect=reflect) @pytest.mark.parametrize('directions',[ ['x'], @@ -147,22 +148,30 @@ class TestGrid: if update: modified.save(reference) assert Grid.load(reference) == modified + def test_flip_order_invariant(self,default): + direction = np.array(['x','y','z']) + assert default.flip(np.random.permutation(direction)) \ + == default.flip(np.random.permutation(direction)) - def test_flip_invariant(self,default): - assert default == default.flip([]) + def test_flip_mirrored_invariant(self,default): + direction = np.random.permutation(['x','y','z']) + assert default.mirror(direction,True) == default.mirror(direction,True).flip(direction) + def test_flip_equal_halfspin(self,default): + direction = ['x','y','z'] + i = np.random.choice(3) + assert default.rotate(Rotation.from_axis_angle(np.hstack((np.identity(3)[i],180)),degrees=True)) \ + == default.flip(direction[:i]+direction[i+1:]) @pytest.mark.parametrize('direction',[['x'],['x','y']]) def test_flip_double(self,default,direction): assert default == default.flip(direction).flip(direction) - @pytest.mark.parametrize('directions',[(1,2,'y'),('a','b','x'),[1]]) def test_flip_invalid(self,default,directions): with pytest.raises(ValueError): default.flip(directions) - @pytest.mark.parametrize('distance',[1.,np.sqrt(3)]) @pytest.mark.parametrize('selection',[None,1,[1],[1,2,3]]) @pytest.mark.parametrize('periodic',[True,False]) @@ -184,7 +193,6 @@ class TestGrid: assert random.clean(selection=None,invert_selection=True,rng_seed=0) == random.clean(rng_seed=0) and \ random.clean(selection=None,invert_selection=False,rng_seed=0) == random.clean(rng_seed=0) - @pytest.mark.parametrize('cells',[ (10,11,10), [10,13,10], @@ -201,7 +209,6 @@ class TestGrid: if update: modified.save(reference) assert Grid.load(reference) == modified - def test_renumber(self,default): material = default.material.copy() for m in np.unique(material): @@ -213,7 +220,6 @@ class TestGrid: assert not default == modified assert default == modified.renumber() - def test_assemble(self): cells = np.random.randint(8,16,3) N = cells.prod() @@ -221,7 +227,6 @@ class TestGrid: idx = np.random.randint(0,N,N).reshape(cells) assert (idx == g.assemble(idx).material).all - def test_substitute(self,default): offset = np.random.randint(1,500) modified = Grid(default.material + offset, @@ -257,7 +262,6 @@ class TestGrid: modified.rotate(Rotation.from_axis_angle(axis_angle,degrees=True)) assert default == modified - @pytest.mark.parametrize('Eulers',[[32.0,68.0,21.0], [0.0,32.0,240.0]]) def test_rotate(self,default,update,ref_path,Eulers): @@ -267,7 +271,6 @@ class TestGrid: if update: modified.save(reference) assert Grid.load(reference) == modified - def test_canvas_extend(self,default): cells = default.cells cells_add = np.random.randint(0,30,(3)) @@ -364,14 +367,12 @@ class TestGrid: assert random.vicinity_offset(selection=None,invert_selection=False) == random.vicinity_offset() and \ random.vicinity_offset(selection=None,invert_selection=True ) == random.vicinity_offset() - @pytest.mark.parametrize('periodic',[True,False]) def test_vicinity_offset_invariant(self,default,periodic): offset = default.vicinity_offset(selection=[default.material.max()+1, default.material.min()-1]) assert np.all(offset.material==default.material) - @pytest.mark.parametrize('periodic',[True,False]) def test_tessellation_approaches(self,periodic): cells = np.random.randint(10,20,3) @@ -382,7 +383,6 @@ class TestGrid: Laguerre = Grid.from_Laguerre_tessellation(cells,size,seeds,np.ones(N_seeds),np.arange(N_seeds)+5,periodic) assert Laguerre == Voronoi - def test_Laguerre_weights(self): cells = np.random.randint(10,20,3) size = np.random.random(3) + 1.0 @@ -394,7 +394,6 @@ class TestGrid: Laguerre = Grid.from_Laguerre_tessellation(cells,size,seeds,weights,periodic=np.random.random()>0.5) assert np.all(Laguerre.material == ms) - @pytest.mark.parametrize('approach',['Laguerre','Voronoi']) def test_tessellate_bicrystal(self,approach): cells = np.random.randint(5,10,3)*2 @@ -408,7 +407,6 @@ class TestGrid: grid = Grid.from_Voronoi_tessellation(cells,size,seeds, periodic=np.random.random()>0.5) assert np.all(grid.material == material) - @pytest.mark.parametrize('surface',['Schwarz P', 'Double Primitive', 'Schwarz D', @@ -450,7 +448,6 @@ class TestGrid: grid = Grid.from_minimal_surface(cells,np.ones(3),surface,threshold) assert np.isclose(np.count_nonzero(grid.material==1)/np.prod(grid.cells),.5,rtol=1e-3) - def test_from_table(self): cells = np.random.randint(60,100,3) size = np.ones(3)+np.random.rand(3) @@ -462,7 +459,6 @@ class TestGrid: g = Grid.from_table(t,'coords',['indicator','z']) assert g.N_materials == g.cells[0]*2 and (g.material[:,:,-1]-g.material[:,:,0] == cells[0]).all() - def test_from_table_recover(self,tmp_path): cells = np.random.randint(60,100,3) size = np.ones(3)+np.random.rand(3) @@ -472,7 +468,6 @@ class TestGrid: t = Table({'c':3,'m':1},np.column_stack((coords.reshape(-1,3,order='F'),grid.material.flatten(order='F')))) assert grid.sort().renumber() == Grid.from_table(t,'c',['m']) - @pytest.mark.parametrize('periodic',[True,False]) @pytest.mark.parametrize('direction',['x','y','z',['x','y'],'zy','xz',['x','y','z']]) @pytest.mark.xfail(int(vtk.vtkVersion.GetVTKVersion().split('.')[0])<8, reason='missing METADATA') @@ -497,7 +492,6 @@ class TestGrid: np.allclose(grain.size,point.size) and \ (grain.sort().material == point.material+1).all() - def test_load_DREAM3D_reference(self,ref_path,update): current = Grid.load_DREAM3D(ref_path/'measured.dream3d') reference = Grid.load(ref_path/'measured.vti')