Merge branch 'typehints_grid' into 'development'
added fist typehints for _grid module See merge request damask/DAMASK!474
This commit is contained in:
commit
a000e477cf
|
@ -205,7 +205,7 @@ class Colormap(mpl.colors.ListedColormap):
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
color : np.ndarray, shape(...,4)
|
color : numpy.ndarray, shape(...,4)
|
||||||
RGBA values of interpolated color(s).
|
RGBA values of interpolated color(s).
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
|
|
|
@ -3,6 +3,9 @@ import copy
|
||||||
import warnings
|
import warnings
|
||||||
import multiprocessing as mp
|
import multiprocessing as mp
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
import typing
|
||||||
|
from typing import Union, Optional, TextIO, List, Sequence
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
@ -13,7 +16,8 @@ from . import VTK
|
||||||
from . import util
|
from . import util
|
||||||
from . import grid_filters
|
from . import grid_filters
|
||||||
from . import Rotation
|
from . import Rotation
|
||||||
|
from . import Table
|
||||||
|
from ._typehints import FloatSequence, IntSequence
|
||||||
|
|
||||||
class Grid:
|
class Grid:
|
||||||
"""
|
"""
|
||||||
|
@ -25,30 +29,34 @@ class Grid:
|
||||||
the physical size.
|
the physical size.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self,material,size,origin=[0.0,0.0,0.0],comments=[]):
|
def __init__(self,
|
||||||
|
material: np.ndarray,
|
||||||
|
size: FloatSequence,
|
||||||
|
origin: FloatSequence = np.zeros(3),
|
||||||
|
comments: Union[str, Sequence[str]] = []):
|
||||||
"""
|
"""
|
||||||
New geometry definition for grid solvers.
|
New geometry definition for grid solvers.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
material : numpy.ndarray of shape (:,:,:)
|
material : numpy.ndarray, shape (:,:,:)
|
||||||
Material indices. The shape of the material array defines
|
Material indices. The shape of the material array defines
|
||||||
the number of cells.
|
the number of cells.
|
||||||
size : list or numpy.ndarray of shape (3)
|
size : sequence of float, len (3)
|
||||||
Physical size of grid in meter.
|
Physical size of grid in meter.
|
||||||
origin : list or numpy.ndarray of shape (3), optional
|
origin : sequence of float, len (3), optional
|
||||||
Coordinates of grid origin in meter.
|
Coordinates of grid origin in meter. Defaults to [0.0,0.0,0.0].
|
||||||
comments : list of str, optional
|
comments : (list of) str, optional
|
||||||
Comments, e.g. history of operations.
|
Comments, e.g. history of operations.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.material = material
|
self.material = material
|
||||||
self.size = size
|
self.size = size # type: ignore
|
||||||
self.origin = origin
|
self.origin = origin # type: ignore
|
||||||
self.comments = comments
|
self.comments = comments # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
"""Basic information on grid definition."""
|
"""Basic information on grid definition."""
|
||||||
mat_min = np.nanmin(self.material)
|
mat_min = np.nanmin(self.material)
|
||||||
mat_max = np.nanmax(self.material)
|
mat_max = np.nanmax(self.material)
|
||||||
|
@ -62,14 +70,14 @@ class Grid:
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
def __copy__(self):
|
def __copy__(self) -> "Grid":
|
||||||
"""Create deep copy."""
|
"""Create deep copy."""
|
||||||
return copy.deepcopy(self)
|
return copy.deepcopy(self)
|
||||||
|
|
||||||
copy = __copy__
|
copy = __copy__
|
||||||
|
|
||||||
|
|
||||||
def __eq__(self,other):
|
def __eq__(self, other: object) -> bool:
|
||||||
"""
|
"""
|
||||||
Test equality of other.
|
Test equality of other.
|
||||||
|
|
||||||
|
@ -79,22 +87,24 @@ class Grid:
|
||||||
Grid to compare self against.
|
Grid to compare self against.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return (np.allclose(other.size,self.size)
|
if not isinstance(other, Grid):
|
||||||
|
return NotImplemented
|
||||||
|
return bool(np.allclose(other.size,self.size)
|
||||||
and np.allclose(other.origin,self.origin)
|
and np.allclose(other.origin,self.origin)
|
||||||
and np.all(other.cells == self.cells)
|
and np.all(other.cells == self.cells)
|
||||||
and np.all(other.material == self.material))
|
and np.all(other.material == self.material))
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def material(self):
|
def material(self) -> np.ndarray:
|
||||||
"""Material indices."""
|
"""Material indices."""
|
||||||
return self._material
|
return self._material
|
||||||
|
|
||||||
@material.setter
|
@material.setter
|
||||||
def material(self,material):
|
def material(self, material: np.ndarray):
|
||||||
if len(material.shape) != 3:
|
if len(material.shape) != 3:
|
||||||
raise ValueError(f'invalid material shape {material.shape}')
|
raise ValueError(f'invalid material shape {material.shape}')
|
||||||
elif material.dtype not in np.sctypes['float'] + np.sctypes['int']:
|
elif material.dtype not in np.sctypes['float'] and material.dtype not in np.sctypes['int']:
|
||||||
raise TypeError(f'invalid material data type {material.dtype}')
|
raise TypeError(f'invalid material data type {material.dtype}')
|
||||||
else:
|
else:
|
||||||
self._material = np.copy(material)
|
self._material = np.copy(material)
|
||||||
|
@ -105,59 +115,59 @@ class Grid:
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def size(self):
|
def size(self) -> np.ndarray:
|
||||||
"""Physical size of grid in meter."""
|
"""Physical size of grid in meter."""
|
||||||
return self._size
|
return self._size
|
||||||
|
|
||||||
@size.setter
|
@size.setter
|
||||||
def size(self,size):
|
def size(self, size: FloatSequence):
|
||||||
if len(size) != 3 or any(np.array(size) < 0):
|
if len(size) != 3 or any(np.array(size) < 0):
|
||||||
raise ValueError(f'invalid size {size}')
|
raise ValueError(f'invalid size {size}')
|
||||||
else:
|
else:
|
||||||
self._size = np.array(size)
|
self._size = np.array(size)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def origin(self):
|
def origin(self) -> np.ndarray:
|
||||||
"""Coordinates of grid origin in meter."""
|
"""Coordinates of grid origin in meter."""
|
||||||
return self._origin
|
return self._origin
|
||||||
|
|
||||||
@origin.setter
|
@origin.setter
|
||||||
def origin(self,origin):
|
def origin(self, origin: FloatSequence):
|
||||||
if len(origin) != 3:
|
if len(origin) != 3:
|
||||||
raise ValueError(f'invalid origin {origin}')
|
raise ValueError(f'invalid origin {origin}')
|
||||||
else:
|
else:
|
||||||
self._origin = np.array(origin)
|
self._origin = np.array(origin)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def comments(self):
|
def comments(self) -> List[str]:
|
||||||
"""Comments, e.g. history of operations."""
|
"""Comments, e.g. history of operations."""
|
||||||
return self._comments
|
return self._comments
|
||||||
|
|
||||||
@comments.setter
|
@comments.setter
|
||||||
def comments(self,comments):
|
def comments(self, comments: Union[str, Sequence[str]]):
|
||||||
self._comments = [str(c) for c in comments] if isinstance(comments,list) else [str(comments)]
|
self._comments = [str(c) for c in comments] if isinstance(comments,list) else [str(comments)]
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cells(self):
|
def cells(self) -> np.ndarray:
|
||||||
"""Number of cells in x,y,z direction."""
|
"""Number of cells in x,y,z direction."""
|
||||||
return np.asarray(self.material.shape)
|
return np.asarray(self.material.shape)
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def N_materials(self):
|
def N_materials(self) -> int:
|
||||||
"""Number of (unique) material indices within grid."""
|
"""Number of (unique) material indices within grid."""
|
||||||
return np.unique(self.material).size
|
return np.unique(self.material).size
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load(fname):
|
def load(fname: Union[str, Path]) -> "Grid":
|
||||||
"""
|
"""
|
||||||
Load from VTK image data file.
|
Load from VTK image data file.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
fname : str or or pathlib.Path
|
fname : str or pathlib.Path
|
||||||
Grid file to read. Valid extension is .vti, which will be appended
|
Grid file to read. Valid extension is .vti, which will be appended
|
||||||
if not given.
|
if not given.
|
||||||
|
|
||||||
|
@ -178,8 +188,9 @@ class Grid:
|
||||||
comments=comments)
|
comments=comments)
|
||||||
|
|
||||||
|
|
||||||
|
@typing. no_type_check
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_ASCII(fname):
|
def load_ASCII(fname)-> "Grid":
|
||||||
"""
|
"""
|
||||||
Load from geom file.
|
Load from geom file.
|
||||||
|
|
||||||
|
@ -198,15 +209,17 @@ class Grid:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
warnings.warn('Support for ASCII-based geom format will be removed in DAMASK 3.0.0', DeprecationWarning,2)
|
warnings.warn('Support for ASCII-based geom format will be removed in DAMASK 3.0.0', DeprecationWarning,2)
|
||||||
try:
|
if isinstance(fname, (str, Path)):
|
||||||
f = open(fname)
|
f = open(fname)
|
||||||
except TypeError:
|
elif isinstance(fname, TextIO):
|
||||||
f = fname
|
f = fname
|
||||||
|
else:
|
||||||
|
raise TypeError
|
||||||
|
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
try:
|
try:
|
||||||
header_length,keyword = f.readline().split()[:2]
|
header_length_,keyword = f.readline().split()[:2]
|
||||||
header_length = int(header_length)
|
header_length = int(header_length_)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
header_length,keyword = (-1, 'invalid')
|
header_length,keyword = (-1, 'invalid')
|
||||||
if not keyword.startswith('head') or header_length < 3:
|
if not keyword.startswith('head') or header_length < 3:
|
||||||
|
@ -226,19 +239,19 @@ class Grid:
|
||||||
else:
|
else:
|
||||||
comments.append(line.strip())
|
comments.append(line.strip())
|
||||||
|
|
||||||
material = np.empty(cells.prod()) # initialize as flat array
|
material = np.empty(int(cells.prod())) # initialize as flat array
|
||||||
i = 0
|
i = 0
|
||||||
for line in content[header_length:]:
|
for line in content[header_length:]:
|
||||||
items = line.split('#')[0].split()
|
items = line.split('#')[0].split()
|
||||||
if len(items) == 3:
|
if len(items) == 3:
|
||||||
if items[1].lower() == 'of':
|
if items[1].lower() == 'of':
|
||||||
items = np.ones(int(items[0]))*float(items[2])
|
material_entry = np.ones(int(items[0]))*float(items[2])
|
||||||
elif items[1].lower() == 'to':
|
elif items[1].lower() == 'to':
|
||||||
items = np.linspace(int(items[0]),int(items[2]),
|
material_entry = np.linspace(int(items[0]),int(items[2]),
|
||||||
abs(int(items[2])-int(items[0]))+1,dtype=float)
|
abs(int(items[2])-int(items[0]))+1,dtype=float)
|
||||||
else: items = list(map(float,items))
|
else: material_entry = list(map(float, items))
|
||||||
else: items = list(map(float,items))
|
else: material_entry = list(map(float, items))
|
||||||
material[i:i+len(items)] = items
|
material[i:i+len(material_entry)] = material_entry
|
||||||
i += len(items)
|
i += len(items)
|
||||||
|
|
||||||
if i != cells.prod():
|
if i != cells.prod():
|
||||||
|
@ -251,13 +264,13 @@ class Grid:
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_Neper(fname):
|
def load_Neper(fname: Union[str, Path]) -> "Grid":
|
||||||
"""
|
"""
|
||||||
Load from Neper VTK file.
|
Load from Neper VTK file.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
fname : str, pathlib.Path, or file handle
|
fname : str or pathlib.Path
|
||||||
Geometry file to read.
|
Geometry file to read.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
|
@ -276,10 +289,10 @@ class Grid:
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_DREAM3D(fname,
|
def load_DREAM3D(fname: Union[str, Path],
|
||||||
feature_IDs=None,cell_data=None,
|
feature_IDs: str = None, cell_data: str = None,
|
||||||
phases='Phases',Euler_angles='EulerAngles',
|
phases: str = 'Phases', Euler_angles: str = 'EulerAngles',
|
||||||
base_group=None):
|
base_group: str = None) -> "Grid":
|
||||||
"""
|
"""
|
||||||
Load DREAM.3D (HDF5) file.
|
Load DREAM.3D (HDF5) file.
|
||||||
|
|
||||||
|
@ -290,24 +303,24 @@ class Grid:
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
fname : str
|
fname : str or or pathlib.Path
|
||||||
Filename of the DREAM.3D (HDF5) file.
|
Filename of the DREAM.3D (HDF5) file.
|
||||||
feature_IDs : str
|
feature_IDs : str, optional
|
||||||
Name of the dataset containing the mapping between cells and
|
Name of the dataset containing the mapping between cells and
|
||||||
grain-wise data. Defaults to 'None', in which case cell-wise
|
grain-wise data. Defaults to 'None', in which case cell-wise
|
||||||
data is used.
|
data is used.
|
||||||
cell_data : str
|
cell_data : str, optional
|
||||||
Name of the group (folder) containing cell-wise data. Defaults to
|
Name of the group (folder) containing cell-wise data. Defaults to
|
||||||
None in wich case it is automatically detected.
|
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
|
Name of the dataset containing the phase ID. It is not used for
|
||||||
grain-wise data, i.e. when feature_IDs is not None.
|
grain-wise data, i.e. when feature_IDs is not None.
|
||||||
Defaults to 'Phases'.
|
Defaults to 'Phases'.
|
||||||
Euler_angles : str
|
Euler_angles : str, optional
|
||||||
Name of the dataset containing the crystallographic orientation as
|
Name of the dataset containing the crystallographic orientation as
|
||||||
Euler angles in radians It is not used for grain-wise data, i.e.
|
Euler angles in radians It is not used for grain-wise data, i.e.
|
||||||
when feature_IDs is not None. Defaults to 'EulerAngles'.
|
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),
|
Path to the group (folder) that contains geometry (_SIMPL_GEOMETRY),
|
||||||
and grain- or cell-wise data. Defaults to None, in which case
|
and grain- or cell-wise data. Defaults to None, in which case
|
||||||
it is set as the path that contains _SIMPL_GEOMETRY/SPACING.
|
it is set as the path that contains _SIMPL_GEOMETRY/SPACING.
|
||||||
|
@ -339,7 +352,9 @@ class Grid:
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_table(table,coordinates,labels):
|
def from_table(table: Table,
|
||||||
|
coordinates: str,
|
||||||
|
labels: Union[str, Sequence[str]]) -> "Grid":
|
||||||
"""
|
"""
|
||||||
Create grid from ASCII table.
|
Create grid from ASCII table.
|
||||||
|
|
||||||
|
@ -350,7 +365,7 @@ class Grid:
|
||||||
coordinates : str
|
coordinates : str
|
||||||
Label of the vector column containing the spatial coordinates.
|
Label of the vector column containing the spatial coordinates.
|
||||||
Need to be ordered (1./x fast, 3./z slow).
|
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.
|
Label(s) of the columns containing the material definition.
|
||||||
Each unique combination of values results in one material ID.
|
Each unique combination of values results in one material ID.
|
||||||
|
|
||||||
|
@ -372,28 +387,33 @@ class Grid:
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _find_closest_seed(seeds, weights, point):
|
def _find_closest_seed(seeds: np.ndarray, weights: np.ndarray, point: np.ndarray) -> np.integer:
|
||||||
return np.argmin(np.sum((np.broadcast_to(point,(len(seeds),3))-seeds)**2,axis=1) - weights)
|
return np.argmin(np.sum((np.broadcast_to(point,(len(seeds),3))-seeds)**2,axis=1) - weights)
|
||||||
|
|
||||||
@staticmethod
|
@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.
|
Create grid from Laguerre tessellation.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
cells : int numpy.ndarray of shape (3)
|
cells : sequence of int, len (3)
|
||||||
Number of cells in x,y,z direction.
|
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.
|
Physical size of the grid in meter.
|
||||||
seeds : numpy.ndarray of shape (:,3)
|
seeds : numpy.ndarray, shape (:,3)
|
||||||
Position of the seed points in meter. All points need to lay within the box.
|
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.
|
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.
|
Material ID of the seeds.
|
||||||
Defaults to None, in which case materials are consecutively numbered.
|
Defaults to None, in which case materials are consecutively numbered.
|
||||||
periodic : Boolean, optional
|
periodic : bool, optional
|
||||||
Assume grid to be periodic. Defaults to True.
|
Assume grid to be periodic. Defaults to True.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
|
@ -421,29 +441,33 @@ class Grid:
|
||||||
|
|
||||||
if periodic: material_ %= len(weights)
|
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,
|
size = size,
|
||||||
comments = util.execution_stamp('Grid','from_Laguerre_tessellation'),
|
comments = util.execution_stamp('Grid','from_Laguerre_tessellation'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_Voronoi_tessellation(cells,size,seeds,material=None,periodic=True):
|
def from_Voronoi_tessellation(cells: IntSequence,
|
||||||
|
size: FloatSequence,
|
||||||
|
seeds: np.ndarray,
|
||||||
|
material: IntSequence = None,
|
||||||
|
periodic: bool = True) -> "Grid":
|
||||||
"""
|
"""
|
||||||
Create grid from Voronoi tessellation.
|
Create grid from Voronoi tessellation.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
cells : int numpy.ndarray of shape (3)
|
cells : sequence of int, len (3)
|
||||||
Number of cells in x,y,z direction.
|
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.
|
Physical size of the grid in meter.
|
||||||
seeds : numpy.ndarray of shape (:,3)
|
seeds : numpy.ndarray, shape (:,3)
|
||||||
Position of the seed points in meter. All points need to lay within the box.
|
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.
|
Material ID of the seeds.
|
||||||
Defaults to None, in which case materials are consecutively numbered.
|
Defaults to None, in which case materials are consecutively numbered.
|
||||||
periodic : Boolean, optional
|
periodic : bool, optional
|
||||||
Assume grid to be periodic. Defaults to True.
|
Assume grid to be periodic. Defaults to True.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
|
@ -460,7 +484,7 @@ class Grid:
|
||||||
except TypeError:
|
except TypeError:
|
||||||
material_ = tree.query(coords, n_jobs = int(os.environ.get('OMP_NUM_THREADS',4)))[1] # scipy <1.6
|
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,
|
size = size,
|
||||||
comments = util.execution_stamp('Grid','from_Voronoi_tessellation'),
|
comments = util.execution_stamp('Grid','from_Voronoi_tessellation'),
|
||||||
)
|
)
|
||||||
|
@ -509,15 +533,20 @@ class Grid:
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_minimal_surface(cells,size,surface,threshold=0.0,periods=1,materials=(0,1)):
|
def from_minimal_surface(cells: IntSequence,
|
||||||
|
size: FloatSequence,
|
||||||
|
surface: str,
|
||||||
|
threshold: float = 0.0,
|
||||||
|
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
|
Parameters
|
||||||
----------
|
----------
|
||||||
cells : int numpy.ndarray of shape (3)
|
cells : sequence of int, len (3)
|
||||||
Number of cells in x,y,z direction.
|
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.
|
Physical size of the grid in meter.
|
||||||
surface : str
|
surface : str
|
||||||
Type of the minimal surface. See notes for details.
|
Type of the minimal surface. See notes for details.
|
||||||
|
@ -525,7 +554,7 @@ class Grid:
|
||||||
Threshold of the minimal surface. Defaults to 0.0.
|
Threshold of the minimal surface. Defaults to 0.0.
|
||||||
periods : integer, optional.
|
periods : integer, optional.
|
||||||
Number of periods per unit cell. Defaults to 1.
|
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).
|
Material IDs. Defaults to (0,1).
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
|
@ -566,22 +595,21 @@ class Grid:
|
||||||
|
|
||||||
>>> import numpy as np
|
>>> import numpy as np
|
||||||
>>> import damask
|
>>> import damask
|
||||||
>>> damask.Grid.from_minimal_surface(np.array([64]*3,int),np.ones(3),
|
>>> damask.Grid.from_minimal_surface([64]*3,np.ones(3)*1.e-4,'Gyroid')
|
||||||
... 'Gyroid')
|
cells : 64 x 64 x 64
|
||||||
cells a b c: 64 x 64 x 64
|
size : 0.0001 x 0.0001 x 0.0001 / m³
|
||||||
size x y z: 1.0 x 1.0 x 1.0
|
origin: 0.0 0.0 0.0 / m
|
||||||
origin x y z: 0.0 0.0 0.0
|
|
||||||
# materials: 2
|
# materials: 2
|
||||||
|
|
||||||
Minimal surface of 'Neovius' type. non-default material IDs.
|
Minimal surface of 'Neovius' type. non-default material IDs.
|
||||||
|
|
||||||
>>> import numpy as np
|
>>> import numpy as np
|
||||||
>>> import damask
|
>>> 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))
|
... 'Neovius',materials=(1,5))
|
||||||
cells a b c: 80 x 80 x 80
|
cells : 80 x 80 x 80
|
||||||
size x y z: 1.0 x 1.0 x 1.0
|
size : 0.0005 x 0.0005 x 0.0005 / m³
|
||||||
origin x y z: 0.0 0.0 0.0
|
origin: 0.0 0.0 0.0 / m
|
||||||
# materials: 2 (min: 1, max: 5)
|
# materials: 2 (min: 1, max: 5)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -595,7 +623,7 @@ class Grid:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def save(self,fname,compress=True):
|
def save(self, fname: Union[str, Path], compress: bool = True):
|
||||||
"""
|
"""
|
||||||
Save as VTK image data file.
|
Save as VTK image data file.
|
||||||
|
|
||||||
|
@ -611,10 +639,10 @@ class Grid:
|
||||||
v.add(self.material.flatten(order='F'),'material')
|
v.add(self.material.flatten(order='F'),'material')
|
||||||
v.add_comments(self.comments)
|
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):
|
def save_ASCII(self, fname: Union[str, TextIO]):
|
||||||
"""
|
"""
|
||||||
Save as geom file.
|
Save as geom file.
|
||||||
|
|
||||||
|
@ -644,26 +672,33 @@ class Grid:
|
||||||
header='\n'.join(header), fmt=format_string, comments='')
|
header='\n'.join(header), fmt=format_string, comments='')
|
||||||
|
|
||||||
|
|
||||||
def show(self):
|
def show(self) -> None:
|
||||||
"""Show on screen."""
|
"""Show on screen."""
|
||||||
VTK.from_rectilinear_grid(self.cells,self.size,self.origin).show()
|
VTK.from_rectilinear_grid(self.cells,self.size,self.origin).show()
|
||||||
|
|
||||||
|
|
||||||
def add_primitive(self,dimension,center,exponent,
|
def add_primitive(self,
|
||||||
fill=None,R=Rotation(),inverse=False,periodic=True):
|
dimension: Union[FloatSequence, IntSequence],
|
||||||
|
center: Union[FloatSequence, IntSequence],
|
||||||
|
exponent: Union[FloatSequence, float],
|
||||||
|
fill: int = None,
|
||||||
|
R: Rotation = Rotation(),
|
||||||
|
inverse: bool = False,
|
||||||
|
periodic: bool = True) -> "Grid":
|
||||||
"""
|
"""
|
||||||
Insert a primitive geometric object at a given position.
|
Insert a primitive geometric object at a given position.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
dimension : int or float numpy.ndarray of shape (3)
|
dimension : sequence of int or float, len (3)
|
||||||
Dimension (diameter/side length) of the primitive. If given as
|
Dimension (diameter/side length) of the primitive.
|
||||||
integers, cell centers are addressed.
|
If given as integers, cell centers are addressed.
|
||||||
If given as floats, coordinates are addressed.
|
If given as floats, physical coordinates are addressed.
|
||||||
center : int or float numpy.ndarray of shape (3)
|
center : sequence of int or float, len (3)
|
||||||
Center of the primitive. If given as integers, cell centers are addressed.
|
Center of the primitive.
|
||||||
If given as floats, coordinates in space are addressed.
|
If given as integers, cell centers are addressed.
|
||||||
exponent : numpy.ndarray of shape (3) or float
|
If given as floats, physical coordinates are addressed.
|
||||||
|
exponent : float or sequence of float, len (3)
|
||||||
Exponents for the three axes.
|
Exponents for the three axes.
|
||||||
0 gives octahedron (ǀxǀ^(2^0) + ǀyǀ^(2^0) + ǀzǀ^(2^0) < 1)
|
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)
|
1 gives sphere (ǀxǀ^(2^1) + ǀyǀ^(2^1) + ǀzǀ^(2^1) < 1)
|
||||||
|
@ -671,10 +706,10 @@ class Grid:
|
||||||
Fill value for primitive. Defaults to material.max()+1.
|
Fill value for primitive. Defaults to material.max()+1.
|
||||||
R : damask.Rotation, optional
|
R : damask.Rotation, optional
|
||||||
Rotation of primitive. Defaults to no rotation.
|
Rotation of primitive. Defaults to no rotation.
|
||||||
inverse : Boolean, optional
|
inverse : bool, optional
|
||||||
Retain original materials within primitive and fill outside.
|
Retain original materials within primitive and fill outside.
|
||||||
Defaults to False.
|
Defaults to False.
|
||||||
periodic : Boolean, optional
|
periodic : bool, optional
|
||||||
Assume grid to be periodic. Defaults to True.
|
Assume grid to be periodic. Defaults to True.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
|
@ -690,9 +725,9 @@ class Grid:
|
||||||
>>> import damask
|
>>> import damask
|
||||||
>>> g = damask.Grid(np.zeros([64]*3,int), np.ones(3)*1e-4)
|
>>> 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)
|
>>> g.add_primitive(np.ones(3)*5e-5,np.ones(3)*5e-5,1)
|
||||||
cells a b c: 64 x 64 x 64
|
cells : 64 x 64 x 64
|
||||||
size x y z: 0.0001 x 0.0001 x 0.0001
|
size : 0.0001 x 0.0001 x 0.0001 / m³
|
||||||
origin x y z: 0.0 0.0 0.0
|
origin: 0.0 0.0 0.0 / m
|
||||||
# materials: 2
|
# materials: 2
|
||||||
|
|
||||||
Add a cube at the origin.
|
Add a cube at the origin.
|
||||||
|
@ -701,9 +736,9 @@ class Grid:
|
||||||
>>> import damask
|
>>> import damask
|
||||||
>>> g = damask.Grid(np.zeros([64]*3,int), np.ones(3)*1e-4)
|
>>> 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)
|
>>> g.add_primitive(np.ones(3,int)*32,np.zeros(3),np.inf)
|
||||||
cells a b c: 64 x 64 x 64
|
cells : 64 x 64 x 64
|
||||||
size x y z: 0.0001 x 0.0001 x 0.0001
|
size : 0.0001 x 0.0001 x 0.0001 / m³
|
||||||
origin x y z: 0.0 0.0 0.0
|
origin: 0.0 0.0 0.0 / m
|
||||||
# materials: 2
|
# materials: 2
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -734,13 +769,13 @@ class Grid:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def mirror(self,directions,reflect=False):
|
def mirror(self, directions: Sequence[str], reflect: bool = False) -> "Grid":
|
||||||
"""
|
"""
|
||||||
Mirror grid along given directions.
|
Mirror grid along given directions.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
directions : iterable containing str
|
directions : (sequence of) str
|
||||||
Direction(s) along which the grid is mirrored.
|
Direction(s) along which the grid is mirrored.
|
||||||
Valid entries are 'x', 'y', 'z'.
|
Valid entries are 'x', 'y', 'z'.
|
||||||
reflect : bool, optional
|
reflect : bool, optional
|
||||||
|
@ -759,9 +794,9 @@ class Grid:
|
||||||
>>> import damask
|
>>> 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-4)
|
||||||
>>> g.mirror('xy',True)
|
>>> g.mirror('xy',True)
|
||||||
cells a b c: 64 x 64 x 32
|
cells : 64 x 64 x 32
|
||||||
size x y z: 0.0002 x 0.0002 x 0.0001
|
size : 0.0002 x 0.0002 x 0.0001 / m³
|
||||||
origin x y z: 0.0 0.0 0.0
|
origin: 0.0 0.0 0.0 / m
|
||||||
# materials: 1
|
# materials: 1
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -769,7 +804,7 @@ class Grid:
|
||||||
if not set(directions).issubset(valid):
|
if not set(directions).issubset(valid):
|
||||||
raise ValueError(f'invalid direction {set(directions).difference(valid)} specified')
|
raise ValueError(f'invalid direction {set(directions).difference(valid)} specified')
|
||||||
|
|
||||||
limits = [None,None] if reflect else [-2,0]
|
limits: Sequence[Optional[int]] = [None,None] if reflect else [-2,0]
|
||||||
mat = self.material.copy()
|
mat = self.material.copy()
|
||||||
|
|
||||||
if 'x' in directions:
|
if 'x' in directions:
|
||||||
|
@ -786,13 +821,13 @@ class Grid:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def flip(self,directions):
|
def flip(self, directions: Sequence[str]) -> "Grid":
|
||||||
"""
|
"""
|
||||||
Flip grid along given directions.
|
Flip grid along given directions.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
directions : iterable containing str
|
directions : (sequence of) str
|
||||||
Direction(s) along which the grid is flipped.
|
Direction(s) along which the grid is flipped.
|
||||||
Valid entries are 'x', 'y', 'z'.
|
Valid entries are 'x', 'y', 'z'.
|
||||||
|
|
||||||
|
@ -815,15 +850,15 @@ class Grid:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def scale(self,cells,periodic=True):
|
def scale(self, cells: IntSequence, periodic: bool = True) -> "Grid":
|
||||||
"""
|
"""
|
||||||
Scale grid to new cells.
|
Scale grid to new cells.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
cells : numpy.ndarray of shape (3)
|
cells : sequence of int, len (3)
|
||||||
Number of cells in x,y,z direction.
|
Number of cells in x,y,z direction.
|
||||||
periodic : Boolean, optional
|
periodic : bool, optional
|
||||||
Assume grid to be periodic. Defaults to True.
|
Assume grid to be periodic. Defaults to True.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
|
@ -839,9 +874,9 @@ class Grid:
|
||||||
>>> import damask
|
>>> 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-4)
|
||||||
>>> g.scale(g.cells*2)
|
>>> g.scale(g.cells*2)
|
||||||
cells a b c: 64 x 64 x 64
|
cells : 64 x 64 x 64
|
||||||
size x y z: 0.0001 x 0.0001 x 0.0001
|
size : 0.0001 x 0.0001 x 0.0001 / m³
|
||||||
origin x y z: 0.0 0.0 0.0
|
origin: 0.0 0.0 0.0 / m
|
||||||
# materials: 1
|
# materials: 1
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -859,7 +894,10 @@ class Grid:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def clean(self,stencil=3,selection=None,periodic=True):
|
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.
|
Smooth grid by selecting most frequent material index within given stencil at each location.
|
||||||
|
|
||||||
|
@ -867,9 +905,9 @@ class Grid:
|
||||||
----------
|
----------
|
||||||
stencil : int, optional
|
stencil : int, optional
|
||||||
Size of smoothing stencil.
|
Size of smoothing stencil.
|
||||||
selection : list, optional
|
selection : sequence of int, optional
|
||||||
Field values that can be altered. Defaults to all.
|
Field values that can be altered. Defaults to all.
|
||||||
periodic : Boolean, optional
|
periodic : bool, optional
|
||||||
Assume grid to be periodic. Defaults to True.
|
Assume grid to be periodic. Defaults to True.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
|
@ -878,7 +916,7 @@ class Grid:
|
||||||
Updated grid-based geometry.
|
Updated grid-based geometry.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def mostFrequent(arr,selection=None):
|
def mostFrequent(arr: np.ndarray, selection = None):
|
||||||
me = arr[arr.size//2]
|
me = arr[arr.size//2]
|
||||||
if selection is None or me in selection:
|
if selection is None or me in selection:
|
||||||
unique, inverse = np.unique(arr, return_inverse=True)
|
unique, inverse = np.unique(arr, return_inverse=True)
|
||||||
|
@ -899,7 +937,7 @@ class Grid:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def renumber(self):
|
def renumber(self) -> "Grid":
|
||||||
"""
|
"""
|
||||||
Renumber sorted material indices as 0,...,N-1.
|
Renumber sorted material indices as 0,...,N-1.
|
||||||
|
|
||||||
|
@ -918,7 +956,7 @@ class Grid:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def rotate(self,R,fill=None):
|
def rotate(self, R: Rotation, fill: int = None) -> "Grid":
|
||||||
"""
|
"""
|
||||||
Rotate grid (pad if required).
|
Rotate grid (pad if required).
|
||||||
|
|
||||||
|
@ -926,7 +964,7 @@ class Grid:
|
||||||
----------
|
----------
|
||||||
R : damask.Rotation
|
R : damask.Rotation
|
||||||
Rotation to apply to the grid.
|
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.
|
Material index to fill the corners. Defaults to material.max() + 1.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
|
@ -956,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.
|
Crop or enlarge/pad grid.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
cells : numpy.ndarray of shape (3)
|
cells : sequence of int, len (3), optional
|
||||||
Number of cells x,y,z direction.
|
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].
|
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.
|
Material index to fill the background. Defaults to material.max() + 1.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
|
@ -981,42 +1022,43 @@ class Grid:
|
||||||
>>> import numpy as np
|
>>> import numpy as np
|
||||||
>>> import damask
|
>>> 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-4)
|
||||||
>>> g.canvas(np.array([32,32,16],int))
|
>>> g.canvas([32,32,16])
|
||||||
cells a b c: 33 x 32 x 16
|
cells : 33 x 32 x 16
|
||||||
size x y z: 0.0001 x 0.0001 x 5e-05
|
size : 0.0001 x 0.0001 x 5e-05 / m³
|
||||||
origin x y z: 0.0 0.0 0.0
|
origin: 0.0 0.0 0.0 / m
|
||||||
# materials: 1
|
# 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
|
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
|
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))
|
LL = np.clip( offset_, 0,np.minimum(self.cells, cells_+offset_))
|
||||||
UR = np.clip( offset+cells, 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))
|
ll = np.clip(-offset_, 0,np.minimum( cells_,self.cells-offset_))
|
||||||
ur = np.clip(-offset+self.cells,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]]
|
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,
|
return Grid(material = canvas,
|
||||||
size = self.size/self.cells*np.asarray(canvas.shape),
|
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')],
|
comments = self.comments+[util.execution_stamp('Grid','canvas')],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def substitute(self,from_material,to_material):
|
def substitute(self, from_material: IntSequence, to_material: IntSequence) -> "Grid":
|
||||||
"""
|
"""
|
||||||
Substitute material indices.
|
Substitute material indices.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
from_material : iterable of ints
|
from_material : sequence of int
|
||||||
Material indices to be substituted.
|
Material indices to be substituted.
|
||||||
to_material : iterable of ints
|
to_material : sequence of int
|
||||||
New material indices.
|
New material indices.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
|
@ -1025,7 +1067,7 @@ class Grid:
|
||||||
Updated grid-based geometry.
|
Updated grid-based geometry.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def mp(entry,mapper):
|
def mp(entry, mapper):
|
||||||
return mapper[entry] if entry in mapper else entry
|
return mapper[entry] if entry in mapper else entry
|
||||||
|
|
||||||
mp = np.vectorize(mp)
|
mp = np.vectorize(mp)
|
||||||
|
@ -1038,7 +1080,7 @@ class Grid:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def sort(self):
|
def sort(self) -> "Grid":
|
||||||
"""
|
"""
|
||||||
Sort material indices such that min(material) is located at (0,0,0).
|
Sort material indices such that min(material) is located at (0,0,0).
|
||||||
|
|
||||||
|
@ -1060,7 +1102,11 @@ class Grid:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def vicinity_offset(self,vicinity=1,offset=None,trigger=[],periodic=True):
|
def vicinity_offset(self,
|
||||||
|
vicinity: int = 1,
|
||||||
|
offset: int = None,
|
||||||
|
trigger: IntSequence = [],
|
||||||
|
periodic: bool = True) -> "Grid":
|
||||||
"""
|
"""
|
||||||
Offset material index of points in the vicinity of xxx.
|
Offset material index of points in the vicinity of xxx.
|
||||||
|
|
||||||
|
@ -1076,10 +1122,10 @@ class Grid:
|
||||||
offset : int, optional
|
offset : int, optional
|
||||||
Offset (positive or negative) to tag material indices,
|
Offset (positive or negative) to tag material indices,
|
||||||
defaults to material.max()+1.
|
defaults to material.max()+1.
|
||||||
trigger : list of ints, optional
|
trigger : sequence of int, optional
|
||||||
List of material indices that trigger a change.
|
List of material indices that trigger a change.
|
||||||
Defaults to [], meaning that any different neighbor triggers a change.
|
Defaults to [], meaning that any different neighbor triggers a change.
|
||||||
periodic : Boolean, optional
|
periodic : bool, optional
|
||||||
Assume grid to be periodic. Defaults to True.
|
Assume grid to be periodic. Defaults to True.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
|
@ -1088,8 +1134,7 @@ class Grid:
|
||||||
Updated grid-based geometry.
|
Updated grid-based geometry.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def tainted_neighborhood(stencil,trigger):
|
def tainted_neighborhood(stencil: np.ndarray, trigger):
|
||||||
|
|
||||||
me = stencil[stencil.shape[0]//2]
|
me = stencil[stencil.shape[0]//2]
|
||||||
return np.any(stencil != me if len(trigger) == 0 else
|
return np.any(stencil != me if len(trigger) == 0 else
|
||||||
np.in1d(stencil,np.array(list(set(trigger) - {me}))))
|
np.in1d(stencil,np.array(list(set(trigger) - {me}))))
|
||||||
|
@ -1108,15 +1153,15 @@ 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.
|
Create VTK unstructured grid containing grain boundaries.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
periodic : Boolean, optional
|
periodic : bool, optional
|
||||||
Assume grid to be periodic. Defaults to True.
|
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.
|
Direction(s) along which the boundaries are determined.
|
||||||
Valid entries are 'x', 'y', 'z'. Defaults to 'xyz'.
|
Valid entries are 'x', 'y', 'z'. Defaults to 'xyz'.
|
||||||
|
|
||||||
|
|
|
@ -393,8 +393,8 @@ class Orientation(Rotation,Crystal):
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
in : numpy.ndarray of quaternion.shape
|
in : numpy.ndarray of bool, quaternion.shape
|
||||||
Boolean array indicating whether Rodrigues-Frank vector falls into fundamental zone.
|
Whether Rodrigues-Frank vector falls into fundamental zone.
|
||||||
|
|
||||||
Notes
|
Notes
|
||||||
-----
|
-----
|
||||||
|
@ -437,8 +437,8 @@ class Orientation(Rotation,Crystal):
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
in : numpy.ndarray of quaternion.shape
|
in : numpy.ndarray of bool, quaternion.shape
|
||||||
Boolean array indicating whether Rodrigues-Frank vector falls into disorientation FZ.
|
Whether Rodrigues-Frank vector falls into disorientation FZ.
|
||||||
|
|
||||||
References
|
References
|
||||||
----------
|
----------
|
||||||
|
@ -651,8 +651,8 @@ class Orientation(Rotation,Crystal):
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
in : numpy.ndarray of shape (...)
|
in : numpy.ndarray, shape (...)
|
||||||
Boolean array indicating whether vector falls into SST.
|
Whether vector falls into SST.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not isinstance(vector,np.ndarray) or vector.shape[-1] != 3:
|
if not isinstance(vector,np.ndarray) or vector.shape[-1] != 3:
|
||||||
|
|
|
@ -1817,7 +1817,7 @@ class Result:
|
||||||
output : (list of) str, optional
|
output : (list of) str, optional
|
||||||
Names of the datasets to export to the file.
|
Names of the datasets to export to the file.
|
||||||
Defaults to '*', in which case all datasets are exported.
|
Defaults to '*', in which case all datasets are exported.
|
||||||
overwrite : boolean, optional
|
overwrite : bool, optional
|
||||||
Overwrite existing configuration files.
|
Overwrite existing configuration files.
|
||||||
Defaults to False.
|
Defaults to False.
|
||||||
|
|
||||||
|
|
|
@ -671,7 +671,7 @@ class Rotation:
|
||||||
----------
|
----------
|
||||||
q : numpy.ndarray of shape (...,4)
|
q : numpy.ndarray of shape (...,4)
|
||||||
Unit quaternion (q_0, q_1, q_2, q_3) in positive real hemisphere, i.e. ǀqǀ = 1, q_0 ≥ 0.
|
Unit quaternion (q_0, q_1, q_2, q_3) in positive real hemisphere, i.e. ǀqǀ = 1, q_0 ≥ 0.
|
||||||
accept_homomorph : boolean, optional
|
accept_homomorph : bool, optional
|
||||||
Allow homomorphic variants, i.e. q_0 < 0 (negative real hemisphere).
|
Allow homomorphic variants, i.e. q_0 < 0 (negative real hemisphere).
|
||||||
Defaults to False.
|
Defaults to False.
|
||||||
P : int ∈ {-1,1}, optional
|
P : int ∈ {-1,1}, optional
|
||||||
|
@ -706,7 +706,7 @@ class Rotation:
|
||||||
phi : numpy.ndarray of shape (...,3)
|
phi : numpy.ndarray of shape (...,3)
|
||||||
Euler angles (φ_1 ∈ [0,2π], ϕ ∈ [0,π], φ_2 ∈ [0,2π])
|
Euler angles (φ_1 ∈ [0,2π], ϕ ∈ [0,π], φ_2 ∈ [0,2π])
|
||||||
or (φ_1 ∈ [0,360], ϕ ∈ [0,180], φ_2 ∈ [0,360]) if degrees == True.
|
or (φ_1 ∈ [0,360], ϕ ∈ [0,180], φ_2 ∈ [0,360]) if degrees == True.
|
||||||
degrees : boolean, optional
|
degrees : bool, optional
|
||||||
Euler angles are given in degrees. Defaults to False.
|
Euler angles are given in degrees. Defaults to False.
|
||||||
|
|
||||||
Notes
|
Notes
|
||||||
|
@ -737,9 +737,9 @@ class Rotation:
|
||||||
axis_angle : numpy.ndarray of shape (...,4)
|
axis_angle : numpy.ndarray of shape (...,4)
|
||||||
Axis and angle (n_1, n_2, n_3, ω) with ǀnǀ = 1 and ω ∈ [0,π]
|
Axis and angle (n_1, n_2, n_3, ω) with ǀnǀ = 1 and ω ∈ [0,π]
|
||||||
or ω ∈ [0,180] if degrees == True.
|
or ω ∈ [0,180] if degrees == True.
|
||||||
degrees : boolean, optional
|
degrees : bool, optional
|
||||||
Angle ω is given in degrees. Defaults to False.
|
Angle ω is given in degrees. Defaults to False.
|
||||||
normalize: boolean, optional
|
normalize: bool, optional
|
||||||
Allow ǀnǀ ≠ 1. Defaults to False.
|
Allow ǀnǀ ≠ 1. Defaults to False.
|
||||||
P : int ∈ {-1,1}, optional
|
P : int ∈ {-1,1}, optional
|
||||||
Sign convention. Defaults to -1.
|
Sign convention. Defaults to -1.
|
||||||
|
@ -773,9 +773,9 @@ class Rotation:
|
||||||
----------
|
----------
|
||||||
basis : numpy.ndarray of shape (...,3,3)
|
basis : numpy.ndarray of shape (...,3,3)
|
||||||
Three three-dimensional lattice basis vectors.
|
Three three-dimensional lattice basis vectors.
|
||||||
orthonormal : boolean, optional
|
orthonormal : bool, optional
|
||||||
Basis is strictly orthonormal, i.e. is free of stretch components. Defaults to True.
|
Basis is strictly orthonormal, i.e. is free of stretch components. Defaults to True.
|
||||||
reciprocal : boolean, optional
|
reciprocal : bool, optional
|
||||||
Basis vectors are given in reciprocal (instead of real) space. Defaults to False.
|
Basis vectors are given in reciprocal (instead of real) space. Defaults to False.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -851,7 +851,7 @@ class Rotation:
|
||||||
----------
|
----------
|
||||||
rho : numpy.ndarray of shape (...,4)
|
rho : numpy.ndarray of shape (...,4)
|
||||||
Rodrigues–Frank vector (n_1, n_2, n_3, tan(ω/2)) with ǀnǀ = 1 and ω ∈ [0,π].
|
Rodrigues–Frank vector (n_1, n_2, n_3, tan(ω/2)) with ǀnǀ = 1 and ω ∈ [0,π].
|
||||||
normalize : boolean, optional
|
normalize : bool, optional
|
||||||
Allow ǀnǀ ≠ 1. Defaults to False.
|
Allow ǀnǀ ≠ 1. Defaults to False.
|
||||||
P : int ∈ {-1,1}, optional
|
P : int ∈ {-1,1}, optional
|
||||||
Sign convention. Defaults to -1.
|
Sign convention. Defaults to -1.
|
||||||
|
@ -977,9 +977,9 @@ class Rotation:
|
||||||
N : integer, optional
|
N : integer, optional
|
||||||
Number of discrete orientations to be sampled from the given ODF.
|
Number of discrete orientations to be sampled from the given ODF.
|
||||||
Defaults to 500.
|
Defaults to 500.
|
||||||
degrees : boolean, optional
|
degrees : bool, optional
|
||||||
Euler space grid coordinates are in degrees. Defaults to True.
|
Euler space grid coordinates are in degrees. Defaults to True.
|
||||||
fractions : boolean, optional
|
fractions : bool, optional
|
||||||
ODF values correspond to volume fractions, not probability densities.
|
ODF values correspond to volume fractions, not probability densities.
|
||||||
Defaults to True.
|
Defaults to True.
|
||||||
rng_seed: {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional
|
rng_seed: {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional
|
||||||
|
@ -1033,7 +1033,7 @@ class Rotation:
|
||||||
Standard deviation of (Gaussian) misorientation distribution.
|
Standard deviation of (Gaussian) misorientation distribution.
|
||||||
N : int, optional
|
N : int, optional
|
||||||
Number of samples. Defaults to 500.
|
Number of samples. Defaults to 500.
|
||||||
degrees : boolean, optional
|
degrees : bool, optional
|
||||||
sigma is given in degrees. Defaults to True.
|
sigma is given in degrees. Defaults to True.
|
||||||
rng_seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional
|
rng_seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional
|
||||||
A seed to initialize the BitGenerator.
|
A seed to initialize the BitGenerator.
|
||||||
|
@ -1072,7 +1072,7 @@ class Rotation:
|
||||||
Defaults to 0.
|
Defaults to 0.
|
||||||
N : int, optional
|
N : int, optional
|
||||||
Number of samples. Defaults to 500.
|
Number of samples. Defaults to 500.
|
||||||
degrees : boolean, optional
|
degrees : bool, optional
|
||||||
sigma, alpha, and beta are given in degrees.
|
sigma, alpha, and beta are given in degrees.
|
||||||
rng_seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional
|
rng_seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional
|
||||||
A seed to initialize the BitGenerator.
|
A seed to initialize the BitGenerator.
|
||||||
|
|
|
@ -28,8 +28,8 @@ class VTK:
|
||||||
----------
|
----------
|
||||||
vtk_data : subclass of vtk.vtkDataSet
|
vtk_data : subclass of vtk.vtkDataSet
|
||||||
Description of geometry and topology, optionally with attached data.
|
Description of geometry and topology, optionally with attached data.
|
||||||
Valid types are vtk.vtkRectilinearGrid, vtk.vtkUnstructuredGrid,
|
Valid types are vtk.vtkImageData, vtk.vtkUnstructuredGrid,
|
||||||
or vtk.vtkPolyData.
|
vtk.vtkPolyData, and vtk.vtkRectilinearGrid.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.vtk_data = vtk_data
|
self.vtk_data = vtk_data
|
||||||
|
@ -242,7 +242,7 @@ class VTK:
|
||||||
----------
|
----------
|
||||||
fname : str or pathlib.Path
|
fname : str or pathlib.Path
|
||||||
Filename for writing.
|
Filename for writing.
|
||||||
parallel : boolean, optional
|
parallel : bool, optional
|
||||||
Write data in parallel background process. Defaults to True.
|
Write data in parallel background process. Defaults to True.
|
||||||
compress : bool, optional
|
compress : bool, optional
|
||||||
Compress with zlib algorithm. Defaults to True.
|
Compress with zlib algorithm. Defaults to True.
|
||||||
|
@ -419,7 +419,7 @@ class VTK:
|
||||||
return writer.GetOutputString()
|
return writer.GetOutputString()
|
||||||
|
|
||||||
|
|
||||||
def show(self):
|
def show(self) -> None:
|
||||||
"""
|
"""
|
||||||
Render.
|
Render.
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ def from_Poisson_disc(size: _FloatSequence, N_seeds: int, N_candidates: int, dis
|
||||||
Number of candidates to consider for finding best candidate.
|
Number of candidates to consider for finding best candidate.
|
||||||
distance : float
|
distance : float
|
||||||
Minimum acceptable distance to other seeds.
|
Minimum acceptable distance to other seeds.
|
||||||
periodic : boolean, optional
|
periodic : bool, optional
|
||||||
Calculate minimum distance for periodically repeated grid.
|
Calculate minimum distance for periodically repeated grid.
|
||||||
rng_seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional
|
rng_seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional
|
||||||
A seed to initialize the BitGenerator. Defaults to None.
|
A seed to initialize the BitGenerator. Defaults to None.
|
||||||
|
@ -99,8 +99,8 @@ def from_Poisson_disc(size: _FloatSequence, N_seeds: int, N_candidates: int, dis
|
||||||
return coords
|
return coords
|
||||||
|
|
||||||
|
|
||||||
def from_grid(grid, selection: _IntSequence = None,
|
def from_grid(grid, selection: _IntSequence = None, invert_selection: bool = False,
|
||||||
invert: bool = False, average: bool = False, periodic: bool = True) -> _Tuple[_np.ndarray, _np.ndarray]:
|
average: bool = False, periodic: bool = True) -> _Tuple[_np.ndarray, _np.ndarray]:
|
||||||
"""
|
"""
|
||||||
Create seeds from grid description.
|
Create seeds from grid description.
|
||||||
|
|
||||||
|
@ -110,11 +110,11 @@ def from_grid(grid, selection: _IntSequence = None,
|
||||||
Grid from which the material IDs are used as seeds.
|
Grid from which the material IDs are used as seeds.
|
||||||
selection : sequence of int, optional
|
selection : sequence of int, optional
|
||||||
Material IDs to consider.
|
Material IDs to consider.
|
||||||
invert : boolean, false
|
invert_selection : bool, optional
|
||||||
Consider all material IDs except those in selection. Defaults to False.
|
Consider all material IDs except those in selection. Defaults to False.
|
||||||
average : boolean, optional
|
average : bool, optional
|
||||||
Seed corresponds to center of gravity of material ID cloud.
|
Seed corresponds to center of gravity of material ID cloud.
|
||||||
periodic : boolean, optional
|
periodic : bool, optional
|
||||||
Center of gravity accounts for periodic boundaries.
|
Center of gravity accounts for periodic boundaries.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
|
@ -125,7 +125,7 @@ def from_grid(grid, selection: _IntSequence = None,
|
||||||
"""
|
"""
|
||||||
material = grid.material.reshape((-1,1),order='F')
|
material = grid.material.reshape((-1,1),order='F')
|
||||||
mask = _np.full(grid.cells.prod(),True,dtype=bool) if selection is None else \
|
mask = _np.full(grid.cells.prod(),True,dtype=bool) if selection is None else \
|
||||||
_np.isin(material,selection,invert=invert).flatten()
|
_np.isin(material,selection,invert=invert_selection).flatten()
|
||||||
coords = _grid_filters.coordinates0_point(grid.cells,grid.size).reshape(-1,3,order='F')
|
coords = _grid_filters.coordinates0_point(grid.cells,grid.size).reshape(-1,3,order='F')
|
||||||
|
|
||||||
if not average:
|
if not average:
|
||||||
|
|
|
@ -237,12 +237,27 @@ class TestGrid:
|
||||||
modified)
|
modified)
|
||||||
|
|
||||||
|
|
||||||
def test_canvas(self,default):
|
def test_canvas_extend(self,default):
|
||||||
cells = default.cells
|
cells = default.cells
|
||||||
grid_add = np.random.randint(0,30,(3))
|
cells_add = np.random.randint(0,30,(3))
|
||||||
modified = default.canvas(cells + grid_add)
|
modified = default.canvas(cells + cells_add)
|
||||||
assert np.all(modified.material[:cells[0],:cells[1],:cells[2]] == default.material)
|
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),
|
@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)))])
|
(np.random.randint(4,8,(3)),np.random.randint(9,12,(3)))])
|
||||||
|
|
|
@ -67,5 +67,5 @@ class TestSeeds:
|
||||||
coords = seeds.from_random(size,N_seeds,cells)
|
coords = seeds.from_random(size,N_seeds,cells)
|
||||||
grid = Grid.from_Voronoi_tessellation(cells,size,coords)
|
grid = Grid.from_Voronoi_tessellation(cells,size,coords)
|
||||||
selection=np.random.randint(N_seeds)+1
|
selection=np.random.randint(N_seeds)+1
|
||||||
coords,material = seeds.from_grid(grid,average=average,periodic=periodic,invert=invert,selection=[selection])
|
coords,material = seeds.from_grid(grid,average=average,periodic=periodic,invert_selection=invert,selection=[selection])
|
||||||
assert selection not in material if invert else (selection==material).all()
|
assert selection not in material if invert else (selection==material).all()
|
||||||
|
|
Loading…
Reference in New Issue