Skip to content

Commit

Permalink
Merge branch '408-bravais-to-miller-accept-dot-undefined' into 'devel…
Browse files Browse the repository at this point in the history
…opment'

flexible Miller-Bravais indices

Closes #408

See merge request damask/DAMASK!1012
  • Loading branch information
eisenlohr committed Dec 23, 2024
2 parents f49abef + e5319d7 commit 71ecf4c
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 26 deletions.
87 changes: 67 additions & 20 deletions python/damask/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ def _docstringer(docstring: _Union[str, _Callable],
docstring : str or callable, optional
Docstring (of callable) to extend.
adopted_* : str or callable, optional
Additional information to insert into/append to respective section.
Additional information to insert into or append to respective section.
Notes
-----
Expand Down Expand Up @@ -680,7 +680,7 @@ def extend_docstring(docstring: _Union[None, str, _Callable] = None,
docstring : str or callable, optional
Docstring to extend. Defaults to that of decorated function.
adopted_* : str or callable, optional
Additional information to insert into/append to respective section.
Additional information to insert into or append to respective section.
Notes
-----
Expand Down Expand Up @@ -804,6 +804,49 @@ def get_cell_data_group(f: _h5py.File) -> str:
return get_cell_data_group(f)


def _standardize_MillerBravais(idx: _IntSequence) -> _np.ndarray:
"""
Convert Miller-Bravais indices with missing component to standard (full) form.
Parameters
----------
idx : numpy.ndarray, shape (...,4) or (...,3)
Miller–Bravais indices of crystallographic direction [uvtw] or plane normal (hkil).
The third index (t or i) can be omitted completely or given as "..." (Ellipsis).
Returns
-------
uvtw|hkil : numpy.ndarray, shape (...,4)
Miller-Bravais indices of [uvtw] direction or (hkil) plane normal.
"""
def expand(v: _np.ndarray) -> _np.ndarray:
"""Expand from 3 to 4 indices."""
return _np.block([v[...,:2], -_np.sum(v[...,:2],axis=-1,keepdims=True), v[...,2:]])

a = _np.asarray(idx)
if _np.issubdtype(a.dtype,_np.signedinteger):
if a.shape[-1] == 4:
if (_np.sum(a[...,:3],axis=-1) != 0).any(): raise ValueError(rf'u+v+t≠0 | h+k+i≠0: {a}')
return a
elif a.shape[-1] == 3:
return expand(a)
else:
if a.shape[-1] == 4:
b = (_np.block([a[...,:2],
_np.where(a[...,2:3] == ..., -_np.sum(a[...,:2],axis=-1,keepdims=True),a[...,2:3]),
a[...,3:]]))
if (_np.sum(b[...,:3].astype(int),axis=-1) != 0).any(): raise ValueError(rf'u+v+t≠0 | h+k+i≠0: {b}')
elif a.shape[-1] == 3:
b = expand(a)

if (b != (c := b.astype(int))).any():
raise ValueError(f'"uvtw" | "hkil" are not (castable to) signed integers: {a}')
return c

raise ValueError(f'invalid Miller-Bravais indices {a}')


def Bravais_to_Miller(*,
uvtw: _Optional[_IntSequence] = None,
hkil: _Optional[_IntSequence] = None) -> _np.ndarray:
Expand All @@ -812,8 +855,9 @@ def Bravais_to_Miller(*,
Parameters
----------
uvtw|hkil : numpy.ndarray, shape (...,4)
uvtw|hkil : numpy.ndarray, shape (...,4) or (...,3)
Miller–Bravais indices of crystallographic direction [uvtw] or plane normal (hkil).
The third index (t or i) can be omitted completely or given as "..." (Ellipsis).
Returns
-------
Expand All @@ -823,23 +867,22 @@ def Bravais_to_Miller(*,
"""
if (uvtw is not None) ^ (hkil is None):
raise KeyError('specify either "uvtw" or "hkil"')
if uvtw is not None and (_np.sum(_np.asarray(uvtw)[...,:3],axis=-1) != 0).any():
raise ValueError(rf'u+v+t≠0: {uvtw}')
if hkil is not None and (_np.sum(_np.asarray(hkil)[...,:3],axis=-1) != 0).any():
raise ValueError(rf'h+k+i≠0: {hkil}')

axis,basis = (_np.array(uvtw),_np.array([[2,1,0,0],
[1,2,0,0],
[0,0,0,1]])) \
if hkil is None else \
(_np.array(hkil),_np.array([[1,0,0,0],
[0,1,0,0],
[0,0,0,1]]))
elif uvtw is not None:
axis,basis = _standardize_MillerBravais(uvtw),_np.array([[2,1,0,0],
[1,2,0,0],
[0,0,0,1]])
elif hkil is not None:
axis,basis = _standardize_MillerBravais(hkil),_np.array([[1,0,0,0],
[0,1,0,0],
[0,0,0,1]])
uvw_hkl = _np.einsum('il,...l',basis,axis)
if not _np.issubdtype(uvw_hkl.dtype,_np.signedinteger):
raise TypeError('"uvtw"/"hkil" are not (signed) integers')

return uvw_hkl//_np.gcd.reduce(uvw_hkl,axis=-1,keepdims=True)


MillerBravais_to_Miller = Bravais_to_Miller


def Miller_to_Bravais(*,
uvw: _Optional[_IntSequence] = None,
hkl: _Optional[_IntSequence] = None) -> _np.ndarray:
Expand Down Expand Up @@ -868,12 +911,16 @@ def Miller_to_Bravais(*,
[ 0, 1, 0],
[-1,-1, 0],
[ 0, 0, 1]]))
uvtw_hkil = _np.einsum('il,...l',basis,axis)
if not _np.issubdtype(uvtw_hkil.dtype,_np.signedinteger):
raise TypeError('"uvw"/"hkl" are not (signed) integers')
if (axis != axis.astype(int)).any():
raise ValueError(f'"uvt" | "hki" are not (castable to) signed integers: {axis}')
uvtw_hkil = _np.einsum('il,...l',basis,axis.astype(int))

return uvtw_hkil//_np.gcd.reduce(uvtw_hkil,axis=-1,keepdims=True)


Miller_to_MillerBravais = Miller_to_Bravais


def dict_prune(d: _Dict) -> _Dict:
"""
Recursively remove empty dictionaries.
Expand Down
23 changes: 17 additions & 6 deletions python/tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,16 +221,16 @@ def test_double_Miller_to_Bravais(self):
with pytest.raises(KeyError):
util.Miller_to_Bravais(uvw=np.ones(4),hkl=np.ones(4))

@pytest.mark.parametrize('key_value',[{'uvtw':[1.,0.,-1.,0.]},
{'hkil':[1.,0.,-1.,0.]}])
@pytest.mark.parametrize('key_value',[{'uvtw':[1.,0.,-1.,1.1]},
{'hkil':[1.,0.,-1.,1.1]}])
def test_float_Bravais_to_Miller(self,key_value):
with pytest.raises(TypeError):
with pytest.raises(ValueError):
util.Bravais_to_Miller(**key_value)

@pytest.mark.parametrize('key_value',[{'uvw':[1.,0.,-1.]},
{'hkl':[1.,0.,-1.]}])
@pytest.mark.parametrize('key_value',[{'uvw':[1.,0.,-1.1]},
{'hkl':[1.,0.,-9.4]}])
def test_float_Miller_to_Bravais(self,key_value):
with pytest.raises(TypeError):
with pytest.raises(ValueError):
util.Miller_to_Bravais(**key_value)


Expand Down Expand Up @@ -267,6 +267,17 @@ def test_Miller_Bravais_Miller_random(self,kw_Miller,kw_Bravais):
def test_Bravais_Miller_Bravais(self,vector,kw_Miller,kw_Bravais):
assert np.all(vector == util.Miller_to_Bravais(**{kw_Miller:util.Bravais_to_Miller(**{kw_Bravais:vector})}))

@pytest.mark.parametrize('dim',(None,1,4))
def test_standardize_MillerBravais(self,dim):
shape = tuple(np.random.randint(1,6,dim))+(3,) if dim is not None else (3,)
idx_red = np.random.randint(-10,11,shape)
idx_full = np.block([idx_red[...,:2], -np.sum(idx_red[...,:2],axis=-1,keepdims=True), idx_red[...,2:]])
idx_missing = idx_full.astype(object)
idx_missing[...,2][idx_full[...,3]>0] = ...
assert np.equal(idx_full,util._standardize_MillerBravais(idx_red)).all() and \
np.equal(idx_full,util._standardize_MillerBravais(idx_missing)).all()


@pytest.mark.parametrize('adopted_parameters',[
pytest.param("""
p2 : str, optional
Expand Down

0 comments on commit 71ecf4c

Please sign in to comment.