unifying interface
same same for same functionality, allow user to specify single integer for convenience
This commit is contained in:
parent
53fe11484d
commit
f9e04bc4cb
|
@ -4,7 +4,7 @@ import warnings
|
||||||
import multiprocessing as mp
|
import multiprocessing as mp
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import typing
|
import typing
|
||||||
from typing import Union, Optional, TextIO, List, Sequence
|
from typing import Union, Optional, TextIO, List, Sequence, Collection
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
@ -17,7 +17,7 @@ from . import util
|
||||||
from . import grid_filters
|
from . import grid_filters
|
||||||
from . import Rotation
|
from . import Rotation
|
||||||
from . import Table
|
from . import Table
|
||||||
from ._typehints import FloatSequence, IntSequence
|
from ._typehints import FloatSequence, IntSequence, IntCollection
|
||||||
|
|
||||||
class Grid:
|
class Grid:
|
||||||
"""
|
"""
|
||||||
|
@ -907,7 +907,8 @@ class Grid:
|
||||||
|
|
||||||
def clean(self,
|
def clean(self,
|
||||||
stencil: int = 3,
|
stencil: int = 3,
|
||||||
mutable: IntSequence = None,
|
selection: IntCollection = None,
|
||||||
|
invert_selection: bool = False,
|
||||||
periodic: bool = True) -> 'Grid':
|
periodic: bool = True) -> 'Grid':
|
||||||
"""
|
"""
|
||||||
Smooth grid by selecting most frequent material ID within given stencil at each location.
|
Smooth grid by selecting most frequent material ID within given stencil at each location.
|
||||||
|
@ -916,8 +917,10 @@ class Grid:
|
||||||
----------
|
----------
|
||||||
stencil : int, optional
|
stencil : int, optional
|
||||||
Size of smoothing stencil. Defaults to 3.
|
Size of smoothing stencil. Defaults to 3.
|
||||||
mutable : sequence of int, optional
|
selection : int or collection of int, optional
|
||||||
Material ID that can be altered. Defaults to all.
|
Material IDs to consider.
|
||||||
|
invert_selection : bool, optional
|
||||||
|
Consider all material IDs except those in selection. Defaults to False.
|
||||||
periodic : bool, optional
|
periodic : bool, optional
|
||||||
Assume grid to be periodic. Defaults to True.
|
Assume grid to be periodic. Defaults to True.
|
||||||
|
|
||||||
|
@ -927,21 +930,23 @@ class Grid:
|
||||||
Updated grid-based geometry.
|
Updated grid-based geometry.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def mostFrequent(arr: np.ndarray, mutable = None):
|
def mostFrequent(arr: np.ndarray, selection: List, invert: bool):
|
||||||
me = arr[arr.size//2]
|
me = arr[arr.size//2]
|
||||||
if selection is None or me in mutable:
|
if len(selection) == 0 or np.isin(me,selection,invert=invert):
|
||||||
unique, inverse = np.unique(arr, return_inverse=True)
|
unique, inverse = np.unique(arr,return_inverse=True)
|
||||||
return unique[np.argmax(np.bincount(inverse))]
|
return unique[np.argmax(np.bincount(inverse))]
|
||||||
else:
|
else:
|
||||||
return me
|
return me
|
||||||
|
|
||||||
return Grid(material = ndimage.filters.generic_filter(
|
extra_keywords = dict(selection=util.tbd(selection),invert=invert_selection)
|
||||||
|
material = ndimage.filters.generic_filter(
|
||||||
self.material,
|
self.material,
|
||||||
mostFrequent,
|
mostFrequent,
|
||||||
size=(stencil if mutable is None else stencil//2*2+1,)*3,
|
size=(stencil if selection is None else stencil//2*2+1,)*3,
|
||||||
mode=('wrap' if periodic else 'nearest'),
|
mode=('wrap' if periodic else 'nearest'),
|
||||||
extra_keywords=dict(mutable=mutable),
|
extra_keywords=extra_keywords,
|
||||||
).astype(self.material.dtype),
|
).astype(self.material.dtype)
|
||||||
|
return Grid(material = material,
|
||||||
size = self.size,
|
size = self.size,
|
||||||
origin = self.origin,
|
origin = self.origin,
|
||||||
comments = self.comments+[util.execution_stamp('Grid','clean')],
|
comments = self.comments+[util.execution_stamp('Grid','clean')],
|
||||||
|
@ -1061,16 +1066,16 @@ class Grid:
|
||||||
|
|
||||||
|
|
||||||
def substitute(self,
|
def substitute(self,
|
||||||
from_material: IntSequence,
|
from_material: Union[int,IntSequence],
|
||||||
to_material: IntSequence) -> 'Grid':
|
to_material: Union[int,IntSequence]) -> 'Grid':
|
||||||
"""
|
"""
|
||||||
Substitute material indices.
|
Substitute material indices.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
from_material : sequence of int
|
from_material : int or sequence of int
|
||||||
Material indices to be substituted.
|
Material indices to be substituted.
|
||||||
to_material : sequence of int
|
to_material : int or sequence of int
|
||||||
New material indices.
|
New material indices.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
|
@ -1080,7 +1085,8 @@ class Grid:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
material = self.material.copy()
|
material = self.material.copy()
|
||||||
for f,t in zip(from_material,to_material): # ToDo Python 3.10 has strict mode for zip
|
for f,t in zip(from_material if isinstance(from_material,(Sequence,np.ndarray)) else [from_material],
|
||||||
|
to_material if isinstance(to_material,(Sequence,np.ndarray)) else [to_material]): # ToDo Python 3.10 has strict mode for zip
|
||||||
material[self.material==f] = t
|
material[self.material==f] = t
|
||||||
|
|
||||||
return Grid(material = material,
|
return Grid(material = material,
|
||||||
|
@ -1115,14 +1121,14 @@ class Grid:
|
||||||
def vicinity_offset(self,
|
def vicinity_offset(self,
|
||||||
vicinity: int = 1,
|
vicinity: int = 1,
|
||||||
offset: int = None,
|
offset: int = None,
|
||||||
trigger: IntSequence = [],
|
selection: IntCollection = None,
|
||||||
|
invert_selection: bool = False,
|
||||||
periodic: bool = True) -> 'Grid':
|
periodic: bool = True) -> 'Grid':
|
||||||
"""
|
"""
|
||||||
Offset material ID of points in the vicinity of xxx.
|
Offset material ID of points in the vicinity of xxx.
|
||||||
|
|
||||||
Different from themselves (or listed as triggers) within a given (cubic) vicinity,
|
Different from themselves (or listed as triggers) within a given (cubic) vicinity,
|
||||||
i.e. within the region close to a grain/phase boundary.
|
i.e. within the region close to a grain/phase boundary.
|
||||||
ToDo: use include/exclude as in seeds.from_grid
|
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
|
@ -1132,9 +1138,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 : sequence of int, optional
|
selection : int or collection of int, optional
|
||||||
List of material indices that trigger a change.
|
Material IDs to that triger xxx.
|
||||||
Defaults to [], meaning that any different neighbor triggers a change.
|
invert_selection : bool, optional
|
||||||
|
Consider all material IDs except those in selection. Defaults to False.
|
||||||
periodic : bool, optional
|
periodic : bool, optional
|
||||||
Assume grid to be periodic. Defaults to True.
|
Assume grid to be periodic. Defaults to True.
|
||||||
|
|
||||||
|
@ -1144,17 +1151,19 @@ class Grid:
|
||||||
Updated grid-based geometry.
|
Updated grid-based geometry.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def tainted_neighborhood(stencil: np.ndarray, trigger):
|
def tainted_neighborhood(stencil: np.ndarray, selection):
|
||||||
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(selection) == 0 else
|
||||||
np.in1d(stencil,np.array(list(set(trigger) - {me}))))
|
np.in1d(stencil,np.array(list(set(selection) - {me}))))
|
||||||
|
|
||||||
offset_ = np.nanmax(self.material)+1 if offset is None else offset
|
offset_ = np.nanmax(self.material)+1 if offset is None else offset
|
||||||
|
selection_ = util.tbd(selection) if not invert_selection else \
|
||||||
|
list(set(self.material) - set(util.tbd(selection)))
|
||||||
mask = ndimage.filters.generic_filter(self.material,
|
mask = ndimage.filters.generic_filter(self.material,
|
||||||
tainted_neighborhood,
|
tainted_neighborhood,
|
||||||
size=1+2*vicinity,
|
size=1+2*vicinity,
|
||||||
mode='wrap' if periodic else 'nearest',
|
mode='wrap' if periodic else 'nearest',
|
||||||
extra_keywords={'trigger':trigger})
|
extra_keywords=dict(selection=selection_))
|
||||||
|
|
||||||
return Grid(material = np.where(mask, self.material + offset_,self.material),
|
return Grid(material = np.where(mask, self.material + offset_,self.material),
|
||||||
size = self.size,
|
size = self.size,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""Functionality for typehints."""
|
"""Functionality for typehints."""
|
||||||
|
|
||||||
from typing import Sequence, Union, Literal, TextIO
|
from typing import Sequence, Union, Literal, TextIO, Collection
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
@ -8,6 +8,7 @@ import numpy as np
|
||||||
|
|
||||||
FloatSequence = Union[np.ndarray,Sequence[float]]
|
FloatSequence = Union[np.ndarray,Sequence[float]]
|
||||||
IntSequence = Union[np.ndarray,Sequence[int]]
|
IntSequence = Union[np.ndarray,Sequence[int]]
|
||||||
|
IntCollection = Union[np.ndarray,Collection[int]]
|
||||||
FileHandle = Union[TextIO, str, Path]
|
FileHandle = Union[TextIO, str, Path]
|
||||||
CrystalFamily = Union[None,Literal['triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic']]
|
CrystalFamily = Union[None,Literal['triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic']]
|
||||||
CrystalLattice = Union[None,Literal['aP', 'mP', 'mS', 'oP', 'oS', 'oI', 'oF', 'tP', 'tI', 'hP', 'cP', 'cI', 'cF']]
|
CrystalLattice = Union[None,Literal['aP', 'mP', 'mS', 'oP', 'oS', 'oI', 'oF', 'tP', 'tI', 'hP', 'cP', 'cI', 'cF']]
|
||||||
|
|
|
@ -6,7 +6,8 @@ from typing import Tuple as _Tuple
|
||||||
from scipy import spatial as _spatial
|
from scipy import spatial as _spatial
|
||||||
import numpy as _np
|
import numpy as _np
|
||||||
|
|
||||||
from ._typehints import FloatSequence as _FloatSequence, IntSequence as _IntSequence, NumpyRngSeed as _NumpyRngSeed
|
from ._typehints import FloatSequence as _FloatSequence, IntSequence as _IntSequence, \
|
||||||
|
NumpyRngSeed as _NumpyRngSeed, IntCollection as _IntCollection
|
||||||
from . import util as _util
|
from . import util as _util
|
||||||
from . import grid_filters as _grid_filters
|
from . import grid_filters as _grid_filters
|
||||||
|
|
||||||
|
@ -106,7 +107,7 @@ def from_Poisson_disc(size: _FloatSequence,
|
||||||
|
|
||||||
|
|
||||||
def from_grid(grid,
|
def from_grid(grid,
|
||||||
selection: _IntSequence = None,
|
selection: _IntCollection = None,
|
||||||
invert_selection: bool = False,
|
invert_selection: bool = False,
|
||||||
average: bool = False,
|
average: bool = False,
|
||||||
periodic: bool = True) -> _Tuple[_np.ndarray, _np.ndarray]:
|
periodic: bool = True) -> _Tuple[_np.ndarray, _np.ndarray]:
|
||||||
|
@ -117,7 +118,7 @@ def from_grid(grid,
|
||||||
----------
|
----------
|
||||||
grid : damask.Grid
|
grid : damask.Grid
|
||||||
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 : int or collection of int, optional
|
||||||
Material IDs to consider.
|
Material IDs to consider.
|
||||||
invert_selection : bool, optional
|
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.
|
||||||
|
@ -133,8 +134,9 @@ def from_grid(grid,
|
||||||
|
|
||||||
"""
|
"""
|
||||||
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 \
|
selection_ = _util.tbd(selection)
|
||||||
_np.isin(material,selection,invert=invert_selection).flatten()
|
mask = _np.full(grid.cells.prod(),True,dtype=bool) if len(selection_) == 0 else \
|
||||||
|
_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:
|
||||||
|
|
|
@ -9,7 +9,7 @@ import re
|
||||||
import fractions
|
import fractions
|
||||||
from collections import abc
|
from collections import abc
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from typing import Union, Tuple, Iterable, Callable, Dict, List, Any, Literal
|
from typing import Union, Tuple, Iterable, Callable, Dict, List, Any, Literal, Collection
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
@ -720,7 +720,13 @@ def dict_flatten(d: Dict) -> Dict:
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
|
def tbd(arg) -> List:
|
||||||
|
if arg is None:
|
||||||
|
return []
|
||||||
|
elif isinstance(arg,(np.ndarray,Collection)):
|
||||||
|
return list(arg)
|
||||||
|
else:
|
||||||
|
return [arg]
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Classes
|
# Classes
|
||||||
|
|
|
@ -155,7 +155,7 @@ class TestGrid:
|
||||||
@pytest.mark.parametrize('selection',[None,[1],[1,2,3]])
|
@pytest.mark.parametrize('selection',[None,[1],[1,2,3]])
|
||||||
@pytest.mark.parametrize('periodic',[True,False])
|
@pytest.mark.parametrize('periodic',[True,False])
|
||||||
def test_clean(self,default,update,ref_path,stencil,selection,periodic):
|
def test_clean(self,default,update,ref_path,stencil,selection,periodic):
|
||||||
current = default.clean(stencil,selection,periodic)
|
current = default.clean(stencil,selection,periodic=periodic)
|
||||||
reference = ref_path/f'clean_{stencil}_{"+".join(map(str,[None] if selection is None else selection))}_{periodic}.vti'
|
reference = ref_path/f'clean_{stencil}_{"+".join(map(str,[None] if selection is None else selection))}_{periodic}.vti'
|
||||||
if update and stencil > 1:
|
if update and stencil > 1:
|
||||||
current.save(reference)
|
current.save(reference)
|
||||||
|
@ -296,8 +296,8 @@ class TestGrid:
|
||||||
assert grid_equal(G_1,G_2)
|
assert grid_equal(G_1,G_2)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('trigger',[[1],[]])
|
@pytest.mark.parametrize('selection',[[1],[]])
|
||||||
def test_vicinity_offset(self,trigger):
|
def test_vicinity_offset(self,selection):
|
||||||
offset = np.random.randint(2,4)
|
offset = np.random.randint(2,4)
|
||||||
vicinity = np.random.randint(2,4)
|
vicinity = np.random.randint(2,4)
|
||||||
|
|
||||||
|
@ -309,17 +309,17 @@ class TestGrid:
|
||||||
for i in [0,1,2]:
|
for i in [0,1,2]:
|
||||||
m2[(np.roll(m,+vicinity,i)-m)!=0] += offset
|
m2[(np.roll(m,+vicinity,i)-m)!=0] += offset
|
||||||
m2[(np.roll(m,-vicinity,i)-m)!=0] += offset
|
m2[(np.roll(m,-vicinity,i)-m)!=0] += offset
|
||||||
if len(trigger) > 0:
|
if len(selection) > 0:
|
||||||
m2[m==1] = 1
|
m2[m==1] = 1
|
||||||
|
|
||||||
grid = Grid(m,np.random.rand(3)).vicinity_offset(vicinity,offset,trigger=trigger)
|
grid = Grid(m,np.random.rand(3)).vicinity_offset(vicinity,offset,selection=selection)
|
||||||
|
|
||||||
assert np.all(m2==grid.material)
|
assert np.all(m2==grid.material)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('periodic',[True,False])
|
@pytest.mark.parametrize('periodic',[True,False])
|
||||||
def test_vicinity_offset_invariant(self,default,periodic):
|
def test_vicinity_offset_invariant(self,default,periodic):
|
||||||
offset = default.vicinity_offset(trigger=[default.material.max()+1,
|
offset = default.vicinity_offset(selection=[default.material.max()+1,
|
||||||
default.material.min()-1])
|
default.material.min()-1])
|
||||||
assert np.all(offset.material==default.material)
|
assert np.all(offset.material==default.material)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue