Merge branch 'development' into 'polish-rotation-error-determination'

# Conflicts:
#   python/damask/_rotation.py
This commit is contained in:
Philip Eisenlohr 2022-11-16 18:05:36 +00:00
commit 25979fcd77
9 changed files with 61 additions and 35 deletions

View File

@ -1 +1 @@
3.0.0-alpha7-86-g72408f389 3.0.0-alpha7-91-g584d5b421

View File

@ -248,7 +248,7 @@ class Colormap(mpl.colors.ListedColormap):
Parameters Parameters
---------- ----------
fraction : float or sequence of float fraction : (sequence of) float
Fractional coordinate(s) to evaluate Colormap at. Fractional coordinate(s) to evaluate Colormap at.
Returns Returns

View File

@ -50,7 +50,7 @@ class Grid:
Coordinates of grid origin in meter. Defaults to [0.0,0.0,0.0]. Coordinates of grid origin in meter. Defaults to [0.0,0.0,0.0].
initial_conditions : dictionary, optional initial_conditions : dictionary, optional
Labels and values of the inital conditions at each material point. Labels and values of the inital conditions at each material point.
comments : str or sequence of str, optional comments : (sequence of) str, optional
Additional, human-readable information, e.g. history of operations. Additional, human-readable information, e.g. history of operations.
""" """
@ -427,7 +427,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 sequence of str labels : (sequence 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.
@ -972,15 +972,16 @@ class Grid:
# materials: 1 # materials: 1
""" """
options = ('nearest',False,None)
orig = tuple(map(np.linspace,self.origin + self.size/self.cells*.5, orig = tuple(map(np.linspace,self.origin + self.size/self.cells*.5,
self.origin + self.size - self.size/self.cells*.5,self.cells)) self.origin + self.size - self.size/self.cells*.5,self.cells))
interpolator = partial(interpolate.RegularGridInterpolator,
points=orig,method='nearest',bounds_error=False,fill_value=None)
new = grid_filters.coordinates0_point(cells,self.size,self.origin) new = grid_filters.coordinates0_point(cells,self.size,self.origin)
return Grid(material = interpolate.RegularGridInterpolator(orig,self.material,*options)(new).astype(int), return Grid(material = interpolator(values=self.material)(new).astype(int),
size = self.size, size = self.size,
origin = self.origin, origin = self.origin,
initial_conditions = {k: interpolate.RegularGridInterpolator(orig,v,*options)(new) initial_conditions = {k: interpolator(values=v)(new)
for k,v in self.initial_conditions.items()}, for k,v in self.initial_conditions.items()},
comments = self.comments+[util.execution_stamp('Grid','scale')], comments = self.comments+[util.execution_stamp('Grid','scale')],
) )
@ -1043,9 +1044,9 @@ class Grid:
Parameters Parameters
---------- ----------
from_material : int or sequence of int from_material : (sequence of) int
Material indices to be substituted. Material indices to be substituted.
to_material : int or sequence of int to_material : (sequence of) int
New material indices. New material indices.
Returns Returns
@ -1104,7 +1105,7 @@ class Grid:
distance : float, optional distance : float, optional
Voxel distance checked for presence of other materials. Voxel distance checked for presence of other materials.
Defaults to sqrt(3). Defaults to sqrt(3).
selection : int or collection of int, optional selection : (collection of) int, optional
Material IDs to consider. Defaults to all. Material IDs to consider. Defaults to all.
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.
@ -1179,7 +1180,7 @@ class Grid:
Center of the primitive. Center of the primitive.
If given as integers, cell centers are addressed. If given as integers, cell centers are addressed.
If given as floats, physical coordinates are addressed. If given as floats, physical coordinates are addressed.
exponent : float or sequence of float, len (3) exponent : (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)
@ -1271,7 +1272,7 @@ class Grid:
offset : int, optional offset : int, optional
Offset (positive or negative) to tag material IDs. Offset (positive or negative) to tag material IDs.
Defaults to material.max()+1. Defaults to material.max()+1.
selection : int or collection of int, optional selection : (collection of) int, optional
Material IDs that trigger an offset. Material IDs that trigger an offset.
Defaults to any other than own material ID. Defaults to any other than own material ID.
invert_selection : bool, optional invert_selection : bool, optional

View File

@ -468,7 +468,7 @@ class Rotation:
Parameters Parameters
---------- ----------
shape : int or sequence of ints shape : (sequence of) int
New shape, number of elements needs to match the original shape. New shape, number of elements needs to match the original shape.
If an integer is supplied, then the result will be a 1-D array of that length. If an integer is supplied, then the result will be a 1-D array of that length.
order : {'C', 'F', 'A'}, optional order : {'C', 'F', 'A'}, optional
@ -496,7 +496,7 @@ class Rotation:
Parameters Parameters
---------- ----------
shape : int or sequence of ints shape : (sequence of) int
Shape of broadcasted array, needs to be compatible with the original shape. Shape of broadcasted array, needs to be compatible with the original shape.
mode : str, optional mode : str, optional
Where to preferentially locate missing dimensions. Where to preferentially locate missing dimensions.
@ -773,12 +773,12 @@ class Rotation:
raise ValueError('P ∉ {-1,1}') raise ValueError('P ∉ {-1,1}')
qu[...,1:4] *= -P qu[...,1:4] *= -P
if accept_homomorph: if accept_homomorph:
qu[qu[...,0] < 0.0] *= -1 qu[qu[...,0] < 0.0] *= -1
else: elif np.any(qu[...,0] < 0.0):
if np.any(qu[...,0] < 0.0): raise ValueError('quaternion with negative first (real) component')
raise ValueError('quaternion with negative first (real) component') if not np.allclose(np.linalg.norm(qu,axis=-1), 1.0,rtol=1e-8):
if not np.allclose(np.linalg.norm(qu,axis=-1), 1.0,rtol=0.0):
raise ValueError('quaternion is not of unit length') raise ValueError('quaternion is not of unit length')
return Rotation(qu) return Rotation(qu)
@ -1022,7 +1022,7 @@ class Rotation:
Parameters Parameters
---------- ----------
shape : int or sequence of ints, optional shape : (sequence of) int, optional
Shape of the returned array. Defaults to None, which gives a scalar. Shape of the returned array. Defaults to None, which gives a scalar.
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.
@ -1058,7 +1058,7 @@ class Rotation:
Texture intensity values (probability density or volume fraction) at Euler space grid points. Texture intensity values (probability density or volume fraction) at Euler space grid points.
phi : numpy.ndarray, shape (n,3) phi : numpy.ndarray, shape (n,3)
Grid coordinates in Euler space at which weights are defined. Grid coordinates in Euler space at which weights are defined.
shape : int or sequence of ints, optional shape : (sequence of) int, optional
Shape of the returned array. Defaults to None, which gives a scalar. Shape of the returned array. Defaults to None, which gives a scalar.
degrees : bool, optional degrees : bool, optional
Euler space grid coordinates are in degrees. Defaults to True. Euler space grid coordinates are in degrees. Defaults to True.
@ -1110,7 +1110,7 @@ class Rotation:
Central rotation. Central rotation.
sigma : float sigma : float
Standard deviation of (Gaussian) misorientation distribution. Standard deviation of (Gaussian) misorientation distribution.
shape : int or sequence of ints, optional shape : (sequence of) int, optional
Shape of the returned array. Defaults to None, which gives a scalar. Shape of the returned array. Defaults to None, which gives a scalar.
degrees : bool, optional degrees : bool, optional
sigma is given in degrees. Defaults to False. sigma is given in degrees. Defaults to False.
@ -1168,7 +1168,7 @@ class Rotation:
sigma : float, optional sigma : float, optional
Standard deviation of (Gaussian) misorientation distribution. Standard deviation of (Gaussian) misorientation distribution.
Defaults to 0. Defaults to 0.
shape : int or sequence of ints, optional shape : (sequence of) int, optional
Shape of the returned array. Defaults to None, which gives a scalar. Shape of the returned array. Defaults to None, which gives a scalar.
degrees : bool, optional degrees : bool, optional
sigma and polar coordinates are given in degrees. Defaults to False. sigma and polar coordinates are given in degrees. Defaults to False.

View File

@ -25,7 +25,7 @@ class Table:
For instance, 'F':(3,3) for a deformation gradient, or 'r':(1,) for a scalar. For instance, 'F':(3,3) for a deformation gradient, or 'r':(1,) for a scalar.
data : numpy.ndarray or pandas.DataFrame, optional data : numpy.ndarray or pandas.DataFrame, optional
Data. Existing column labels of a pandas.DataFrame will be replaced. Data. Existing column labels of a pandas.DataFrame will be replaced.
comments : str or iterable of str, optional comments : (iterable of) str, optional
Additional, human-readable information. Additional, human-readable information.
""" """
@ -464,9 +464,9 @@ class Table:
Parameters Parameters
---------- ----------
label_old : str or iterable of str label_old : (iterable of) str
Old column label(s). Old column label(s).
label_new : str or iterable of str label_new : (iterable of) str
New column label(s). New column label(s).
Returns Returns

View File

@ -110,7 +110,7 @@ class VTK:
Parameters Parameters
---------- ----------
comments : str or sequence of str comments : (sequence of) str
Comments. Comments.
""" """

View File

@ -118,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 : int or collection of int, optional selection : (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.

View File

@ -46,7 +46,7 @@ def srepr(msg,
Parameters Parameters
---------- ----------
msg : object with __repr__ or sequence of objects with __repr__ msg : (sequence of) object with __repr__
Items to join. Items to join.
glue : str, optional glue : str, optional
Glue used for joining operation. Defaults to '\n'. Glue used for joining operation. Defaults to '\n'.
@ -71,7 +71,7 @@ def emph(msg) -> str:
Parameters Parameters
---------- ----------
msg : object with __repr__ or sequence of objects with __repr__ msg : (sequence of) object with __repr__
Message to format. Message to format.
Returns Returns
@ -88,7 +88,7 @@ def deemph(msg) -> str:
Parameters Parameters
---------- ----------
msg : object with __repr__ or sequence of objects with __repr__ msg : (sequence of) object with __repr__
Message to format. Message to format.
Returns Returns
@ -105,7 +105,7 @@ def warn(msg) -> str:
Parameters Parameters
---------- ----------
msg : object with __repr__ or sequence of objects with __repr__ msg : (sequence of) object with __repr__
Message to format. Message to format.
Returns Returns
@ -122,7 +122,7 @@ def strikeout(msg) -> str:
Parameters Parameters
---------- ----------
msg : object with __repr__ or iterable of objects with __repr__ msg : (iterable of) object with __repr__
Message to format. Message to format.
Returns Returns
@ -738,7 +738,7 @@ def tail_repack(extended: _Union[str, _Sequence[str]],
Parameters Parameters
---------- ----------
extended : str or list of str extended : (list of) str
Extended string list with potentially autosplitted tailing string relative to `existing`. Extended string list with potentially autosplitted tailing string relative to `existing`.
existing : list of str existing : list of str
Base string list. Base string list.
@ -767,7 +767,7 @@ def aslist(arg: _Union[_IntCollection, int, None]) -> _List:
Parameters Parameters
---------- ----------
arg : int or collection of int or None arg : (collection of) int or None
Entity to transform into list. Entity to transform into list.
Returns Returns

View File

@ -708,6 +708,7 @@ class TestRotation:
@pytest.mark.parametrize('degrees',[True,False]) @pytest.mark.parametrize('degrees',[True,False])
def test_axis_angle(self,set_of_rotations,degrees,normalize,P): def test_axis_angle(self,set_of_rotations,degrees,normalize,P):
c = np.array([P*-1,P*-1,P*-1,1.]) c = np.array([P*-1,P*-1,P*-1,1.])
c[:3] *= 0.9 if normalize else 1.0
for rot in set_of_rotations: for rot in set_of_rotations:
m = rot.as_Euler_angles() m = rot.as_Euler_angles()
o = Rotation.from_axis_angle(rot.as_axis_angle(degrees)*c,degrees,normalize,P).as_Euler_angles() o = Rotation.from_axis_angle(rot.as_axis_angle(degrees)*c,degrees,normalize,P).as_Euler_angles()
@ -730,16 +731,30 @@ class TestRotation:
assert ok and np.isclose(np.linalg.norm(o[:3]),1.0) \ assert ok and np.isclose(np.linalg.norm(o[:3]),1.0) \
and o[3]<=np.pi+1.e-9, f'{m},{o},{rot.as_quaternion()}' and o[3]<=np.pi+1.e-9, f'{m},{o},{rot.as_quaternion()}'
def test_parallel(self,set_of_rotations):
a = np.array([[1.0,0.0,0.0],
[0.0,1.0,0.0]])
for rot in set_of_rotations:
assert rot.allclose(Rotation.from_parallel(a,rot.broadcast_to((2,))@a))
@pytest.mark.parametrize('P',[1,-1]) @pytest.mark.parametrize('P',[1,-1])
@pytest.mark.parametrize('normalize',[True,False]) @pytest.mark.parametrize('normalize',[True,False])
def test_Rodrigues(self,set_of_rotations,normalize,P): def test_Rodrigues(self,set_of_rotations,normalize,P):
c = np.array([P*-1,P*-1,P*-1,1.]) c = np.array([P*-1,P*-1,P*-1,1.])
c[:3] *= 0.9 if normalize else 1.0
for rot in set_of_rotations: for rot in set_of_rotations:
m = rot.as_matrix() m = rot.as_matrix()
o = Rotation.from_Rodrigues_vector(rot.as_Rodrigues_vector()*c,normalize,P).as_matrix() o = Rotation.from_Rodrigues_vector(rot.as_Rodrigues_vector()*c,normalize,P).as_matrix()
ok = np.allclose(m,o,atol=atol) ok = np.allclose(m,o,atol=atol)
assert ok and np.isclose(np.linalg.det(o),1.0), f'{m},{o}' assert ok and np.isclose(np.linalg.det(o),1.0), f'{m},{o}'
def test_Rodrigues_compact(self,set_of_rotations):
for rot in set_of_rotations:
c = rot.as_Rodrigues_vector(compact=True)
r = rot.as_Rodrigues_vector(compact=False)
assert np.allclose(r[:3]*r[3], c, equal_nan=True)
@pytest.mark.parametrize('P',[1,-1]) @pytest.mark.parametrize('P',[1,-1])
def test_homochoric(self,set_of_rotations,P): def test_homochoric(self,set_of_rotations,P):
cutoff = np.tan(np.pi*.5*(1.-1e-4)) cutoff = np.tan(np.pi*.5*(1.-1e-4))
@ -760,8 +775,9 @@ class TestRotation:
@pytest.mark.parametrize('P',[1,-1]) @pytest.mark.parametrize('P',[1,-1])
@pytest.mark.parametrize('accept_homomorph',[True,False]) @pytest.mark.parametrize('accept_homomorph',[True,False])
# @pytest.mark.parametrize('normalize',[True,False])
def test_quaternion(self,set_of_rotations,P,accept_homomorph): def test_quaternion(self,set_of_rotations,P,accept_homomorph):
c = np.array([1,P*-1,P*-1,P*-1]) * (-1 if accept_homomorph else 1) c = np.array([1,P*-1,P*-1,P*-1]) * (-1 if accept_homomorph else 1) #* (0.9 if normalize else 1.0)
for rot in set_of_rotations: for rot in set_of_rotations:
m = rot.as_cubochoric() m = rot.as_cubochoric()
o = Rotation.from_quaternion(rot.as_quaternion()*c,accept_homomorph,P).as_cubochoric() o = Rotation.from_quaternion(rot.as_quaternion()*c,accept_homomorph,P).as_cubochoric()
@ -889,6 +905,15 @@ class TestRotation:
with pytest.raises(ValueError): with pytest.raises(ValueError):
fr(eval(f'R.{to}()'),P=-30) fr(eval(f'R.{to}()'),P=-30)
def test_invalid_multiplication(self):
rot = Rotation.from_random()
with pytest.raises(TypeError):
rot@Rotation.from_random()
with pytest.raises(TypeError):
rot@[1,2,3,4]
@pytest.mark.parametrize('shape',[None,(3,),(4,2)]) @pytest.mark.parametrize('shape',[None,(3,),(4,2)])
def test_broadcast(self,shape): def test_broadcast(self,shape):
rot = Rotation.from_random(shape) rot = Rotation.from_random(shape)