Merge branch 'improved-docstrings' into 'development'
Improvements to Python docstrings See merge request damask/DAMASK!729
This commit is contained in:
commit
538770278f
|
@ -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([
|
||||
|
|
|
@ -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}'
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
--------
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)}'\
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -402,7 +402,7 @@ def displacement_node(size: _FloatSequence,
|
|||
|
||||
Returns
|
||||
-------
|
||||
u_p : numpy.ndarray, shape (:,:,:,3)
|
||||
u_n : numpy.ndarray, shape (:,:,:,3)
|
||||
Nodal displacements.
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
-------
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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')
|
||||
|
|
Loading…
Reference in New Issue