using best practices from grid_filters
This commit is contained in:
parent
9a8e7c8445
commit
2c1231a806
|
@ -3,6 +3,7 @@ import copy
|
|||
import warnings
|
||||
import multiprocessing as mp
|
||||
from functools import partial
|
||||
import typing
|
||||
from typing import Union, Optional, TextIO, List, Sequence
|
||||
from pathlib import Path
|
||||
|
||||
|
@ -16,6 +17,7 @@ from . import util
|
|||
from . import grid_filters
|
||||
from . import Rotation
|
||||
from . import Table
|
||||
from ._typehints import FloatSequence, IntSequence
|
||||
|
||||
class Grid:
|
||||
"""
|
||||
|
@ -29,9 +31,9 @@ class Grid:
|
|||
|
||||
def __init__(self,
|
||||
material: np.ndarray,
|
||||
size,
|
||||
origin = [0.0,0.0,0.0],
|
||||
comments = []):
|
||||
size: FloatSequence,
|
||||
origin: FloatSequence = np.zeros(3),
|
||||
comments: Union[str, Sequence[str]] = []):
|
||||
"""
|
||||
New geometry definition for grid solvers.
|
||||
|
||||
|
@ -40,18 +42,18 @@ class Grid:
|
|||
material : numpy.ndarray of shape (:,:,:)
|
||||
Material indices. The shape of the material array defines
|
||||
the number of cells.
|
||||
size : list or numpy.ndarray of shape (3)
|
||||
size : sequence of float, len (3)
|
||||
Physical size of grid in meter.
|
||||
origin : list or numpy.ndarray of shape (3), optional
|
||||
Coordinates of grid origin in meter.
|
||||
comments : list of str, optional
|
||||
origin : sequence of float, len (3), optional
|
||||
Coordinates of grid origin in meter. Defaults to [0.0,0.0,0.0].
|
||||
comments : (list of) str, optional
|
||||
Comments, e.g. history of operations.
|
||||
|
||||
"""
|
||||
self.material = material
|
||||
self.size = size
|
||||
self.origin = origin
|
||||
self.comments = comments
|
||||
self.size = size # type: ignore
|
||||
self.origin = origin # type: ignore
|
||||
self.comments = comments # type: ignore
|
||||
|
||||
|
||||
def __repr__(self) -> str:
|
||||
|
@ -75,7 +77,7 @@ class Grid:
|
|||
copy = __copy__
|
||||
|
||||
|
||||
def __eq__(self, other):
|
||||
def __eq__(self, other: object) -> bool:
|
||||
"""
|
||||
Test equality of other.
|
||||
|
||||
|
@ -86,8 +88,8 @@ class Grid:
|
|||
|
||||
"""
|
||||
if not isinstance(other, Grid):
|
||||
raise TypeError
|
||||
return (np.allclose(other.size,self.size)
|
||||
return NotImplemented
|
||||
return bool(np.allclose(other.size,self.size)
|
||||
and np.allclose(other.origin,self.origin)
|
||||
and np.all(other.cells == self.cells)
|
||||
and np.all(other.material == self.material))
|
||||
|
@ -118,19 +120,19 @@ class Grid:
|
|||
return self._size
|
||||
|
||||
@size.setter
|
||||
def size(self, size: Union[Sequence[float], np.ndarray]):
|
||||
def size(self, size: FloatSequence):
|
||||
if len(size) != 3 or any(np.array(size) < 0):
|
||||
raise ValueError(f'invalid size {size}')
|
||||
else:
|
||||
self._size = np.array(size)
|
||||
|
||||
@property
|
||||
def origin(self) -> Union[Sequence[float], np.ndarray]:
|
||||
def origin(self) -> np.ndarray:
|
||||
"""Coordinates of grid origin in meter."""
|
||||
return self._origin
|
||||
|
||||
@origin.setter
|
||||
def origin(self, origin: np.ndarray):
|
||||
def origin(self, origin: FloatSequence):
|
||||
if len(origin) != 3:
|
||||
raise ValueError(f'invalid origin {origin}')
|
||||
else:
|
||||
|
@ -165,7 +167,7 @@ class Grid:
|
|||
|
||||
Parameters
|
||||
----------
|
||||
fname : str or or pathlib.Path
|
||||
fname : str or pathlib.Path
|
||||
Grid file to read. Valid extension is .vti, which will be appended
|
||||
if not given.
|
||||
|
||||
|
@ -186,8 +188,9 @@ class Grid:
|
|||
comments=comments)
|
||||
|
||||
|
||||
@typing. no_type_check
|
||||
@staticmethod
|
||||
def load_ASCII(fname):
|
||||
def load_ASCII(fname)-> "Grid":
|
||||
"""
|
||||
Load from geom file.
|
||||
|
||||
|
@ -225,10 +228,10 @@ class Grid:
|
|||
comments = []
|
||||
content = f.readlines()
|
||||
for i,line in enumerate(content[:header_length]):
|
||||
items: List[str] = line.split('#')[0].lower().strip().split()
|
||||
items = line.split('#')[0].lower().strip().split()
|
||||
key = items[0] if items else ''
|
||||
if key == 'grid':
|
||||
cells = np.array([int(dict(zip(items[1::2],items[2::2]))[i]) for i in ['a','b','c']])
|
||||
cells = np.array([ int(dict(zip(items[1::2],items[2::2]))[i]) for i in ['a','b','c']])
|
||||
elif key == 'size':
|
||||
size = np.array([float(dict(zip(items[1::2],items[2::2]))[i]) for i in ['x','y','z']])
|
||||
elif key == 'origin':
|
||||
|
@ -236,7 +239,7 @@ class Grid:
|
|||
else:
|
||||
comments.append(line.strip())
|
||||
|
||||
material = np.empty(int(cells.prod())) # initialize as flat array
|
||||
material = np.empty(int(cells.prod())) # initialize as flat array
|
||||
i = 0
|
||||
for line in content[header_length:]:
|
||||
items = line.split('#')[0].split()
|
||||
|
@ -267,7 +270,7 @@ class Grid:
|
|||
|
||||
Parameters
|
||||
----------
|
||||
fname : str, pathlib.Path, or file handle
|
||||
fname : str or pathlib.Path
|
||||
Geometry file to read.
|
||||
|
||||
Returns
|
||||
|
@ -286,7 +289,7 @@ class Grid:
|
|||
|
||||
|
||||
@staticmethod
|
||||
def load_DREAM3D(fname: str,
|
||||
def load_DREAM3D(fname: Union[str, Path],
|
||||
feature_IDs: str = None, cell_data: str = None,
|
||||
phases: str = 'Phases', Euler_angles: str = 'EulerAngles',
|
||||
base_group: str = None) -> "Grid":
|
||||
|
@ -300,24 +303,24 @@ class Grid:
|
|||
|
||||
Parameters
|
||||
----------
|
||||
fname : str
|
||||
fname : str or or pathlib.Path
|
||||
Filename of the DREAM.3D (HDF5) file.
|
||||
feature_IDs : str
|
||||
feature_IDs : str, optional
|
||||
Name of the dataset containing the mapping between cells and
|
||||
grain-wise data. Defaults to 'None', in which case cell-wise
|
||||
data is used.
|
||||
cell_data : str
|
||||
cell_data : str, optional
|
||||
Name of the group (folder) containing cell-wise data. Defaults to
|
||||
None in wich case it is automatically detected.
|
||||
phases : str
|
||||
phases : str, optional
|
||||
Name of the dataset containing the phase ID. It is not used for
|
||||
grain-wise data, i.e. when feature_IDs is not None.
|
||||
Defaults to 'Phases'.
|
||||
Euler_angles : str
|
||||
Euler_angles : str, optional
|
||||
Name of the dataset containing the crystallographic orientation as
|
||||
Euler angles in radians It is not used for grain-wise data, i.e.
|
||||
when feature_IDs is not None. Defaults to 'EulerAngles'.
|
||||
base_group : str
|
||||
base_group : str, optional
|
||||
Path to the group (folder) that contains geometry (_SIMPL_GEOMETRY),
|
||||
and grain- or cell-wise data. Defaults to None, in which case
|
||||
it is set as the path that contains _SIMPL_GEOMETRY/SPACING.
|
||||
|
@ -349,7 +352,9 @@ class Grid:
|
|||
|
||||
|
||||
@staticmethod
|
||||
def from_table(table: Table, coordinates: str, labels: Union[str, Sequence[str]]) -> "Grid":
|
||||
def from_table(table: Table,
|
||||
coordinates: str,
|
||||
labels: Union[str, Sequence[str]]) -> "Grid":
|
||||
"""
|
||||
Create grid from ASCII table.
|
||||
|
||||
|
@ -360,7 +365,7 @@ class Grid:
|
|||
coordinates : str
|
||||
Label of the vector column containing the spatial coordinates.
|
||||
Need to be ordered (1./x fast, 3./z slow).
|
||||
labels : str or list of str
|
||||
labels : (list of) str
|
||||
Label(s) of the columns containing the material definition.
|
||||
Each unique combination of values results in one material ID.
|
||||
|
||||
|
@ -386,26 +391,26 @@ class Grid:
|
|||
return np.argmin(np.sum((np.broadcast_to(point,(len(seeds),3))-seeds)**2,axis=1) - weights)
|
||||
|
||||
@staticmethod
|
||||
def from_Laguerre_tessellation(cells,
|
||||
size,
|
||||
seeds,
|
||||
weights,
|
||||
material = None,
|
||||
periodic = True):
|
||||
def from_Laguerre_tessellation(cells: IntSequence,
|
||||
size: FloatSequence,
|
||||
seeds: np.ndarray,
|
||||
weights: FloatSequence,
|
||||
material: IntSequence = None,
|
||||
periodic: bool = True):
|
||||
"""
|
||||
Create grid from Laguerre tessellation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cells : int numpy.ndarray of shape (3)
|
||||
cells : sequence of int, len (3)
|
||||
Number of cells in x,y,z direction.
|
||||
size : list or numpy.ndarray of shape (3)
|
||||
size : sequence of float, len (3)
|
||||
Physical size of the grid in meter.
|
||||
seeds : numpy.ndarray of shape (:,3)
|
||||
Position of the seed points in meter. All points need to lay within the box.
|
||||
weights : numpy.ndarray of shape (seeds.shape[0])
|
||||
weights : sequence of float, len (seeds.shape[0])
|
||||
Weights of the seeds. Setting all weights to 1.0 gives a standard Voronoi tessellation.
|
||||
material : numpy.ndarray of shape (seeds.shape[0]), optional
|
||||
material : sequence of int, len (seeds.shape[0]), optional
|
||||
Material ID of the seeds.
|
||||
Defaults to None, in which case materials are consecutively numbered.
|
||||
periodic : Boolean, optional
|
||||
|
@ -427,6 +432,7 @@ class Grid:
|
|||
seeds_p = seeds
|
||||
|
||||
coords = grid_filters.coordinates0_point(cells,size).reshape(-1,3)
|
||||
|
||||
pool = mp.Pool(int(os.environ.get('OMP_NUM_THREADS',4)))
|
||||
result = pool.map_async(partial(Grid._find_closest_seed,seeds_p,weights_p), coords)
|
||||
pool.close()
|
||||
|
@ -435,30 +441,30 @@ class Grid:
|
|||
|
||||
if periodic: material_ %= len(weights)
|
||||
|
||||
return Grid(material = material_ if material is None else material[material_],
|
||||
return Grid(material = material_ if material is None else np.array(material)[material_],
|
||||
size = size,
|
||||
comments = util.execution_stamp('Grid','from_Laguerre_tessellation'),
|
||||
)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def from_Voronoi_tessellation(cells: np.ndarray,
|
||||
size: Union[Sequence[float], np.ndarray],
|
||||
def from_Voronoi_tessellation(cells: IntSequence,
|
||||
size: FloatSequence,
|
||||
seeds: np.ndarray,
|
||||
material: np.ndarray = None,
|
||||
material: IntSequence = None,
|
||||
periodic: bool = True) -> "Grid":
|
||||
"""
|
||||
Create grid from Voronoi tessellation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cells : int numpy.ndarray of shape (3)
|
||||
cells : sequence of int, len (3)
|
||||
Number of cells in x,y,z direction.
|
||||
size : list or numpy.ndarray of shape (3)
|
||||
size : sequence of float, len (3)
|
||||
Physical size of the grid in meter.
|
||||
seeds : numpy.ndarray of shape (:,3)
|
||||
Position of the seed points in meter. All points need to lay within the box.
|
||||
material : numpy.ndarray of shape (seeds.shape[0]), optional
|
||||
material : sequence of int, len (seeds.shape[0]), optional
|
||||
Material ID of the seeds.
|
||||
Defaults to None, in which case materials are consecutively numbered.
|
||||
periodic : Boolean, optional
|
||||
|
@ -478,7 +484,7 @@ class Grid:
|
|||
except TypeError:
|
||||
material_ = tree.query(coords, n_jobs = int(os.environ.get('OMP_NUM_THREADS',4)))[1] # scipy <1.6
|
||||
|
||||
return Grid(material = (material_ if material is None else material[material_]).reshape(cells),
|
||||
return Grid(material = (material_ if material is None else np.array(material)[material_]).reshape(cells),
|
||||
size = size,
|
||||
comments = util.execution_stamp('Grid','from_Voronoi_tessellation'),
|
||||
)
|
||||
|
@ -527,20 +533,20 @@ class Grid:
|
|||
|
||||
|
||||
@staticmethod
|
||||
def from_minimal_surface(cells: np.ndarray,
|
||||
size: Union[Sequence[float], np.ndarray],
|
||||
def from_minimal_surface(cells: IntSequence,
|
||||
size: FloatSequence,
|
||||
surface: str,
|
||||
threshold: float = 0.0,
|
||||
periods: int = 1,
|
||||
materials: tuple = (0,1)) -> "Grid":
|
||||
materials: IntSequence = (0,1)) -> "Grid":
|
||||
"""
|
||||
Create grid from definition of triply periodic minimal surface.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cells : int numpy.ndarray of shape (3)
|
||||
cells : sequence of int, len (3)
|
||||
Number of cells in x,y,z direction.
|
||||
size : list or numpy.ndarray of shape (3)
|
||||
size : sequence of float, len (3)
|
||||
Physical size of the grid in meter.
|
||||
surface : str
|
||||
Type of the minimal surface. See notes for details.
|
||||
|
@ -548,7 +554,7 @@ class Grid:
|
|||
Threshold of the minimal surface. Defaults to 0.0.
|
||||
periods : integer, optional.
|
||||
Number of periods per unit cell. Defaults to 1.
|
||||
materials : (int, int), optional
|
||||
materials : sequence of int, len (2)
|
||||
Material IDs. Defaults to (0,1).
|
||||
|
||||
Returns
|
||||
|
@ -589,22 +595,21 @@ class Grid:
|
|||
|
||||
>>> import numpy as np
|
||||
>>> import damask
|
||||
>>> damask.Grid.from_minimal_surface(np.array([64]*3,int),np.ones(3),
|
||||
... 'Gyroid')
|
||||
cells a b c: 64 x 64 x 64
|
||||
size x y z: 1.0 x 1.0 x 1.0
|
||||
origin x y z: 0.0 0.0 0.0
|
||||
>>> 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³
|
||||
origin: 0.0 0.0 0.0 / m
|
||||
# materials: 2
|
||||
|
||||
Minimal surface of 'Neovius' type. non-default material IDs.
|
||||
|
||||
>>> import numpy as np
|
||||
>>> import damask
|
||||
>>> damask.Grid.from_minimal_surface(np.array([80]*3,int),np.ones(3),
|
||||
>>> damask.Grid.from_minimal_surface([80]*3,np.ones(3)*5.e-4,
|
||||
... 'Neovius',materials=(1,5))
|
||||
cells a b c: 80 x 80 x 80
|
||||
size x y z: 1.0 x 1.0 x 1.0
|
||||
origin x y z: 0.0 0.0 0.0
|
||||
cells : 80 x 80 x 80
|
||||
size : 0.0005 x 0.0005 x 0.0005 / m³
|
||||
origin: 0.0 0.0 0.0 / m
|
||||
# materials: 2 (min: 1, max: 5)
|
||||
|
||||
"""
|
||||
|
@ -634,7 +639,7 @@ class Grid:
|
|||
v.add(self.material.flatten(order='F'),'material')
|
||||
v.add_comments(self.comments)
|
||||
|
||||
v.save(fname if str(fname).endswith('.vti') else str(fname)+'.vti',parallel=False,compress=compress)
|
||||
v.save(fname,parallel=False,compress=compress)
|
||||
|
||||
|
||||
def save_ASCII(self, fname: Union[str, TextIO]):
|
||||
|
@ -667,15 +672,15 @@ class Grid:
|
|||
header='\n'.join(header), fmt=format_string, comments='')
|
||||
|
||||
|
||||
def show(self):
|
||||
def show(self) -> None:
|
||||
"""Show on screen."""
|
||||
VTK.from_rectilinear_grid(self.cells,self.size,self.origin).show()
|
||||
|
||||
|
||||
def add_primitive(self,
|
||||
dimension: np.ndarray,
|
||||
center: np.ndarray,
|
||||
exponent: Union[np.ndarray, float],
|
||||
dimension: Union[FloatSequence, IntSequence],
|
||||
center: Union[FloatSequence, IntSequence],
|
||||
exponent: Union[FloatSequence, float],
|
||||
fill: int = None,
|
||||
R: Rotation = Rotation(),
|
||||
inverse: bool = False,
|
||||
|
@ -685,14 +690,15 @@ class Grid:
|
|||
|
||||
Parameters
|
||||
----------
|
||||
dimension : int or float numpy.ndarray of shape (3)
|
||||
Dimension (diameter/side length) of the primitive. If given as
|
||||
integers, cell centers are addressed.
|
||||
If given as floats, coordinates are addressed.
|
||||
center : int or float numpy.ndarray of shape (3)
|
||||
Center of the primitive. If given as integers, cell centers are addressed.
|
||||
If given as floats, coordinates in space are addressed.
|
||||
exponent : numpy.ndarray of shape (3) or float
|
||||
dimension : sequence of int or float, len (3)
|
||||
Dimension (diameter/side length) of the primitive.
|
||||
If given as integers, cell centers are addressed.
|
||||
If given as floats, physical coordinates are addressed.
|
||||
center : sequence of int or float, len (3)
|
||||
Center of the primitive.
|
||||
If given as integers, cell centers are addressed.
|
||||
If given as floats, physical coordinates are addressed.
|
||||
exponent : float or sequence of float, len (3)
|
||||
Exponents for the three axes.
|
||||
0 gives octahedron (ǀxǀ^(2^0) + ǀyǀ^(2^0) + ǀzǀ^(2^0) < 1)
|
||||
1 gives sphere (ǀxǀ^(2^1) + ǀyǀ^(2^1) + ǀzǀ^(2^1) < 1)
|
||||
|
@ -719,9 +725,9 @@ 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 a b c: 64 x 64 x 64
|
||||
size x y z: 0.0001 x 0.0001 x 0.0001
|
||||
origin x y z: 0.0 0.0 0.0
|
||||
cells : 64 x 64 x 64
|
||||
size : 0.0001 x 0.0001 x 0.0001 / m³
|
||||
origin: 0.0 0.0 0.0 / m
|
||||
# materials: 2
|
||||
|
||||
Add a cube at the origin.
|
||||
|
@ -730,9 +736,9 @@ 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 a b c: 64 x 64 x 64
|
||||
size x y z: 0.0001 x 0.0001 x 0.0001
|
||||
origin x y z: 0.0 0.0 0.0
|
||||
cells : 64 x 64 x 64
|
||||
size : 0.0001 x 0.0001 x 0.0001 / m³
|
||||
origin: 0.0 0.0 0.0 / m
|
||||
# materials: 2
|
||||
|
||||
"""
|
||||
|
@ -769,7 +775,7 @@ class Grid:
|
|||
|
||||
Parameters
|
||||
----------
|
||||
directions : iterable containing str
|
||||
directions : (sequence of) str
|
||||
Direction(s) along which the grid is mirrored.
|
||||
Valid entries are 'x', 'y', 'z'.
|
||||
reflect : bool, optional
|
||||
|
@ -788,9 +794,9 @@ class Grid:
|
|||
>>> import damask
|
||||
>>> g = damask.Grid(np.zeros([32]*3,int), np.ones(3)*1e-4)
|
||||
>>> g.mirror('xy',True)
|
||||
cells a b c: 64 x 64 x 32
|
||||
size x y z: 0.0002 x 0.0002 x 0.0001
|
||||
origin x y z: 0.0 0.0 0.0
|
||||
cells : 64 x 64 x 32
|
||||
size : 0.0002 x 0.0002 x 0.0001 / m³
|
||||
origin: 0.0 0.0 0.0 / m
|
||||
# materials: 1
|
||||
|
||||
"""
|
||||
|
@ -821,7 +827,7 @@ class Grid:
|
|||
|
||||
Parameters
|
||||
----------
|
||||
directions : iterable containing str
|
||||
directions : (sequence of) str
|
||||
Direction(s) along which the grid is flipped.
|
||||
Valid entries are 'x', 'y', 'z'.
|
||||
|
||||
|
@ -844,13 +850,13 @@ class Grid:
|
|||
)
|
||||
|
||||
|
||||
def scale(self, cells: np.ndarray, periodic: bool = True) -> "Grid":
|
||||
def scale(self, cells: IntSequence, periodic: bool = True) -> "Grid":
|
||||
"""
|
||||
Scale grid to new cells.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cells : numpy.ndarray of shape (3)
|
||||
cells : sequence of int, len (3)
|
||||
Number of cells in x,y,z direction.
|
||||
periodic : Boolean, optional
|
||||
Assume grid to be periodic. Defaults to True.
|
||||
|
@ -868,9 +874,9 @@ class Grid:
|
|||
>>> import damask
|
||||
>>> g = damask.Grid(np.zeros([32]*3,int),np.ones(3)*1e-4)
|
||||
>>> g.scale(g.cells*2)
|
||||
cells a b c: 64 x 64 x 64
|
||||
size x y z: 0.0001 x 0.0001 x 0.0001
|
||||
origin x y z: 0.0 0.0 0.0
|
||||
cells : 64 x 64 x 64
|
||||
size : 0.0001 x 0.0001 x 0.0001 / m³
|
||||
origin: 0.0 0.0 0.0 / m
|
||||
# materials: 1
|
||||
|
||||
"""
|
||||
|
@ -888,7 +894,10 @@ class Grid:
|
|||
)
|
||||
|
||||
|
||||
def clean(self, stencil: int = 3, selection: Sequence[float] = None, periodic: bool = True) -> "Grid":
|
||||
def clean(self,
|
||||
stencil: int = 3,
|
||||
selection: IntSequence = None,
|
||||
periodic: bool = True) -> "Grid":
|
||||
"""
|
||||
Smooth grid by selecting most frequent material index within given stencil at each location.
|
||||
|
||||
|
@ -896,7 +905,7 @@ class Grid:
|
|||
----------
|
||||
stencil : int, optional
|
||||
Size of smoothing stencil.
|
||||
selection : list, optional
|
||||
selection : sequence of int, optional
|
||||
Field values that can be altered. Defaults to all.
|
||||
periodic : Boolean, optional
|
||||
Assume grid to be periodic. Defaults to True.
|
||||
|
@ -907,7 +916,7 @@ class Grid:
|
|||
Updated grid-based geometry.
|
||||
|
||||
"""
|
||||
def mostFrequent(arr, selection = None):
|
||||
def mostFrequent(arr: np.ndarray, selection = None):
|
||||
me = arr[arr.size//2]
|
||||
if selection is None or me in selection:
|
||||
unique, inverse = np.unique(arr, return_inverse=True)
|
||||
|
@ -947,7 +956,7 @@ class Grid:
|
|||
)
|
||||
|
||||
|
||||
def rotate(self, R: Rotation, fill: Union[int, float] = None) -> "Grid":
|
||||
def rotate(self, R: Rotation, fill: int = None) -> "Grid":
|
||||
"""
|
||||
Rotate grid (pad if required).
|
||||
|
||||
|
@ -955,7 +964,7 @@ class Grid:
|
|||
----------
|
||||
R : damask.Rotation
|
||||
Rotation to apply to the grid.
|
||||
fill : int or float, optional
|
||||
fill : int, optional
|
||||
Material index to fill the corners. Defaults to material.max() + 1.
|
||||
|
||||
Returns
|
||||
|
@ -985,17 +994,20 @@ class Grid:
|
|||
)
|
||||
|
||||
|
||||
def canvas(self, cells = None, offset = None, fill = None):
|
||||
def canvas(self,
|
||||
cells: IntSequence = None,
|
||||
offset: IntSequence = None,
|
||||
fill: int = None) -> "Grid":
|
||||
"""
|
||||
Crop or enlarge/pad grid.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cells : numpy.ndarray of shape (3)
|
||||
cells : sequence of int, len (3), optional
|
||||
Number of cells x,y,z direction.
|
||||
offset : numpy.ndarray of shape (3)
|
||||
offset : sequence of int, len (3), optional
|
||||
Offset (measured in cells) from old to new grid [0,0,0].
|
||||
fill : int or float, optional
|
||||
fill : int, optional
|
||||
Material index to fill the background. Defaults to material.max() + 1.
|
||||
|
||||
Returns
|
||||
|
@ -1010,42 +1022,43 @@ class Grid:
|
|||
>>> import numpy as np
|
||||
>>> import damask
|
||||
>>> g = damask.Grid(np.zeros([32]*3,int),np.ones(3)*1e-4)
|
||||
>>> g.canvas(np.array([32,32,16],int))
|
||||
cells a b c: 33 x 32 x 16
|
||||
size x y z: 0.0001 x 0.0001 x 5e-05
|
||||
origin x y z: 0.0 0.0 0.0
|
||||
>>> g.canvas([32,32,16])
|
||||
cells : 33 x 32 x 16
|
||||
size : 0.0001 x 0.0001 x 5e-05 / m³
|
||||
origin: 0.0 0.0 0.0 / m
|
||||
# materials: 1
|
||||
|
||||
"""
|
||||
if offset is None: offset = 0
|
||||
offset_ = np.array(offset,int) if offset is not None else np.zeros(3,int)
|
||||
cells_ = np.array(cells,int) if cells is not None else self.cells
|
||||
if fill is None: fill = np.nanmax(self.material) + 1
|
||||
dtype = float if int(fill) != fill or self.material.dtype in np.sctypes['float'] else int
|
||||
|
||||
canvas = np.full(self.cells if cells is None else cells,fill,dtype)
|
||||
canvas = np.full(cells_,fill,dtype)
|
||||
|
||||
LL = np.clip( offset, 0,np.minimum(self.cells, cells+offset))
|
||||
UR = np.clip( offset+cells, 0,np.minimum(self.cells, cells+offset))
|
||||
ll = np.clip(-offset, 0,np.minimum( cells,self.cells-offset))
|
||||
ur = np.clip(-offset+self.cells,0,np.minimum( cells,self.cells-offset))
|
||||
LL = np.clip( offset_, 0,np.minimum(self.cells, cells_+offset_))
|
||||
UR = np.clip( offset_+cells_, 0,np.minimum(self.cells, cells_+offset_))
|
||||
ll = np.clip(-offset_, 0,np.minimum( cells_,self.cells-offset_))
|
||||
ur = np.clip(-offset_+self.cells,0,np.minimum( cells_,self.cells-offset_))
|
||||
|
||||
canvas[ll[0]:ur[0],ll[1]:ur[1],ll[2]:ur[2]] = self.material[LL[0]:UR[0],LL[1]:UR[1],LL[2]:UR[2]]
|
||||
|
||||
return Grid(material = canvas,
|
||||
size = self.size/self.cells*np.asarray(canvas.shape),
|
||||
origin = self.origin+offset*self.size/self.cells,
|
||||
origin = self.origin+offset_*self.size/self.cells,
|
||||
comments = self.comments+[util.execution_stamp('Grid','canvas')],
|
||||
)
|
||||
|
||||
|
||||
def substitute(self, from_material: np.ndarray, to_material: np.ndarray) -> "Grid":
|
||||
def substitute(self, from_material: IntSequence, to_material: IntSequence) -> "Grid":
|
||||
"""
|
||||
Substitute material indices.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
from_material : iterable of ints
|
||||
from_material : sequence of int
|
||||
Material indices to be substituted.
|
||||
to_material : iterable of ints
|
||||
to_material : sequence of int
|
||||
New material indices.
|
||||
|
||||
Returns
|
||||
|
@ -1092,7 +1105,7 @@ class Grid:
|
|||
def vicinity_offset(self,
|
||||
vicinity: int = 1,
|
||||
offset: int = None,
|
||||
trigger: Sequence[int] = [],
|
||||
trigger: IntSequence = [],
|
||||
periodic: bool = True) -> "Grid":
|
||||
"""
|
||||
Offset material index of points in the vicinity of xxx.
|
||||
|
@ -1109,7 +1122,7 @@ class Grid:
|
|||
offset : int, optional
|
||||
Offset (positive or negative) to tag material indices,
|
||||
defaults to material.max()+1.
|
||||
trigger : list of ints, optional
|
||||
trigger : sequence of int, optional
|
||||
List of material indices that trigger a change.
|
||||
Defaults to [], meaning that any different neighbor triggers a change.
|
||||
periodic : Boolean, optional
|
||||
|
@ -1121,7 +1134,7 @@ class Grid:
|
|||
Updated grid-based geometry.
|
||||
|
||||
"""
|
||||
def tainted_neighborhood(stencil, trigger):
|
||||
def tainted_neighborhood(stencil: np.ndarray, trigger):
|
||||
me = stencil[stencil.shape[0]//2]
|
||||
return np.any(stencil != me if len(trigger) == 0 else
|
||||
np.in1d(stencil,np.array(list(set(trigger) - {me}))))
|
||||
|
@ -1140,7 +1153,7 @@ class Grid:
|
|||
)
|
||||
|
||||
|
||||
def get_grain_boundaries(self, periodic = True, directions = 'xyz'):
|
||||
def get_grain_boundaries(self, periodic: bool = True, directions: Sequence[str] = 'xyz'):
|
||||
"""
|
||||
Create VTK unstructured grid containing grain boundaries.
|
||||
|
||||
|
@ -1148,7 +1161,7 @@ class Grid:
|
|||
----------
|
||||
periodic : Boolean, optional
|
||||
Assume grid to be periodic. Defaults to True.
|
||||
directions : iterable containing str, optional
|
||||
directions : (sequence of) string, optional
|
||||
Direction(s) along which the boundaries are determined.
|
||||
Valid entries are 'x', 'y', 'z'. Defaults to 'xyz'.
|
||||
|
||||
|
|
|
@ -419,7 +419,7 @@ class VTK:
|
|||
return writer.GetOutputString()
|
||||
|
||||
|
||||
def show(self):
|
||||
def show(self) -> None:
|
||||
"""
|
||||
Render.
|
||||
|
||||
|
|
|
@ -237,12 +237,27 @@ class TestGrid:
|
|||
modified)
|
||||
|
||||
|
||||
def test_canvas(self,default):
|
||||
def test_canvas_extend(self,default):
|
||||
cells = default.cells
|
||||
grid_add = np.random.randint(0,30,(3))
|
||||
modified = default.canvas(cells + grid_add)
|
||||
cells_add = np.random.randint(0,30,(3))
|
||||
modified = default.canvas(cells + cells_add)
|
||||
assert np.all(modified.material[:cells[0],:cells[1],:cells[2]] == default.material)
|
||||
|
||||
@pytest.mark.parametrize('sign',[+1,-1])
|
||||
@pytest.mark.parametrize('extra_offset',[0,-1])
|
||||
def test_canvas_move_out(self,sign,extra_offset):
|
||||
g = Grid(np.zeros(np.random.randint(3,30,(3)),int),np.ones(3))
|
||||
o = sign*np.ones(3)*g.cells.min() +extra_offset*sign
|
||||
if extra_offset == 0:
|
||||
assert np.all(g.canvas(offset=o).material == 1)
|
||||
else:
|
||||
assert np.all(np.unique(g.canvas(offset=o).material) == (0,1))
|
||||
|
||||
def test_canvas_cells(self,default):
|
||||
g = Grid(np.zeros(np.random.randint(3,30,(3)),int),np.ones(3))
|
||||
cells = np.random.randint(1,30,(3))
|
||||
offset = np.random.randint(-30,30,(3))
|
||||
assert np.all(g.canvas(cells,offset).cells == cells)
|
||||
|
||||
@pytest.mark.parametrize('center1,center2',[(np.random.random(3)*.5,np.random.random()*8),
|
||||
(np.random.randint(4,8,(3)),np.random.randint(9,12,(3)))])
|
||||
|
|
Loading…
Reference in New Issue