From 684016f3e3e2e4a1ac9c504b12e02cda9b09df07 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 1 Jun 2021 21:29:35 +0200 Subject: [PATCH 01/43] utility function without connection to an object --- python/damask/_orientation.py | 12 +++---- python/damask/util.py | 57 ++++++++++++++++++++++++++++++++ python/tests/test_Orientation.py | 1 + python/tests/test_lattice.py | 34 ------------------- python/tests/test_util.py | 30 +++++++++++++++++ 5 files changed, 94 insertions(+), 40 deletions(-) delete mode 100644 python/tests/test_lattice.py diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 72bc8a4d8..349079bb6 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -666,10 +666,10 @@ class Orientation(Rotation): o = r[ol] p_,_p = np.zeros(m.shape[:-1]+(3,)),np.zeros(o.shape[:-1]+(3,)) - p_[...,0,:] = m[...,0,:] if m.shape[-1] == 3 else lattice_.Bravais_to_Miller(uvtw=m[...,0,0:4]) - p_[...,1,:] = m[...,1,:] if m.shape[-1] == 3 else lattice_.Bravais_to_Miller(hkil=m[...,1,0:4]) - _p[...,0,:] = o[...,0,:] if o.shape[-1] == 3 else lattice_.Bravais_to_Miller(uvtw=o[...,0,0:4]) - _p[...,1,:] = o[...,1,:] if o.shape[-1] == 3 else lattice_.Bravais_to_Miller(hkil=o[...,1,0:4]) + p_[...,0,:] = m[...,0,:] if m.shape[-1] == 3 else util.Bravais_to_Miller(uvtw=m[...,0,0:4]) + p_[...,1,:] = m[...,1,:] if m.shape[-1] == 3 else util.Bravais_to_Miller(hkil=m[...,1,0:4]) + _p[...,0,:] = o[...,0,:] if o.shape[-1] == 3 else util.Bravais_to_Miller(uvtw=o[...,0,0:4]) + _p[...,1,:] = o[...,1,:] if o.shape[-1] == 3 else util.Bravais_to_Miller(hkil=o[...,1,0:4]) return (Rotation.from_parallel(p_,_p),ol) \ if return_lattice else \ @@ -1254,8 +1254,8 @@ class Orientation(Rotation): master = lattice_.kinematics[self.lattice][mode] kinematics = {'direction':master[:,0:3],'plane':master[:,3:6]} \ if master.shape[-1] == 6 else \ - {'direction':lattice_.Bravais_to_Miller(uvtw=master[:,0:4]), - 'plane': lattice_.Bravais_to_Miller(hkil=master[:,4:8])} + {'direction':util.Bravais_to_Miller(uvtw=master[:,0:4]), + 'plane': util.Bravais_to_Miller(hkil=master[:,4:8])} except KeyError: raise (f'"{mode}" not defined for lattice "{self.lattice}"') d = self.to_frame(uvw=kinematics['direction'],with_symmetry=False) diff --git a/python/damask/util.py b/python/damask/util.py index 529b63d20..7ad788bdf 100644 --- a/python/damask/util.py +++ b/python/damask/util.py @@ -27,6 +27,7 @@ __all__=[ 'execution_stamp', 'shapeshifter', 'shapeblender', 'extend_docstring', 'extended_docstring', + 'Bravais_to_Miller', 'Miller_to_Bravais', 'DREAM3D_base_group', 'DREAM3D_cell_data_group', 'dict_prune', 'dict_flatten' ] @@ -499,6 +500,62 @@ def DREAM3D_cell_data_group(fname): return cell_data_group +def Bravais_to_Miller(*,uvtw=None,hkil=None): + """ + Transform 4 Miller–Bravais indices to 3 Miller indices of crystal direction [uvw] or plane normal (hkl). + + Parameters + ---------- + uvtw|hkil : numpy.ndarray of shape (...,4) + Miller–Bravais indices of crystallographic direction [uvtw] or plane normal (hkil). + + Returns + ------- + uvw|hkl : numpy.ndarray of shape (...,3) + Miller indices of [uvw] direction or (hkl) plane normal. + + """ + if (uvtw is not None) ^ (hkil is None): + raise KeyError('Specify either "uvtw" or "hkil"') + axis,basis = (np.array(uvtw),np.array([[1,0,-1,0], + [0,1,-1,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]])) + return np.einsum('il,...l',basis,axis) + + +def Miller_to_Bravais(*,uvw=None,hkl=None): + """ + Transform 3 Miller indices to 4 Miller–Bravais indices of crystal direction [uvtw] or plane normal (hkil). + + Parameters + ---------- + uvw|hkl : numpy.ndarray of shape (...,3) + Miller indices of crystallographic direction [uvw] or plane normal (hkl). + + Returns + ------- + uvtw|hkil : numpy.ndarray of shape (...,4) + Miller–Bravais indices of [uvtw] direction or (hkil) plane normal. + + """ + if (uvw is not None) ^ (hkl is None): + raise KeyError('Specify either "uvw" or "hkl"') + axis,basis = (np.array(uvw),np.array([[ 2,-1, 0], + [-1, 2, 0], + [-1,-1, 0], + [ 0, 0, 3]])/3) \ + if hkl is None else \ + (np.array(hkl),np.array([[ 1, 0, 0], + [ 0, 1, 0], + [-1,-1, 0], + [ 0, 0, 1]])) + return np.einsum('il,...l',basis,axis) + + def dict_prune(d): """ Recursively remove empty dictionaries. diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index 63caca34e..39d511d68 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -484,6 +484,7 @@ class TestOrientation: assert np.allclose(vector, L.to_frame(**{keyFrame:L.to_lattice(**{keyLattice:vector})})) + @pytest.mark.parametrize('lattice,a,b,c,alpha,beta,gamma', [ ('aP',0.5,2.0,3.0,0.8,0.5,1.2), diff --git a/python/tests/test_lattice.py b/python/tests/test_lattice.py deleted file mode 100644 index 4060025a1..000000000 --- a/python/tests/test_lattice.py +++ /dev/null @@ -1,34 +0,0 @@ -import pytest -import numpy as np - -from damask import lattice - -class TestLattice: - - def test_double_Bravais_to_Miller(self): - with pytest.raises(KeyError): - lattice.Bravais_to_Miller(uvtw=np.ones(4),hkil=np.ones(4)) # noqa - - def test_double_Miller_to_Bravais(self): - with pytest.raises(KeyError): - lattice.Miller_to_Bravais(uvw=np.ones(4),hkl=np.ones(4)) # noqa - - @pytest.mark.parametrize('vector',np.array([ - [1,0,0], - [1,1,0], - [1,1,1], - [1,0,-2], - ])) - @pytest.mark.parametrize('kw_Miller,kw_Bravais',[('uvw','uvtw'),('hkl','hkil')]) - def test_Miller_Bravais_Miller(self,vector,kw_Miller,kw_Bravais): - assert np.all(vector == lattice.Bravais_to_Miller(**{kw_Bravais:lattice.Miller_to_Bravais(**{kw_Miller:vector})})) - - @pytest.mark.parametrize('vector',np.array([ - [1,0,-1,2], - [1,-1,0,3], - [1,1,-2,-3], - [0,0,0,1], - ])) - @pytest.mark.parametrize('kw_Miller,kw_Bravais',[('uvw','uvtw'),('hkl','hkil')]) - def test_Bravais_Miller_Bravais(self,vector,kw_Miller,kw_Bravais): - assert np.all(vector == lattice.Miller_to_Bravais(**{kw_Miller:lattice.Bravais_to_Miller(**{kw_Bravais:vector})})) diff --git a/python/tests/test_util.py b/python/tests/test_util.py index 3ed3bf908..9af609ad0 100644 --- a/python/tests/test_util.py +++ b/python/tests/test_util.py @@ -158,3 +158,33 @@ class TestUtil: ({'A':{'B':{},'C':'D'}}, {'B':{},'C':'D'})]) def test_flatten(self,full,reduced): assert util.dict_flatten(full) == reduced + + + def test_double_Bravais_to_Miller(self): + with pytest.raises(KeyError): + util.Bravais_to_Miller(uvtw=np.ones(4),hkil=np.ones(4)) + + 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('vector',np.array([ + [1,0,0], + [1,1,0], + [1,1,1], + [1,0,-2], + ])) + @pytest.mark.parametrize('kw_Miller,kw_Bravais',[('uvw','uvtw'),('hkl','hkil')]) + def test_Miller_Bravais_Miller(self,vector,kw_Miller,kw_Bravais): + assert np.all(vector == util.Bravais_to_Miller(**{kw_Bravais:util.Miller_to_Bravais(**{kw_Miller:vector})})) + + @pytest.mark.parametrize('vector',np.array([ + [1,0,-1,2], + [1,-1,0,3], + [1,1,-2,-3], + [0,0,0,1], + ])) + @pytest.mark.parametrize('kw_Miller,kw_Bravais',[('uvw','uvtw'),('hkl','hkil')]) + 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})})) From e281d8384fc39e153c5fc2fb97b8a6b8eeb27b76 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 1 Jun 2021 21:51:28 +0200 Subject: [PATCH 02/43] distinguish 'family' and 'lattice' this requires the user to specify a keyword ('*' notation in __init__). Since all 'from_xxx' methods anyway require a keyword, it does not causes much more work (one explicit keyword in case of cube orientation when using __init__ directly). --- python/damask/_orientation.py | 52 +++++---- python/tests/test_Orientation.py | 192 ++++++++++++++++--------------- 2 files changed, 126 insertions(+), 118 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 349079bb6..f3919d1ff 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -30,10 +30,12 @@ lattice_symmetries = { } _parameter_doc = \ - """lattice : str - Either a crystal family out of {triclinic, monoclinic, orthorhombic, tetragonal, hexagonal, cubic} - or a Bravais lattice out of {aP, mP, mS, oP, oS, oI, oF, tP, tI, hP, cP, cI, cF}. - When specifying a Bravais lattice, additional lattice parameters might be required. + """ + family : {'triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic'} + Crystal family. Mutual exclusive with 'lattice' parameter. + lattice : {'aP', 'mP', 'mS', 'oP', 'oS', 'oI', 'oF', 'tP', 'tI', 'hP', 'cP', 'cI', 'cF'}. + Bravais lattice in Pearson notation. + Mutual exclusive with 'family' parameter. a : float, optional Length of lattice parameter 'a'. b : float, optional @@ -110,7 +112,8 @@ class Orientation(Rotation): @util.extend_docstring(_parameter_doc) def __init__(self, - rotation = None, + rotation = np.array([1.0,0.0,0.0,0.0]), *, + family = None, lattice = None, a = None,b = None,c = None, alpha = None,beta = None,gamma = None, @@ -126,9 +129,16 @@ class Orientation(Rotation): Defaults to no rotation. """ - Rotation.__init__(self) if rotation is None else Rotation.__init__(self,rotation=rotation) + Rotation.__init__(self,rotation=rotation) - if lattice in lattice_symmetries: + if family in set(lattice_symmetries.values()) and lattice is None: + self.family = family + self.lattice = None + + self.a = self.b = self.c = None + self.alpha = self.beta = self.gamma = None + + elif lattice in lattice_symmetries: self.family = lattice_symmetries[lattice] self.lattice = lattice @@ -169,14 +179,8 @@ class Orientation(Rotation): > np.sum(np.roll([self.alpha,self.beta,self.gamma],r)[1:]) for r in range(3)]): raise ValueError ('Each lattice angle must be less than sum of others') - elif lattice in set(lattice_symmetries.values()): - self.family = lattice - self.lattice = None - - self.a = self.b = self.c = None - self.alpha = self.beta = self.gamma = None else: - raise KeyError(f'Lattice "{lattice}" is unknown') + raise KeyError(f'no valid family or lattice') def __repr__(self): @@ -188,16 +192,16 @@ class Orientation(Rotation): def __copy__(self,**kwargs): """Create deep copy.""" - return self.__class__(rotation=kwargs['rotation'] if 'rotation' in kwargs else self.quaternion, - lattice =kwargs['lattice'] if 'lattice' in kwargs else self.lattice - if self.lattice is not None else self.family, - a =kwargs['a'] if 'a' in kwargs else self.a, - b =kwargs['b'] if 'b' in kwargs else self.b, - c =kwargs['c'] if 'c' in kwargs else self.c, - alpha =kwargs['alpha'] if 'alpha' in kwargs else self.alpha, - beta =kwargs['beta'] if 'beta' in kwargs else self.beta, - gamma =kwargs['gamma'] if 'gamma' in kwargs else self.gamma, - degrees =kwargs['degrees'] if 'degrees' in kwargs else None, + return self.__class__(rotation= kwargs['rotation'] if 'rotation' in kwargs else self.quaternion, + family = kwargs['family'] if 'family' in kwargs else self.family, + lattice = kwargs['lattice'] if 'lattice' in kwargs else self.lattice, + a = kwargs['a'] if 'a' in kwargs else self.a, + b = kwargs['b'] if 'b' in kwargs else self.b, + c = kwargs['c'] if 'c' in kwargs else self.c, + alpha = kwargs['alpha'] if 'alpha' in kwargs else self.alpha, + beta = kwargs['beta'] if 'beta' in kwargs else self.beta, + gamma = kwargs['gamma'] if 'gamma' in kwargs else self.gamma, + degrees = kwargs['degrees'] if 'degrees' in kwargs else None, ) copy = __copy__ diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index 39d511d68..7fc20a3e6 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -25,38 +25,42 @@ def set_of_rodrigues(set_of_quaternions): class TestOrientation: - @pytest.mark.parametrize('lattice',crystal_families) + @pytest.mark.parametrize('family',crystal_families) @pytest.mark.parametrize('shape',[None,5,(4,6)]) - def test_equal(self,lattice,shape): + def test_equal(self,family,shape): R = Rotation.from_random(shape) - assert Orientation(R,lattice) == Orientation(R,lattice) if shape is None else \ - (Orientation(R,lattice) == Orientation(R,lattice)).all() + assert Orientation(R,family=family) == Orientation(R,family=family) if shape is None else \ + (Orientation(R,family=family) == Orientation(R,family=family)).all() - @pytest.mark.parametrize('lattice',crystal_families) + @pytest.mark.parametrize('family',crystal_families) @pytest.mark.parametrize('shape',[None,5,(4,6)]) - def test_unequal(self,lattice,shape): + def test_unequal(self,family,shape): R = Rotation.from_random(shape) - assert not ( Orientation(R,lattice) != Orientation(R,lattice) if shape is None else \ - (Orientation(R,lattice) != Orientation(R,lattice)).any()) + assert not ( Orientation(R,family=family) != Orientation(R,family=family) if shape is None else \ + (Orientation(R,family=family) != Orientation(R,family=family)).any()) - @pytest.mark.parametrize('lattice',crystal_families) + @pytest.mark.parametrize('family',crystal_families) @pytest.mark.parametrize('shape',[None,5,(4,6)]) - def test_close(self,lattice,shape): - R = Orientation.from_random(lattice=lattice,shape=shape) + def test_close(self,family,shape): + R = Orientation.from_random(family=family,shape=shape) assert R.isclose(R.reduced).all() and R.allclose(R.reduced) @pytest.mark.parametrize('a,b',[ - (dict(rotation=[1,0,0,0],lattice='triclinic'), - dict(rotation=[0.5,0.5,0.5,0.5],lattice='triclinic')), + (dict(rotation=[1,0,0,0],family='triclinic'), + dict(rotation=[0.5,0.5,0.5,0.5],family='triclinic')), - (dict(rotation=[1,0,0,0],lattice='cubic'), - dict(rotation=[1,0,0,0],lattice='hexagonal')), + (dict(rotation=[1,0,0,0],family='cubic'), + dict(rotation=[1,0,0,0],family='hexagonal')), + ]) + def test_unequal_family(self,a,b): + assert Orientation(**a) != Orientation(**b) + @pytest.mark.parametrize('a,b',[ (dict(rotation=[1,0,0,0],lattice='cF',a=1), dict(rotation=[1,0,0,0],lattice='cF',a=2)), ]) - def test_nonequal(self,a,b): + def test_unequal_lattice(self,a,b): assert Orientation(**a) != Orientation(**b) @pytest.mark.parametrize('kwargs',[ @@ -100,47 +104,47 @@ class TestOrientation: assert o != p def test_from_quaternion(self): - assert np.all(Orientation.from_quaternion(q=np.array([1,0,0,0]),lattice='triclinic').as_matrix() + assert np.all(Orientation.from_quaternion(q=np.array([1,0,0,0]),family='triclinic').as_matrix() == np.eye(3)) def test_from_Euler_angles(self): - assert np.all(Orientation.from_Euler_angles(phi=np.zeros(3),lattice='triclinic').as_matrix() + assert np.all(Orientation.from_Euler_angles(phi=np.zeros(3),family='triclinic').as_matrix() == np.eye(3)) def test_from_axis_angle(self): - assert np.all(Orientation.from_axis_angle(axis_angle=[1,0,0,0],lattice='triclinic').as_matrix() + assert np.all(Orientation.from_axis_angle(axis_angle=[1,0,0,0],family='triclinic').as_matrix() == np.eye(3)) def test_from_basis(self): - assert np.all(Orientation.from_basis(basis=np.eye(3),lattice='triclinic').as_matrix() + assert np.all(Orientation.from_basis(basis=np.eye(3),family='triclinic').as_matrix() == np.eye(3)) def test_from_matrix(self): - assert np.all(Orientation.from_matrix(R=np.eye(3),lattice='triclinic').as_matrix() + assert np.all(Orientation.from_matrix(R=np.eye(3),family='triclinic').as_matrix() == np.eye(3)) def test_from_Rodrigues_vector(self): - assert np.all(Orientation.from_Rodrigues_vector(rho=np.array([0,0,1,0]),lattice='triclinic').as_matrix() + assert np.all(Orientation.from_Rodrigues_vector(rho=np.array([0,0,1,0]),family='triclinic').as_matrix() == np.eye(3)) def test_from_homochoric(self): - assert np.all(Orientation.from_homochoric(h=np.zeros(3),lattice='triclinic').as_matrix() + assert np.all(Orientation.from_homochoric(h=np.zeros(3),family='triclinic').as_matrix() == np.eye(3)) def test_from_cubochoric(self): - assert np.all(Orientation.from_cubochoric(x=np.zeros(3),lattice='triclinic').as_matrix() + assert np.all(Orientation.from_cubochoric(x=np.zeros(3),family='triclinic').as_matrix() == np.eye(3)) def test_from_spherical_component(self): assert np.all(Orientation.from_spherical_component(center=Rotation(), - sigma=0.0,N=1,lattice='triclinic').as_matrix() + sigma=0.0,N=1,family='triclinic').as_matrix() == np.eye(3)) def test_from_fiber_component(self): r = Rotation.from_fiber_component(alpha=np.zeros(2),beta=np.zeros(2), sigma=0.0,N=1,rng_seed=0) assert np.all(Orientation.from_fiber_component(alpha=np.zeros(2),beta=np.zeros(2), - sigma=0.0,N=1,rng_seed=0,lattice='triclinic').quaternion + sigma=0.0,N=1,rng_seed=0,family='triclinic').quaternion == r.quaternion) @pytest.mark.parametrize('kwargs',[ @@ -185,26 +189,26 @@ class TestOrientation: with pytest.raises(ValueError): Orientation(lattice='aP',a=1,b=2,c=3,alpha=45,beta=45,gamma=90.0001,degrees=True) # noqa - @pytest.mark.parametrize('lattice',crystal_families) + @pytest.mark.parametrize('family',crystal_families) @pytest.mark.parametrize('angle',[10,20,30,40]) - def test_average(self,angle,lattice): - o = Orientation.from_axis_angle(lattice=lattice,axis_angle=[[0,0,1,10],[0,0,1,angle]],degrees=True) + def test_average(self,angle,family): + o = Orientation.from_axis_angle(family=family,axis_angle=[[0,0,1,10],[0,0,1,angle]],degrees=True) avg_angle = o.average().as_axis_angle(degrees=True,pair=True)[1] assert np.isclose(avg_angle,10+(angle-10)/2.) - @pytest.mark.parametrize('lattice',crystal_families) - def test_reduced_equivalent(self,lattice): - i = Orientation(lattice=lattice) - o = Orientation.from_random(lattice=lattice) + @pytest.mark.parametrize('family',crystal_families) + def test_reduced_equivalent(self,family): + i = Orientation(family=family) + o = Orientation.from_random(family=family) eq = o.equivalent FZ = np.argmin(abs(eq.misorientation(i.broadcast_to(len(eq))).as_axis_angle(pair=True)[1])) assert o.reduced == eq[FZ] - @pytest.mark.parametrize('lattice',crystal_families) + @pytest.mark.parametrize('family',crystal_families) @pytest.mark.parametrize('N',[1,8,32]) - def test_disorientation(self,lattice,N): - o = Orientation.from_random(lattice=lattice,shape=N) - p = Orientation.from_random(lattice=lattice,shape=N) + def test_disorientation(self,family,N): + o = Orientation.from_random(family=family,shape=N) + p = Orientation.from_random(family=family,shape=N) d,ops = o.disorientation(p,return_operators=True) @@ -218,72 +222,72 @@ class TestOrientation: .misorientation(p[n].equivalent[ops[n][1]]) .as_quaternion()) - @pytest.mark.parametrize('lattice',crystal_families) + @pytest.mark.parametrize('family',crystal_families) @pytest.mark.parametrize('a,b',[ ((2,3,2),(2,3,2)), ((2,2),(4,4)), ((3,1),(1,3)), (None,None), ]) - def test_disorientation_blending(self,lattice,a,b): - o = Orientation.from_random(lattice=lattice,shape=a) - p = Orientation.from_random(lattice=lattice,shape=b) + def test_disorientation_blending(self,family,a,b): + o = Orientation.from_random(family=family,shape=a) + p = Orientation.from_random(family=family,shape=b) blend = util.shapeblender(o.shape,p.shape) for loc in np.random.randint(0,blend,(10,len(blend))): assert o[tuple(loc[:len(o.shape)])].disorientation(p[tuple(loc[-len(p.shape):])]) \ .isclose(o.disorientation(p)[tuple(loc)]) - @pytest.mark.parametrize('lattice',crystal_families) - def test_disorientation360(self,lattice): - o_1 = Orientation(Rotation(),lattice) - o_2 = Orientation.from_Euler_angles(lattice=lattice,phi=[360,0,0],degrees=True) + @pytest.mark.parametrize('family',crystal_families) + def test_disorientation360(self,family): + o_1 = Orientation(Rotation(),family=family) + o_2 = Orientation.from_Euler_angles(family=family,phi=[360,0,0],degrees=True) assert np.allclose((o_1.disorientation(o_2)).as_matrix(),np.eye(3)) - @pytest.mark.parametrize('lattice',crystal_families) + @pytest.mark.parametrize('family',crystal_families) @pytest.mark.parametrize('shape',[(1),(2,3),(4,3,2)]) - def test_reduced_vectorization(self,lattice,shape): - o = Orientation.from_random(lattice=lattice,shape=shape) + def test_reduced_vectorization(self,family,shape): + o = Orientation.from_random(family=family,shape=shape) for r, theO in zip(o.reduced.flatten(),o.flatten()): assert r == theO.reduced - @pytest.mark.parametrize('lattice',crystal_families) - def test_reduced_corner_cases(self,lattice): + @pytest.mark.parametrize('family',crystal_families) + def test_reduced_corner_cases(self,family): # test whether there is always a sym-eq rotation that falls into the FZ N = np.random.randint(10,40) size = np.ones(3)*np.pi**(2./3.) grid = grid_filters.coordinates0_node([N+1,N+1,N+1],size,-size*.5) - evenly_distributed = Orientation.from_cubochoric(x=grid[:-2,:-2,:-2],lattice=lattice) + evenly_distributed = Orientation.from_cubochoric(x=grid[:-2,:-2,:-2],family=family) assert evenly_distributed.shape == evenly_distributed.reduced.shape - @pytest.mark.parametrize('lattice',crystal_families) + @pytest.mark.parametrize('family',crystal_families) @pytest.mark.parametrize('shape',[(1),(2,3),(4,3,2)]) @pytest.mark.parametrize('vector',np.array([[1,0,0],[1,2,3],[-1,1,-1]])) @pytest.mark.parametrize('proper',[True,False]) - def test_to_SST_vectorization(self,lattice,shape,vector,proper): - o = Orientation.from_random(lattice=lattice,shape=shape) + def test_to_SST_vectorization(self,family,shape,vector,proper): + o = Orientation.from_random(family=family,shape=shape) for r, theO in zip(o.to_SST(vector=vector,proper=proper).reshape((-1,3)),o.flatten()): assert np.allclose(r,theO.to_SST(vector=vector,proper=proper)) - @pytest.mark.parametrize('lattice',crystal_families) + @pytest.mark.parametrize('family',crystal_families) @pytest.mark.parametrize('shape',[(1),(2,3),(4,3,2)]) @pytest.mark.parametrize('vector',np.array([[1,0,0],[1,2,3],[-1,1,-1]])) @pytest.mark.parametrize('proper',[True,False]) @pytest.mark.parametrize('in_SST',[True,False]) - def test_IPF_color_vectorization(self,lattice,shape,vector,proper,in_SST): - o = Orientation.from_random(lattice=lattice,shape=shape) + def test_IPF_color_vectorization(self,family,shape,vector,proper,in_SST): + o = Orientation.from_random(family=family,shape=shape) for r, theO in zip(o.IPF_color(vector,in_SST=in_SST,proper=proper).reshape((-1,3)),o.flatten()): assert np.allclose(r,theO.IPF_color(vector,in_SST=in_SST,proper=proper)) - @pytest.mark.parametrize('lattice',crystal_families) + @pytest.mark.parametrize('family',crystal_families) @pytest.mark.parametrize('a,b',[ ((2,3,2),(2,3,2)), ((2,2),(4,4)), ((3,1),(1,3)), (None,(3,)), ]) - def test_to_SST_blending(self,lattice,a,b): - o = Orientation.from_random(lattice=lattice,shape=a) + def test_to_SST_blending(self,family,a,b): + o = Orientation.from_random(family=family,shape=a) v = np.random.random(b+(3,)) blend = util.shapeblender(o.shape,b) for loc in np.random.randint(0,blend,(10,len(blend))): @@ -298,55 +302,55 @@ class TestOrientation: {'label':'blue', 'RGB':[0,0,1],'direction':[1,1,1]}]) @pytest.mark.parametrize('proper',[True,False]) def test_IPF_cubic(self,color,proper): - cube = Orientation(lattice='cubic') + cube = Orientation(family='cubic') for direction in set(permutations(np.array(color['direction']))): assert np.allclose(np.array(color['RGB']), cube.IPF_color(vector=np.array(direction),proper=proper)) - @pytest.mark.parametrize('lattice',crystal_families) + @pytest.mark.parametrize('family',crystal_families) @pytest.mark.parametrize('proper',[True,False]) - def test_IPF_equivalent(self,set_of_quaternions,lattice,proper): + def test_IPF_equivalent(self,set_of_quaternions,family,proper): direction = np.random.random(3)*2.0-1.0 - o = Orientation(rotation=set_of_quaternions,lattice=lattice).equivalent + o = Orientation(rotation=set_of_quaternions,family=family).equivalent color = o.IPF_color(vector=direction,proper=proper) assert np.allclose(np.broadcast_to(color[0,...],color.shape),color) - @pytest.mark.parametrize('lattice',crystal_families) - def test_in_FZ_vectorization(self,set_of_rodrigues,lattice): - result = Orientation.from_Rodrigues_vector(rho=set_of_rodrigues.reshape((-1,4,4)),lattice=lattice).in_FZ.reshape(-1) + @pytest.mark.parametrize('family',crystal_families) + def test_in_FZ_vectorization(self,set_of_rodrigues,family): + result = Orientation.from_Rodrigues_vector(rho=set_of_rodrigues.reshape((-1,4,4)),family=family).in_FZ.reshape(-1) for r,rho in zip(result,set_of_rodrigues[:len(result)]): - assert r == Orientation.from_Rodrigues_vector(rho=rho,lattice=lattice).in_FZ + assert r == Orientation.from_Rodrigues_vector(rho=rho,family=family).in_FZ - @pytest.mark.parametrize('lattice',crystal_families) - def test_in_disorientation_FZ_vectorization(self,set_of_rodrigues,lattice): + @pytest.mark.parametrize('family',crystal_families) + def test_in_disorientation_FZ_vectorization(self,set_of_rodrigues,family): result = Orientation.from_Rodrigues_vector(rho=set_of_rodrigues.reshape((-1,4,4)), - lattice=lattice).in_disorientation_FZ.reshape(-1) + family=family).in_disorientation_FZ.reshape(-1) for r,rho in zip(result,set_of_rodrigues[:len(result)]): - assert r == Orientation.from_Rodrigues_vector(rho=rho,lattice=lattice).in_disorientation_FZ + assert r == Orientation.from_Rodrigues_vector(rho=rho,family=family).in_disorientation_FZ @pytest.mark.parametrize('proper',[True,False]) - @pytest.mark.parametrize('lattice',crystal_families) - def test_in_SST_vectorization(self,lattice,proper): + @pytest.mark.parametrize('family',crystal_families) + def test_in_SST_vectorization(self,family,proper): vecs = np.random.rand(20,4,3) - result = Orientation(lattice=lattice).in_SST(vecs,proper).flatten() + result = Orientation(family=family).in_SST(vecs,proper).flatten() for r,v in zip(result,vecs.reshape((-1,3))): - assert np.all(r == Orientation(lattice=lattice).in_SST(v,proper)) + assert np.all(r == Orientation(family=family).in_SST(v,proper)) - @pytest.mark.parametrize('invalid_lattice',['fcc','bcc','hello']) - def test_invalid_lattice_init(self,invalid_lattice): + @pytest.mark.parametrize('invalid_family',['fcc','bcc','hello']) + def test_invalid_lattice_init(self,invalid_family): with pytest.raises(KeyError): - Orientation(lattice=invalid_lattice) # noqa + Orientation(family=invalid_family) # noqa @pytest.mark.parametrize('invalid_family',[None,'fcc','bcc','hello']) def test_invalid_symmetry_family(self,invalid_family): with pytest.raises(KeyError): - o = Orientation(lattice='cubic') + o = Orientation(family='cubic') o.family = invalid_family o.symmetry_operations # noqa def test_invalid_rot(self): with pytest.raises(TypeError): - Orientation.from_random(lattice='cubic') * np.ones(3) + Orientation.from_random(family='cubic') * np.ones(3) def test_missing_symmetry_immutable(self): with pytest.raises(KeyError): @@ -388,14 +392,14 @@ class TestOrientation: a=a,b=b,c=c, alpha=alpha,beta=beta,gamma=gamma).related(relation) # noqa - @pytest.mark.parametrize('lattice',crystal_families) + @pytest.mark.parametrize('family',crystal_families) @pytest.mark.parametrize('proper',[True,False]) - def test_in_SST(self,lattice,proper): - assert Orientation(lattice=lattice).in_SST(np.zeros(3),proper) + def test_in_SST(self,family,proper): + assert Orientation(family=family).in_SST(np.zeros(3),proper) @pytest.mark.parametrize('function',['in_SST','IPF_color']) def test_invalid_argument(self,function): - o = Orientation(lattice='cubic') # noqa + o = Orientation(family='cubic') # noqa with pytest.raises(ValueError): eval(f'o.{function}(np.ones(4))') @@ -409,7 +413,7 @@ class TestOrientation: def test_relationship_vectorize(self,set_of_quaternions,lattice,model): r = Orientation(rotation=set_of_quaternions[:200].reshape((50,4,4)),lattice=lattice).related(model) for i in range(200): - assert (r.reshape((-1,200))[:,i] == Orientation(set_of_quaternions[i],lattice).related(model)).all() + assert (r.reshape((-1,200))[:,i] == Orientation(set_of_quaternions[i],lattice=lattice).related(model)).all() @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) @pytest.mark.parametrize('lattice',['cF','cI']) @@ -512,12 +516,12 @@ class TestOrientation: == o.shape + (o.symmetry_operations.shape if with_symmetry else ()) + vector.shape @pytest.mark.parametrize('lattice',['hP','cI','cF']) - @pytest.mark.parametrize('mode',['slip','twin']) - def test_Schmid(self,update,ref_path,lattice,mode): + def test_Schmid(self,update,ref_path,lattice): L = Orientation(lattice=lattice) - reference = ref_path/f'{lattice}_{mode}.txt' - P = L.Schmid(mode) - if update: - table = Table(P.reshape(-1,9),{'Schmid':(3,3,)}) - table.save(reference) - assert np.allclose(P,Table.load(reference).get('Schmid')) + for mode in ['slip','twin']: # ToDo test tI + reference = ref_path/f'{lattice}_{mode}.txt' + P = L.Schmid(mode) + if update: + table = Table(P.reshape(-1,9),{'Schmid':(3,3,)}) + table.save(reference) + assert np.allclose(P,Table.load(reference).get('Schmid')) From b55d51491df055747ca5deb2b520029ad926bc2d Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 1 Jun 2021 23:23:04 +0200 Subject: [PATCH 03/43] separating functionality --- python/damask/__init__.py | 1 + python/damask/_lattice_family.py | 192 +++++++++++++++++++++++ python/damask/_orientation.py | 258 +++---------------------------- python/damask/lattice.py | 57 ------- python/tests/test_Orientation.py | 11 +- 5 files changed, 220 insertions(+), 299 deletions(-) create mode 100644 python/damask/_lattice_family.py diff --git a/python/damask/__init__.py b/python/damask/__init__.py index ad46d454f..5cdfbe899 100644 --- a/python/damask/__init__.py +++ b/python/damask/__init__.py @@ -18,6 +18,7 @@ from . import lattice # noqa #Modules that contain only one class (of the same name), are prefixed by a '_'. #For example, '_colormap' containsa class called 'Colormap' which is imported as 'damask.Colormap'. from ._rotation import Rotation # noqa +from ._lattice_family import LatticeFamily # noqa from ._orientation import Orientation # noqa from ._table import Table # noqa from ._vtk import VTK # noqa diff --git a/python/damask/_lattice_family.py b/python/damask/_lattice_family.py new file mode 100644 index 000000000..8f352e8df --- /dev/null +++ b/python/damask/_lattice_family.py @@ -0,0 +1,192 @@ +import numpy as np + +from . import Rotation + +class LatticeFamily(): + + def __init__(self,family): + """Symmetry-related operations for crystal families.""" + if family not in self._immutable.keys(): + raise KeyError(f'invalid lattice family "{family}"') + self.family = family + + + @property + def symmetry_operations(self): + """Symmetry operations as Rotations.""" + return Rotation.from_quaternion(self._symmetry_operations[self.family],accept_homomorph=True) + + + @property + def immutable(self): + """Return immutable parameters lattice parameters.""" + return self._immutable[self.family] + + + @property + def basis(self): + """ + Corners of the standard triangle. + + Not yet defined for monoclinic. + + + References + ---------- + Bases are computed from + + >>> basis = { + ... 'cubic' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red + ... [1.,0.,1.]/np.sqrt(2.), # green + ... [1.,1.,1.]/np.sqrt(3.)]).T), # blue + ... 'hexagonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red + ... [1.,0.,0.], # green + ... [np.sqrt(3.),1.,0.]/np.sqrt(4.)]).T), # blue + ... 'tetragonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red + ... [1.,0.,0.], # green + ... [1.,1.,0.]/np.sqrt(2.)]).T), # blue + ... 'orthorhombic': np.linalg.inv(np.array([[0.,0.,1.], # direction of red + ... [1.,0.,0.], # green + ... [0.,1.,0.]]).T), # blue + ... } + + """ + return self._basis.get(self.family,None) + + + _symmetry_operations = { + 'cubic': [ + [ 1.0, 0.0, 0.0, 0.0 ], + [ 0.0, 1.0, 0.0, 0.0 ], + [ 0.0, 0.0, 1.0, 0.0 ], + [ 0.0, 0.0, 0.0, 1.0 ], + [ 0.0, 0.0, 0.5*np.sqrt(2), 0.5*np.sqrt(2) ], + [ 0.0, 0.0, 0.5*np.sqrt(2),-0.5*np.sqrt(2) ], + [ 0.0, 0.5*np.sqrt(2), 0.0, 0.5*np.sqrt(2) ], + [ 0.0, 0.5*np.sqrt(2), 0.0, -0.5*np.sqrt(2) ], + [ 0.0, 0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0 ], + [ 0.0, -0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0 ], + [ 0.5, 0.5, 0.5, 0.5 ], + [-0.5, 0.5, 0.5, 0.5 ], + [-0.5, 0.5, 0.5, -0.5 ], + [-0.5, 0.5, -0.5, 0.5 ], + [-0.5, -0.5, 0.5, 0.5 ], + [-0.5, -0.5, 0.5, -0.5 ], + [-0.5, -0.5, -0.5, 0.5 ], + [-0.5, 0.5, -0.5, -0.5 ], + [-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], + [ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], + [-0.5*np.sqrt(2), 0.0, 0.5*np.sqrt(2), 0.0 ], + [-0.5*np.sqrt(2), 0.0, -0.5*np.sqrt(2), 0.0 ], + [-0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0, 0.0 ], + [-0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0, 0.0 ], + ], + 'hexagonal': [ + [ 1.0, 0.0, 0.0, 0.0 ], + [-0.5*np.sqrt(3), 0.0, 0.0, -0.5 ], + [ 0.5, 0.0, 0.0, 0.5*np.sqrt(3) ], + [ 0.0, 0.0, 0.0, 1.0 ], + [-0.5, 0.0, 0.0, 0.5*np.sqrt(3) ], + [-0.5*np.sqrt(3), 0.0, 0.0, 0.5 ], + [ 0.0, 1.0, 0.0, 0.0 ], + [ 0.0, -0.5*np.sqrt(3), 0.5, 0.0 ], + [ 0.0, 0.5, -0.5*np.sqrt(3), 0.0 ], + [ 0.0, 0.0, 1.0, 0.0 ], + [ 0.0, -0.5, -0.5*np.sqrt(3), 0.0 ], + [ 0.0, 0.5*np.sqrt(3), 0.5, 0.0 ], + ], + 'tetragonal': [ + [ 1.0, 0.0, 0.0, 0.0 ], + [ 0.0, 1.0, 0.0, 0.0 ], + [ 0.0, 0.0, 1.0, 0.0 ], + [ 0.0, 0.0, 0.0, 1.0 ], + [ 0.0, 0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0 ], + [ 0.0, -0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0 ], + [ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], + [-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], + ], + 'orthorhombic': [ + [ 1.0,0.0,0.0,0.0 ], + [ 0.0,1.0,0.0,0.0 ], + [ 0.0,0.0,1.0,0.0 ], + [ 0.0,0.0,0.0,1.0 ], + ], + 'monoclinic': [ + [ 1.0,0.0,0.0,0.0 ], + [ 0.0,0.0,1.0,0.0 ], + ], + 'triclinic': [ + [ 1.0,0.0,0.0,0.0 ], + ]} + + + _immutable = { + 'cubic': + { + 'b': 1.0, + 'c': 1.0, + 'alpha': np.pi/2., + 'beta': np.pi/2., + 'gamma': np.pi/2., + }, + 'hexagonal': + { + 'b': 1.0, + 'alpha': np.pi/2., + 'beta': np.pi/2., + 'gamma': 2.*np.pi/3., + }, + 'tetragonal': + { + 'b': 1.0, + 'alpha': np.pi/2., + 'beta': np.pi/2., + 'gamma': np.pi/2., + }, + 'orthorhombic': + { + 'alpha': np.pi/2., + 'beta': np.pi/2., + 'gamma': np.pi/2., + }, + 'monoclinic': + { + 'alpha': np.pi/2., + 'gamma': np.pi/2., + }, + 'triclinic': {} + } + + + _basis = { + 'cubic': {'improper':np.array([ [-1. , 0. , 1. ], + [ np.sqrt(2.) , -np.sqrt(2.) , 0. ], + [ 0. , np.sqrt(3.) , 0. ] ]), + 'proper':np.array([ [ 0. , -1. , 1. ], + [-np.sqrt(2.) , np.sqrt(2.) , 0. ], + [ np.sqrt(3.) , 0. , 0. ] ]), + }, + 'hexagonal': + {'improper':np.array([ [ 0. , 0. , 1. ], + [ 1. , -np.sqrt(3.) , 0. ], + [ 0. , 2. , 0. ] ]), + 'proper':np.array([ [ 0. , 0. , 1. ], + [-1. , np.sqrt(3.) , 0. ], + [ np.sqrt(3.) , -1. , 0. ] ]), + }, + 'tetragonal': + {'improper':np.array([ [ 0. , 0. , 1. ], + [ 1. , -1. , 0. ], + [ 0. , np.sqrt(2.) , 0. ] ]), + 'proper':np.array([ [ 0. , 0. , 1. ], + [-1. , 1. , 0. ], + [ np.sqrt(2.) , 0. , 0. ] ]), + }, + 'orthorhombic': + {'improper':np.array([ [ 0., 0., 1.], + [ 1., 0., 0.], + [ 0., 1., 0.] ]), + 'proper':np.array([ [ 0., 0., 1.], + [-1., 0., 0.], + [ 0., 1., 0.] ]), + }} diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index f3919d1ff..8a3f9fe24 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -3,6 +3,7 @@ import inspect import numpy as np from . import Rotation +from . import LatticeFamily from . import util from . import tensor from . import lattice as lattice_ @@ -132,16 +133,27 @@ class Orientation(Rotation): Rotation.__init__(self,rotation=rotation) if family in set(lattice_symmetries.values()) and lattice is None: - self.family = family + self.family = family self.lattice = None + l = LatticeFamily(self.family) + self.immutable = l.immutable + self.basis = l.basis + self.symmetry_operations = l.symmetry_operations + self.a = self.b = self.c = None self.alpha = self.beta = self.gamma = None elif lattice in lattice_symmetries: + self.family = lattice_symmetries[lattice] self.lattice = lattice + l = LatticeFamily(self.family) + self.immutable = l.immutable + self.basis = l.basis + self.symmetry_operations = l.symmetry_operations + self.a = 1 if a is None else a self.b = b self.c = c @@ -201,7 +213,6 @@ class Orientation(Rotation): alpha = kwargs['alpha'] if 'alpha' in kwargs else self.alpha, beta = kwargs['beta'] if 'beta' in kwargs else self.beta, gamma = kwargs['gamma'] if 'gamma' in kwargs else self.gamma, - degrees = kwargs['degrees'] if 'degrees' in kwargs else None, ) copy = __copy__ @@ -435,84 +446,6 @@ class Orientation(Rotation): return o.copy(rotation=Rotation.from_matrix(tensor.transpose(om/np.linalg.norm(om,axis=-1,keepdims=True)))) - @property - def symmetry_operations(self): - """Symmetry operations as Rotations.""" - if self.family == 'cubic': - sym_quats = [ - [ 1.0, 0.0, 0.0, 0.0 ], - [ 0.0, 1.0, 0.0, 0.0 ], - [ 0.0, 0.0, 1.0, 0.0 ], - [ 0.0, 0.0, 0.0, 1.0 ], - [ 0.0, 0.0, 0.5*np.sqrt(2), 0.5*np.sqrt(2) ], - [ 0.0, 0.0, 0.5*np.sqrt(2),-0.5*np.sqrt(2) ], - [ 0.0, 0.5*np.sqrt(2), 0.0, 0.5*np.sqrt(2) ], - [ 0.0, 0.5*np.sqrt(2), 0.0, -0.5*np.sqrt(2) ], - [ 0.0, 0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0 ], - [ 0.0, -0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0 ], - [ 0.5, 0.5, 0.5, 0.5 ], - [-0.5, 0.5, 0.5, 0.5 ], - [-0.5, 0.5, 0.5, -0.5 ], - [-0.5, 0.5, -0.5, 0.5 ], - [-0.5, -0.5, 0.5, 0.5 ], - [-0.5, -0.5, 0.5, -0.5 ], - [-0.5, -0.5, -0.5, 0.5 ], - [-0.5, 0.5, -0.5, -0.5 ], - [-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], - [ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], - [-0.5*np.sqrt(2), 0.0, 0.5*np.sqrt(2), 0.0 ], - [-0.5*np.sqrt(2), 0.0, -0.5*np.sqrt(2), 0.0 ], - [-0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0, 0.0 ], - [-0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0, 0.0 ], - ] - elif self.family == 'hexagonal': - sym_quats = [ - [ 1.0, 0.0, 0.0, 0.0 ], - [-0.5*np.sqrt(3), 0.0, 0.0, -0.5 ], - [ 0.5, 0.0, 0.0, 0.5*np.sqrt(3) ], - [ 0.0, 0.0, 0.0, 1.0 ], - [-0.5, 0.0, 0.0, 0.5*np.sqrt(3) ], - [-0.5*np.sqrt(3), 0.0, 0.0, 0.5 ], - [ 0.0, 1.0, 0.0, 0.0 ], - [ 0.0, -0.5*np.sqrt(3), 0.5, 0.0 ], - [ 0.0, 0.5, -0.5*np.sqrt(3), 0.0 ], - [ 0.0, 0.0, 1.0, 0.0 ], - [ 0.0, -0.5, -0.5*np.sqrt(3), 0.0 ], - [ 0.0, 0.5*np.sqrt(3), 0.5, 0.0 ], - ] - elif self.family == 'tetragonal': - sym_quats = [ - [ 1.0, 0.0, 0.0, 0.0 ], - [ 0.0, 1.0, 0.0, 0.0 ], - [ 0.0, 0.0, 1.0, 0.0 ], - [ 0.0, 0.0, 0.0, 1.0 ], - [ 0.0, 0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0 ], - [ 0.0, -0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0 ], - [ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], - [-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], - ] - elif self.family == 'orthorhombic': - sym_quats = [ - [ 1.0,0.0,0.0,0.0 ], - [ 0.0,1.0,0.0,0.0 ], - [ 0.0,0.0,1.0,0.0 ], - [ 0.0,0.0,0.0,1.0 ], - ] - elif self.family == 'monoclinic': - sym_quats = [ - [ 1.0,0.0,0.0,0.0 ], - [ 0.0,0.0,1.0,0.0 ], - ] - elif self.family == 'triclinic': - sym_quats = [ - [ 1.0,0.0,0.0,0.0 ], - ] - else: - raise KeyError(f'unknown crystal family "{self.family}"') - - return Rotation.from_quaternion(sym_quats,accept_homomorph=True) - - @property def equivalent(self): """ @@ -707,46 +640,6 @@ class Orientation(Rotation): return (self.a,self.b,self.c,self.alpha,self.beta,self.gamma) - @property - def immutable(self): - """Return immutable parameters of own lattice.""" - if self.family == 'triclinic': - return {} - if self.family == 'monoclinic': - return { - 'alpha': np.pi/2., - 'gamma': np.pi/2., - } - if self.family == 'orthorhombic': - return { - 'alpha': np.pi/2., - 'beta': np.pi/2., - 'gamma': np.pi/2., - } - if self.family == 'tetragonal': - return { - 'b': 1.0, - 'alpha': np.pi/2., - 'beta': np.pi/2., - 'gamma': np.pi/2., - } - if self.family == 'hexagonal': - return { - 'b': 1.0, - 'alpha': np.pi/2., - 'beta': np.pi/2., - 'gamma': 2.*np.pi/3., - } - if self.family == 'cubic': - return { - 'b': 1.0, - 'c': 1.0, - 'alpha': np.pi/2., - 'beta': np.pi/2., - 'gamma': np.pi/2., - } - - @property def ratio(self): """Return axes ratios of own lattice.""" @@ -808,76 +701,25 @@ class Orientation(Rotation): in : numpy.ndarray of shape (...) Boolean array indicating whether vector falls into SST. - References - ---------- - Bases are computed from - - >>> basis = { - ... 'cubic' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,1.]/np.sqrt(2.), # green - ... [1.,1.,1.]/np.sqrt(3.)]).T), # blue - ... 'hexagonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,0.], # green - ... [np.sqrt(3.),1.,0.]/np.sqrt(4.)]).T), # blue - ... 'tetragonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,0.], # green - ... [1.,1.,0.]/np.sqrt(2.)]).T), # blue - ... 'orthorhombic': np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,0.], # green - ... [0.,1.,0.]]).T), # blue - ... } - """ if not isinstance(vector,np.ndarray) or vector.shape[-1] != 3: raise ValueError('input is not a field of three-dimensional vectors') - if self.family == 'cubic': - basis = {'improper':np.array([ [-1. , 0. , 1. ], - [ np.sqrt(2.) , -np.sqrt(2.) , 0. ], - [ 0. , np.sqrt(3.) , 0. ] ]), - 'proper':np.array([ [ 0. , -1. , 1. ], - [-np.sqrt(2.) , np.sqrt(2.) , 0. ], - [ np.sqrt(3.) , 0. , 0. ] ]), - } - elif self.family == 'hexagonal': - basis = {'improper':np.array([ [ 0. , 0. , 1. ], - [ 1. , -np.sqrt(3.) , 0. ], - [ 0. , 2. , 0. ] ]), - 'proper':np.array([ [ 0. , 0. , 1. ], - [-1. , np.sqrt(3.) , 0. ], - [ np.sqrt(3.) , -1. , 0. ] ]), - } - elif self.family == 'tetragonal': - basis = {'improper':np.array([ [ 0. , 0. , 1. ], - [ 1. , -1. , 0. ], - [ 0. , np.sqrt(2.) , 0. ] ]), - 'proper':np.array([ [ 0. , 0. , 1. ], - [-1. , 1. , 0. ], - [ np.sqrt(2.) , 0. , 0. ] ]), - } - elif self.family == 'orthorhombic': - basis = {'improper':np.array([ [ 0., 0., 1.], - [ 1., 0., 0.], - [ 0., 1., 0.] ]), - 'proper':np.array([ [ 0., 0., 1.], - [-1., 0., 0.], - [ 0., 1., 0.] ]), - } - else: # direct exit for unspecified symmetry + if self.basis is None: # direct exit for no symmetry return np.ones_like(vector[...,0],bool) if proper: components_proper = np.around(np.einsum('...ji,...i', - np.broadcast_to(basis['proper'], vector.shape+(3,)), + np.broadcast_to(self.basis['proper'], vector.shape+(3,)), vector), 12) components_improper = np.around(np.einsum('...ji,...i', - np.broadcast_to(basis['improper'], vector.shape+(3,)), + np.broadcast_to(self.basis['improper'], vector.shape+(3,)), vector), 12) return np.all(components_proper >= 0.0,axis=-1) \ | np.all(components_improper >= 0.0,axis=-1) else: components = np.around(np.einsum('...ji,...i', - np.broadcast_to(basis['improper'], vector.shape+(3,)), + np.broadcast_to(self.basis['improper'], vector.shape+(3,)), np.block([vector[...,:2],np.abs(vector[...,2:3])])), 12) return np.all(components >= 0.0,axis=-1) @@ -911,25 +753,6 @@ class Orientation(Rotation): >>> o.IPF_color([0,0,1]) array([1., 0., 0.]) - References - ---------- - Bases are computed from - - >>> basis = { - ... 'cubic' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,1.]/np.sqrt(2.), # green - ... [1.,1.,1.]/np.sqrt(3.)]).T), # blue - ... 'hexagonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,0.], # green - ... [np.sqrt(3.),1.,0.]/np.sqrt(4.)]).T), # blue - ... 'tetragonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,0.], # green - ... [1.,1.,0.]/np.sqrt(2.)]).T), # blue - ... 'orthorhombic': np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,0.], # green - ... [0.,1.,0.]]).T), # blue - ... } - """ if np.array(vector).shape[-1] != 3: raise ValueError('input is not a field of three-dimensional vectors') @@ -937,47 +760,15 @@ class Orientation(Rotation): vector_ = self.to_SST(vector,proper) if in_SST else \ self @ np.broadcast_to(vector,self.shape+(3,)) - if self.family == 'cubic': - basis = {'improper':np.array([ [-1. , 0. , 1. ], - [ np.sqrt(2.) , -np.sqrt(2.) , 0. ], - [ 0. , np.sqrt(3.) , 0. ] ]), - 'proper':np.array([ [ 0. , -1. , 1. ], - [-np.sqrt(2.) , np.sqrt(2.) , 0. ], - [ np.sqrt(3.) , 0. , 0. ] ]), - } - elif self.family == 'hexagonal': - basis = {'improper':np.array([ [ 0. , 0. , 1. ], - [ 1. , -np.sqrt(3.) , 0. ], - [ 0. , 2. , 0. ] ]), - 'proper':np.array([ [ 0. , 0. , 1. ], - [-1. , np.sqrt(3.) , 0. ], - [ np.sqrt(3.) , -1. , 0. ] ]), - } - elif self.family == 'tetragonal': - basis = {'improper':np.array([ [ 0. , 0. , 1. ], - [ 1. , -1. , 0. ], - [ 0. , np.sqrt(2.) , 0. ] ]), - 'proper':np.array([ [ 0. , 0. , 1. ], - [-1. , 1. , 0. ], - [ np.sqrt(2.) , 0. , 0. ] ]), - } - elif self.family == 'orthorhombic': - basis = {'improper':np.array([ [ 0., 0., 1.], - [ 1., 0., 0.], - [ 0., 1., 0.] ]), - 'proper':np.array([ [ 0., 0., 1.], - [-1., 0., 0.], - [ 0., 1., 0.] ]), - } - else: # direct exit for unspecified symmetry + if self.basis is None: # direct exit for no symmetry return np.zeros_like(vector_) if proper: components_proper = np.around(np.einsum('...ji,...i', - np.broadcast_to(basis['proper'], vector_.shape+(3,)), + np.broadcast_to(self.basis['proper'], vector_.shape+(3,)), vector_), 12) components_improper = np.around(np.einsum('...ji,...i', - np.broadcast_to(basis['improper'], vector_.shape+(3,)), + np.broadcast_to(self.basis['improper'], vector_.shape+(3,)), vector_), 12) in_SST = np.all(components_proper >= 0.0,axis=-1) \ | np.all(components_improper >= 0.0,axis=-1) @@ -985,16 +776,17 @@ class Orientation(Rotation): components_proper,components_improper) else: components = np.around(np.einsum('...ji,...i', - np.broadcast_to(basis['improper'], vector_.shape+(3,)), + np.broadcast_to(self.basis['improper'], vector_.shape+(3,)), np.block([vector_[...,:2],np.abs(vector_[...,2:3])])), 12) in_SST = np.all(components >= 0.0,axis=-1) with np.errstate(invalid='ignore',divide='ignore'): - rgb = (components/np.linalg.norm(components,axis=-1,keepdims=True))**0.5 # smoothen color ramps - rgb = np.clip(rgb,0.,1.) # clip intensity - rgb /= np.max(rgb,axis=-1,keepdims=True) # normalize to (HS)V = 1 + rgb = (components/np.linalg.norm(components,axis=-1,keepdims=True))**0.5 # smoothen color ramps + rgb = np.clip(rgb,0.,1.) # clip intensity + rgb /= np.max(rgb,axis=-1,keepdims=True) # normalize to (HS)V = 1 rgb[np.broadcast_to(~in_SST[...,np.newaxis],rgb.shape)] = 0.0 + return rgb diff --git a/python/damask/lattice.py b/python/damask/lattice.py index e335fc85b..5b4621f3d 100644 --- a/python/damask/lattice.py +++ b/python/damask/lattice.py @@ -1,62 +1,5 @@ import numpy as _np - -def Bravais_to_Miller(*,uvtw=None,hkil=None): - """ - Transform 4 Miller–Bravais indices to 3 Miller indices of crystal direction [uvw] or plane normal (hkl). - - Parameters - ---------- - uvtw|hkil : numpy.ndarray of shape (...,4) - Miller–Bravais indices of crystallographic direction [uvtw] or plane normal (hkil). - - Returns - ------- - uvw|hkl : numpy.ndarray of shape (...,3) - Miller indices of [uvw] direction or (hkl) plane normal. - - """ - if (uvtw is not None) ^ (hkil is None): - raise KeyError('Specify either "uvtw" or "hkil"') - axis,basis = (_np.array(uvtw),_np.array([[1,0,-1,0], - [0,1,-1,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]])) - return _np.einsum('il,...l',basis,axis) - - -def Miller_to_Bravais(*,uvw=None,hkl=None): - """ - Transform 3 Miller indices to 4 Miller–Bravais indices of crystal direction [uvtw] or plane normal (hkil). - - Parameters - ---------- - uvw|hkl : numpy.ndarray of shape (...,3) - Miller indices of crystallographic direction [uvw] or plane normal (hkl). - - Returns - ------- - uvtw|hkil : numpy.ndarray of shape (...,4) - Miller–Bravais indices of [uvtw] direction or (hkil) plane normal. - - """ - if (uvw is not None) ^ (hkl is None): - raise KeyError('Specify either "uvw" or "hkl"') - axis,basis = (_np.array(uvw),_np.array([[ 2,-1, 0], - [-1, 2, 0], - [-1,-1, 0], - [ 0, 0, 3]])/3) \ - if hkl is None else \ - (_np.array(hkl),_np.array([[ 1, 0, 0], - [ 0, 1, 0], - [-1,-1, 0], - [ 0, 0, 1]])) - return _np.einsum('il,...l',basis,axis) - - kinematics = { 'cF': { 'slip' : _np.array([ diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index 7fc20a3e6..471dada58 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -336,17 +336,10 @@ class TestOrientation: for r,v in zip(result,vecs.reshape((-1,3))): assert np.all(r == Orientation(family=family).in_SST(v,proper)) - @pytest.mark.parametrize('invalid_family',['fcc','bcc','hello']) + @pytest.mark.parametrize('invalid_family',[None,'fcc','bcc','hello']) def test_invalid_lattice_init(self,invalid_family): with pytest.raises(KeyError): - Orientation(family=invalid_family) # noqa - - @pytest.mark.parametrize('invalid_family',[None,'fcc','bcc','hello']) - def test_invalid_symmetry_family(self,invalid_family): - with pytest.raises(KeyError): - o = Orientation(family='cubic') - o.family = invalid_family - o.symmetry_operations # noqa + Orientation(family=invalid_family) def test_invalid_rot(self): with pytest.raises(TypeError): From 302da1f76a43caa09c273db125d4a4fd93b5c27f Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 2 Jun 2021 09:28:27 +0200 Subject: [PATCH 04/43] separating functionality --- python/damask/__init__.py | 2 +- python/damask/_lattice.py | 651 +++++++++++++++++++++++++++++++ python/damask/_lattice_family.py | 29 +- python/damask/_orientation.py | 169 ++------ python/damask/lattice.py | 444 --------------------- python/tests/test_Lattice.py | 64 +++ python/tests/test_Orientation.py | 70 ---- 7 files changed, 755 insertions(+), 674 deletions(-) create mode 100644 python/damask/_lattice.py delete mode 100644 python/damask/lattice.py create mode 100644 python/tests/test_Lattice.py diff --git a/python/damask/__init__.py b/python/damask/__init__.py index 5cdfbe899..eb333487d 100644 --- a/python/damask/__init__.py +++ b/python/damask/__init__.py @@ -14,11 +14,11 @@ from . import tensor # noqa from . import mechanics # noqa from . import solver # noqa from . import grid_filters # noqa -from . import lattice # noqa #Modules that contain only one class (of the same name), are prefixed by a '_'. #For example, '_colormap' containsa class called 'Colormap' which is imported as 'damask.Colormap'. from ._rotation import Rotation # noqa from ._lattice_family import LatticeFamily # noqa +from ._lattice import Lattice # noqa from ._orientation import Orientation # noqa from ._table import Table # noqa from ._vtk import VTK # noqa diff --git a/python/damask/_lattice.py b/python/damask/_lattice.py new file mode 100644 index 000000000..c9303968c --- /dev/null +++ b/python/damask/_lattice.py @@ -0,0 +1,651 @@ +import numpy as np + +from . import util +from . import LatticeFamily + +lattice_symmetries = { + 'aP': 'triclinic', + + 'mP': 'monoclinic', + 'mS': 'monoclinic', + + 'oP': 'orthorhombic', + 'oS': 'orthorhombic', + 'oI': 'orthorhombic', + 'oF': 'orthorhombic', + + 'tP': 'tetragonal', + 'tI': 'tetragonal', + + 'hP': 'hexagonal', + + 'cP': 'cubic', + 'cI': 'cubic', + 'cF': 'cubic', + } + + +class Lattice(LatticeFamily): + """Lattice.""" + + def __init__(self, + lattice = None, + a = None,b = None,c = None, + alpha = None,beta = None,gamma = None, + degrees = False): + """ + Lattice. + + Parameters + ---------- + lattice : {'aP', 'mP', 'mS', 'oP', 'oS', 'oI', 'oF', 'tP', 'tI', 'hP', 'cP', 'cI', 'cF'}. + Name of the Bravais lattice in Pearson notation. + a : float, optional + Length of lattice parameter 'a'. + b : float, optional + Length of lattice parameter 'b'. + c : float, optional + Length of lattice parameter 'c'. + alpha : float, optional + Angle between b and c lattice basis. + beta : float, optional + Angle between c and a lattice basis. + gamma : float, optional + Angle between a and b lattice basis. + degrees : bool, optional + Angles are given in degrees. Defaults to False. + + """ + super().__init__(lattice_symmetries[lattice]) + self.lattice = lattice + + + self.a = 1 if a is None else a + self.b = b + self.c = c + self.a = float(self.a) if self.a is not None else \ + (self.b / self.ratio['b'] if self.b is not None and self.ratio['b'] is not None else + self.c / self.ratio['c'] if self.c is not None and self.ratio['c'] is not None else None) + self.b = float(self.b) if self.b is not None else \ + (self.a * self.ratio['b'] if self.a is not None and self.ratio['b'] is not None else + self.c / self.ratio['c'] * self.ratio['b'] + if self.c is not None and self.ratio['b'] is not None and self.ratio['c'] is not None else None) + self.c = float(self.c) if self.c is not None else \ + (self.a * self.ratio['c'] if self.a is not None and self.ratio['c'] is not None else + self.b / self.ratio['b'] * self.ratio['c'] + if self.c is not None and self.ratio['b'] is not None and self.ratio['c'] is not None else None) + + self.alpha = np.radians(alpha) if degrees and alpha is not None else alpha + self.beta = np.radians(beta) if degrees and beta is not None else beta + self.gamma = np.radians(gamma) if degrees and gamma is not None else gamma + if self.alpha is None and 'alpha' in self.immutable: self.alpha = self.immutable['alpha'] + if self.beta is None and 'beta' in self.immutable: self.beta = self.immutable['beta'] + if self.gamma is None and 'gamma' in self.immutable: self.gamma = self.immutable['gamma'] + + if \ + (self.a is None) \ + or (self.b is None or ('b' in self.immutable and self.b != self.immutable['b'] * self.a)) \ + or (self.c is None or ('c' in self.immutable and self.c != self.immutable['c'] * self.b)) \ + or (self.alpha is None or ('alpha' in self.immutable and self.alpha != self.immutable['alpha'])) \ + or (self.beta is None or ( 'beta' in self.immutable and self.beta != self.immutable['beta'])) \ + or (self.gamma is None or ('gamma' in self.immutable and self.gamma != self.immutable['gamma'])): + raise ValueError (f'Incompatible parameters {self.parameters} for crystal family {self.family}') + + if np.any(np.array([self.alpha,self.beta,self.gamma]) <= 0): + raise ValueError ('Lattice angles must be positive') + if np.any([np.roll([self.alpha,self.beta,self.gamma],r)[0] + > np.sum(np.roll([self.alpha,self.beta,self.gamma],r)[1:]) for r in range(3)]): + raise ValueError ('Each lattice angle must be less than sum of others') + + + @property + def parameters(self): + """Return lattice parameters a, b, c, alpha, beta, gamma.""" + return (self.a,self.b,self.c,self.alpha,self.beta,self.gamma) + + + @property + def ratio(self): + """Return axes ratios of own lattice.""" + _ratio = { 'hexagonal': {'c': np.sqrt(8./3.)}} + + return dict(b = self.immutable['b'] + if 'b' in self.immutable else + _ratio[self.family]['b'] if self.family in _ratio and 'b' in _ratio[self.family] else None, + c = self.immutable['c'] + if 'c' in self.immutable else + _ratio[self.family]['c'] if self.family in _ratio and 'c' in _ratio[self.family] else None, + ) + + + @property + def basis_real(self): + """ + Calculate orthogonal real space crystal basis. + + References + ---------- + C.T. Young and J.L. Lytton, Journal of Applied Physics 43:1408–1417, 1972 + https://doi.org/10.1063/1.1661333 + + """ + if None in self.parameters: + raise KeyError('missing crystal lattice parameters') + return np.array([ + [1,0,0], + [np.cos(self.gamma),np.sin(self.gamma),0], + [np.cos(self.beta), + (np.cos(self.alpha)-np.cos(self.beta)*np.cos(self.gamma)) /np.sin(self.gamma), + np.sqrt(1 - np.cos(self.alpha)**2 - np.cos(self.beta)**2 - np.cos(self.gamma)**2 + + 2 * np.cos(self.alpha) * np.cos(self.beta) * np.cos(self.gamma))/np.sin(self.gamma)], + ],dtype=float).T \ + * np.array([self.a,self.b,self.c]) + + + @property + def basis_reciprocal(self): + """Calculate reciprocal (dual) crystal basis.""" + return np.linalg.inv(self.basis_real.T) + + + def to_lattice(self,*,direction=None,plane=None): + """ + Calculate lattice vector corresponding to crystal frame direction or plane normal. + + Parameters + ---------- + direction|normal : numpy.ndarray of shape (...,3) + Vector along direction or plane normal. + + Returns + ------- + Miller : numpy.ndarray of shape (...,3) + lattice vector of direction or plane. + Use util.scale_to_coprime to convert to (integer) Miller indices. + + """ + if (direction is not None) ^ (plane is None): + raise KeyError('Specify either "direction" or "plane"') + axis,basis = (np.array(direction),self.basis_reciprocal.T) \ + if plane is None else \ + (np.array(plane),self.basis_real.T) + return np.einsum('il,...l',basis,axis) + + + def to_frame(self,*,uvw=None,hkl=None): + """ + Calculate crystal frame vector along lattice direction [uvw] or plane normal (hkl). + + Parameters + ---------- + uvw|hkl : numpy.ndarray of shape (...,3) + Miller indices of crystallographic direction or plane normal. + + Returns + ------- + vector : numpy.ndarray of shape (...,3) or (N,...,3) + Crystal frame vector (or vectors if with_symmetry) along [uvw] direction or (hkl) plane normal. + + """ + if (uvw is not None) ^ (hkl is None): + raise KeyError('Specify either "uvw" or "hkl"') + axis,basis = (np.array(uvw),self.basis_real) \ + if hkl is None else \ + (np.array(hkl),self.basis_reciprocal) + return np.einsum('il,...l',basis,axis) + + + def kinematics(self,mode): + master = self._kinematics[self.lattice][mode] + if self.lattice == 'hP': + return {'direction':util.Bravais_to_Miller(uvtw=master[:,0:4]), + 'plane': util.Bravais_to_Miller(hkil=master[:,4:8])} + else: + return {'direction':master[:,0:3], + 'plane': master[:,3:6]} + + + @property + def orientation_relationships(self): + return {k:v for k,v in self._orientation_relationships.items() if self.lattice in v} + + + _kinematics = { + 'cF': { + 'slip' : np.array([ + [+0,+1,-1, +1,+1,+1], + [-1,+0,+1, +1,+1,+1], + [+1,-1,+0, +1,+1,+1], + [+0,-1,-1, -1,-1,+1], + [+1,+0,+1, -1,-1,+1], + [-1,+1,+0, -1,-1,+1], + [+0,-1,+1, +1,-1,-1], + [-1,+0,-1, +1,-1,-1], + [+1,+1,+0, +1,-1,-1], + [+0,+1,+1, -1,+1,-1], + [+1,+0,-1, -1,+1,-1], + [-1,-1,+0, -1,+1,-1], + [+1,+1,+0, +1,-1,+0], + [+1,-1,+0, +1,+1,+0], + [+1,+0,+1, +1,+0,-1], + [+1,+0,-1, +1,+0,+1], + [+0,+1,+1, +0,+1,-1], + [+0,+1,-1, +0,+1,+1], + ],'d'), + 'twin' : np.array([ + [-2, 1, 1, 1, 1, 1], + [ 1,-2, 1, 1, 1, 1], + [ 1, 1,-2, 1, 1, 1], + [ 2,-1, 1, -1,-1, 1], + [-1, 2, 1, -1,-1, 1], + [-1,-1,-2, -1,-1, 1], + [-2,-1,-1, 1,-1,-1], + [ 1, 2,-1, 1,-1,-1], + [ 1,-1, 2, 1,-1,-1], + [ 2, 1,-1, -1, 1,-1], + [-1,-2,-1, -1, 1,-1], + [-1, 1, 2, -1, 1,-1], + ],dtype=float), + }, + 'cI': { + 'slip' : np.array([ + [+1,-1,+1, +0,+1,+1], + [-1,-1,+1, +0,+1,+1], + [+1,+1,+1, +0,-1,+1], + [-1,+1,+1, +0,-1,+1], + [-1,+1,+1, +1,+0,+1], + [-1,-1,+1, +1,+0,+1], + [+1,+1,+1, -1,+0,+1], + [+1,-1,+1, -1,+0,+1], + [-1,+1,+1, +1,+1,+0], + [-1,+1,-1, +1,+1,+0], + [+1,+1,+1, -1,+1,+0], + [+1,+1,-1, -1,+1,+0], + [-1,+1,+1, +2,+1,+1], + [+1,+1,+1, -2,+1,+1], + [+1,+1,-1, +2,-1,+1], + [+1,-1,+1, +2,+1,-1], + [+1,-1,+1, +1,+2,+1], + [+1,+1,-1, -1,+2,+1], + [+1,+1,+1, +1,-2,+1], + [-1,+1,+1, +1,+2,-1], + [+1,+1,-1, +1,+1,+2], + [+1,-1,+1, -1,+1,+2], + [-1,+1,+1, +1,-1,+2], + [+1,+1,+1, +1,+1,-2], + [+1,+1,-1, +1,+2,+3], + [+1,-1,+1, -1,+2,+3], + [-1,+1,+1, +1,-2,+3], + [+1,+1,+1, +1,+2,-3], + [+1,-1,+1, +1,+3,+2], + [+1,+1,-1, -1,+3,+2], + [+1,+1,+1, +1,-3,+2], + [-1,+1,+1, +1,+3,-2], + [+1,+1,-1, +2,+1,+3], + [+1,-1,+1, -2,+1,+3], + [-1,+1,+1, +2,-1,+3], + [+1,+1,+1, +2,+1,-3], + [+1,-1,+1, +2,+3,+1], + [+1,+1,-1, -2,+3,+1], + [+1,+1,+1, +2,-3,+1], + [-1,+1,+1, +2,+3,-1], + [-1,+1,+1, +3,+1,+2], + [+1,+1,+1, -3,+1,+2], + [+1,+1,-1, +3,-1,+2], + [+1,-1,+1, +3,+1,-2], + [-1,+1,+1, +3,+2,+1], + [+1,+1,+1, -3,+2,+1], + [+1,+1,-1, +3,-2,+1], + [+1,-1,+1, +3,+2,-1], + ],'d'), + 'twin' : np.array([ + [-1, 1, 1, 2, 1, 1], + [ 1, 1, 1, -2, 1, 1], + [ 1, 1,-1, 2,-1, 1], + [ 1,-1, 1, 2, 1,-1], + [ 1,-1, 1, 1, 2, 1], + [ 1, 1,-1, -1, 2, 1], + [ 1, 1, 1, 1,-2, 1], + [-1, 1, 1, 1, 2,-1], + [ 1, 1,-1, 1, 1, 2], + [ 1,-1, 1, -1, 1, 2], + [-1, 1, 1, 1,-1, 2], + [ 1, 1, 1, 1, 1,-2], + ],dtype=float), + }, + 'hP': { + 'slip' : np.array([ + [+2,-1,-1,+0, +0,+0,+0,+1], + [-1,+2,-1,+0, +0,+0,+0,+1], + [-1,-1,+2,+0, +0,+0,+0,+1], + [+2,-1,-1,+0, +0,+1,-1,+0], + [-1,+2,-1,+0, -1,+0,+1,+0], + [-1,-1,+2,+0, +1,-1,+0,+0], + [-1,+1,+0,+0, +1,+1,-2,+0], + [+0,-1,+1,+0, -2,+1,+1,+0], + [+1,+0,-1,+0, +1,-2,+1,+0], + [-1,+2,-1,+0, +1,+0,-1,+1], + [-2,+1,+1,+0, +0,+1,-1,+1], + [-1,-1,+2,+0, -1,+1,+0,+1], + [+1,-2,+1,+0, -1,+0,+1,+1], + [+2,-1,-1,+0, +0,-1,+1,+1], + [+1,+1,-2,+0, +1,-1,+0,+1], + [-2,+1,+1,+3, +1,+0,-1,+1], + [-1,-1,+2,+3, +1,+0,-1,+1], + [-1,-1,+2,+3, +0,+1,-1,+1], + [+1,-2,+1,+3, +0,+1,-1,+1], + [+1,-2,+1,+3, -1,+1,+0,+1], + [+2,-1,-1,+3, -1,+1,+0,+1], + [+2,-1,-1,+3, -1,+0,+1,+1], + [+1,+1,-2,+3, -1,+0,+1,+1], + [+1,+1,-2,+3, +0,-1,+1,+1], + [-1,+2,-1,+3, +0,-1,+1,+1], + [-1,+2,-1,+3, +1,-1,+0,+1], + [-2,+1,+1,+3, +1,-1,+0,+1], + [-1,-1,+2,+3, +1,+1,-2,+2], + [+1,-2,+1,+3, -1,+2,-1,+2], + [+2,-1,-1,+3, -2,+1,+1,+2], + [+1,+1,-2,+3, -1,-1,+2,+2], + [-1,+2,-1,+3, +1,-2,+1,+2], + [-2,+1,+1,+3, +2,-1,-1,+2], + ],'d'), + 'twin' : np.array([ + [-1, 0, 1, 1, 1, 0,-1, 2], # shear = (3-(c/a)^2)/(sqrt(3) c/a) <-10.1>{10.2} + [ 0,-1, 1, 1, 0, 1,-1, 2], + [ 1,-1, 0, 1, -1, 1, 0, 2], + [ 1, 0,-1, 1, -1, 0, 1, 2], + [ 0, 1,-1, 1, 0,-1, 1, 2], + [-1, 1, 0, 1, 1,-1, 0, 2], + [-1,-1, 2, 6, 1, 1,-2, 1], # shear = 1/(c/a) <11.6>{-1-1.1} + [ 1,-2, 1, 6, -1, 2,-1, 1], + [ 2,-1,-1, 6, -2, 1, 1, 1], + [ 1, 1,-2, 6, -1,-1, 2, 1], + [-1, 2,-1, 6, 1,-2, 1, 1], + [-2, 1, 1, 6, 2,-1,-1, 1], + [ 1, 0,-1,-2, 1, 0,-1, 1], # shear = (4(c/a)^2-9)/(4 sqrt(3) c/a) <10.-2>{10.1} + [ 0, 1,-1,-2, 0, 1,-1, 1], + [-1, 1, 0,-2, -1, 1, 0, 1], + [-1, 0, 1,-2, -1, 0, 1, 1], + [ 0,-1, 1,-2, 0,-1, 1, 1], + [ 1,-1, 0,-2, 1,-1, 0, 1], + [ 1, 1,-2,-3, 1, 1,-2, 2], # shear = 2((c/a)^2-2)/(3 c/a) <11.-3>{11.2} + [-1, 2,-1,-3, -1, 2,-1, 2], + [-2, 1, 1,-3, -2, 1, 1, 2], + [-1,-1, 2,-3, -1,-1, 2, 2], + [ 1,-2, 1,-3, 1,-2, 1, 2], + [ 2,-1,-1,-3, 2,-1,-1, 2], + ],dtype=float), + }, + } + + + _orientation_relationships = { + 'KS': { + 'cF' : np.array([ + [[-1, 0, 1],[ 1, 1, 1]], + [[-1, 0, 1],[ 1, 1, 1]], + [[ 0, 1,-1],[ 1, 1, 1]], + [[ 0, 1,-1],[ 1, 1, 1]], + [[ 1,-1, 0],[ 1, 1, 1]], + [[ 1,-1, 0],[ 1, 1, 1]], + [[ 1, 0,-1],[ 1,-1, 1]], + [[ 1, 0,-1],[ 1,-1, 1]], + [[-1,-1, 0],[ 1,-1, 1]], + [[-1,-1, 0],[ 1,-1, 1]], + [[ 0, 1, 1],[ 1,-1, 1]], + [[ 0, 1, 1],[ 1,-1, 1]], + [[ 0,-1, 1],[-1, 1, 1]], + [[ 0,-1, 1],[-1, 1, 1]], + [[-1, 0,-1],[-1, 1, 1]], + [[-1, 0,-1],[-1, 1, 1]], + [[ 1, 1, 0],[-1, 1, 1]], + [[ 1, 1, 0],[-1, 1, 1]], + [[-1, 1, 0],[ 1, 1,-1]], + [[-1, 1, 0],[ 1, 1,-1]], + [[ 0,-1,-1],[ 1, 1,-1]], + [[ 0,-1,-1],[ 1, 1,-1]], + [[ 1, 0, 1],[ 1, 1,-1]], + [[ 1, 0, 1],[ 1, 1,-1]], + ],dtype=float), + 'cI' : np.array([ + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + ],dtype=float), + }, + 'GT': { + 'cF' : np.array([ + [[ -5,-12, 17],[ 1, 1, 1]], + [[ 17, -5,-12],[ 1, 1, 1]], + [[-12, 17, -5],[ 1, 1, 1]], + [[ 5, 12, 17],[ -1, -1, 1]], + [[-17, 5,-12],[ -1, -1, 1]], + [[ 12,-17, -5],[ -1, -1, 1]], + [[ -5, 12,-17],[ -1, 1, 1]], + [[ 17, 5, 12],[ -1, 1, 1]], + [[-12,-17, 5],[ -1, 1, 1]], + [[ 5,-12,-17],[ 1, -1, 1]], + [[-17, -5, 12],[ 1, -1, 1]], + [[ 12, 17, 5],[ 1, -1, 1]], + [[ -5, 17,-12],[ 1, 1, 1]], + [[-12, -5, 17],[ 1, 1, 1]], + [[ 17,-12, -5],[ 1, 1, 1]], + [[ 5,-17,-12],[ -1, -1, 1]], + [[ 12, 5, 17],[ -1, -1, 1]], + [[-17, 12, -5],[ -1, -1, 1]], + [[ -5,-17, 12],[ -1, 1, 1]], + [[-12, 5,-17],[ -1, 1, 1]], + [[ 17, 12, 5],[ -1, 1, 1]], + [[ 5, 17, 12],[ 1, -1, 1]], + [[ 12, -5,-17],[ 1, -1, 1]], + [[-17,-12, 5],[ 1, -1, 1]], + ],dtype=float), + 'cI' : np.array([ + [[-17, -7, 17],[ 1, 0, 1]], + [[ 17,-17, -7],[ 1, 1, 0]], + [[ -7, 17,-17],[ 0, 1, 1]], + [[ 17, 7, 17],[ -1, 0, 1]], + [[-17, 17, -7],[ -1, -1, 0]], + [[ 7,-17,-17],[ 0, -1, 1]], + [[-17, 7,-17],[ -1, 0, 1]], + [[ 17, 17, 7],[ -1, 1, 0]], + [[ -7,-17, 17],[ 0, 1, 1]], + [[ 17, -7,-17],[ 1, 0, 1]], + [[-17,-17, 7],[ 1, -1, 0]], + [[ 7, 17, 17],[ 0, -1, 1]], + [[-17, 17, -7],[ 1, 1, 0]], + [[ -7,-17, 17],[ 0, 1, 1]], + [[ 17, -7,-17],[ 1, 0, 1]], + [[ 17,-17, -7],[ -1, -1, 0]], + [[ 7, 17, 17],[ 0, -1, 1]], + [[-17, 7,-17],[ -1, 0, 1]], + [[-17,-17, 7],[ -1, 1, 0]], + [[ -7, 17,-17],[ 0, 1, 1]], + [[ 17, 7, 17],[ -1, 0, 1]], + [[ 17, 17, 7],[ 1, -1, 0]], + [[ 7,-17,-17],[ 0, -1, 1]], + [[-17, -7, 17],[ 1, 0, 1]], + ],dtype=float), + }, + 'GT_prime': { + 'cF' : np.array([ + [[ 0, 1, -1],[ 7, 17, 17]], + [[ -1, 0, 1],[ 17, 7, 17]], + [[ 1, -1, 0],[ 17, 17, 7]], + [[ 0, -1, -1],[ -7,-17, 17]], + [[ 1, 0, 1],[-17, -7, 17]], + [[ 1, -1, 0],[-17,-17, 7]], + [[ 0, 1, -1],[ 7,-17,-17]], + [[ 1, 0, 1],[ 17, -7,-17]], + [[ -1, -1, 0],[ 17,-17, -7]], + [[ 0, -1, -1],[ -7, 17,-17]], + [[ -1, 0, 1],[-17, 7,-17]], + [[ -1, -1, 0],[-17, 17, -7]], + [[ 0, -1, 1],[ 7, 17, 17]], + [[ 1, 0, -1],[ 17, 7, 17]], + [[ -1, 1, 0],[ 17, 17, 7]], + [[ 0, 1, 1],[ -7,-17, 17]], + [[ -1, 0, -1],[-17, -7, 17]], + [[ -1, 1, 0],[-17,-17, 7]], + [[ 0, -1, 1],[ 7,-17,-17]], + [[ -1, 0, -1],[ 17, -7,-17]], + [[ 1, 1, 0],[ 17,-17, -7]], + [[ 0, 1, 1],[ -7, 17,-17]], + [[ 1, 0, -1],[-17, 7,-17]], + [[ 1, 1, 0],[-17, 17, -7]], + ],dtype=float), + 'cI' : np.array([ + [[ 1, 1, -1],[ 12, 5, 17]], + [[ -1, 1, 1],[ 17, 12, 5]], + [[ 1, -1, 1],[ 5, 17, 12]], + [[ -1, -1, -1],[-12, -5, 17]], + [[ 1, -1, 1],[-17,-12, 5]], + [[ 1, -1, -1],[ -5,-17, 12]], + [[ -1, 1, -1],[ 12, -5,-17]], + [[ 1, 1, 1],[ 17,-12, -5]], + [[ -1, -1, 1],[ 5,-17,-12]], + [[ 1, -1, -1],[-12, 5,-17]], + [[ -1, -1, 1],[-17, 12, -5]], + [[ -1, -1, -1],[ -5, 17,-12]], + [[ 1, -1, 1],[ 12, 17, 5]], + [[ 1, 1, -1],[ 5, 12, 17]], + [[ -1, 1, 1],[ 17, 5, 12]], + [[ -1, 1, 1],[-12,-17, 5]], + [[ -1, -1, -1],[ -5,-12, 17]], + [[ -1, 1, -1],[-17, -5, 12]], + [[ -1, -1, 1],[ 12,-17, -5]], + [[ -1, 1, -1],[ 5,-12,-17]], + [[ 1, 1, 1],[ 17, -5,-12]], + [[ 1, 1, 1],[-12, 17, -5]], + [[ 1, -1, -1],[ -5, 12,-17]], + [[ 1, 1, -1],[-17, 5,-12]], + ],dtype=float), + }, + 'NW': { + 'cF' : np.array([ + [[ 2, -1, -1],[ 1, 1, 1]], + [[ -1, 2, -1],[ 1, 1, 1]], + [[ -1, -1, 2],[ 1, 1, 1]], + [[ -2, -1, -1],[ -1, 1, 1]], + [[ 1, 2, -1],[ -1, 1, 1]], + [[ 1, -1, 2],[ -1, 1, 1]], + [[ 2, 1, -1],[ 1, -1, 1]], + [[ -1, -2, -1],[ 1, -1, 1]], + [[ -1, 1, 2],[ 1, -1, 1]], + [[ 2, -1, 1],[ -1, -1, 1]], + [[ -1, 2, 1],[ -1, -1, 1]], + [[ -1, -1, -2],[ -1, -1, 1]], + ],dtype=float), + 'cI' : np.array([ + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + ],dtype=float), + }, + 'Pitsch': { + 'cF' : np.array([ + [[ 1, 0, 1],[ 0, 1, 0]], + [[ 1, 1, 0],[ 0, 0, 1]], + [[ 0, 1, 1],[ 1, 0, 0]], + [[ 0, 1, -1],[ 1, 0, 0]], + [[ -1, 0, 1],[ 0, 1, 0]], + [[ 1, -1, 0],[ 0, 0, 1]], + [[ 1, 0, -1],[ 0, 1, 0]], + [[ -1, 1, 0],[ 0, 0, 1]], + [[ 0, -1, 1],[ 1, 0, 0]], + [[ 0, 1, 1],[ 1, 0, 0]], + [[ 1, 0, 1],[ 0, 1, 0]], + [[ 1, 1, 0],[ 0, 0, 1]], + ],dtype=float), + 'cI' : np.array([ + [[ 1, -1, 1],[ -1, 0, 1]], + [[ 1, 1, -1],[ 1, -1, 0]], + [[ -1, 1, 1],[ 0, 1, -1]], + [[ -1, 1, -1],[ 0, -1, -1]], + [[ -1, -1, 1],[ -1, 0, -1]], + [[ 1, -1, -1],[ -1, -1, 0]], + [[ 1, -1, -1],[ -1, 0, -1]], + [[ -1, 1, -1],[ -1, -1, 0]], + [[ -1, -1, 1],[ 0, -1, -1]], + [[ -1, 1, 1],[ 0, -1, 1]], + [[ 1, -1, 1],[ 1, 0, -1]], + [[ 1, 1, -1],[ -1, 1, 0]], + ],dtype=float), + }, + 'Bain': { + 'cF' : np.array([ + [[ 0, 1, 0],[ 1, 0, 0]], + [[ 0, 0, 1],[ 0, 1, 0]], + [[ 1, 0, 0],[ 0, 0, 1]], + ],dtype=float), + 'cI' : np.array([ + [[ 0, 1, 1],[ 1, 0, 0]], + [[ 1, 0, 1],[ 0, 1, 0]], + [[ 1, 1, 0],[ 0, 0, 1]], + ],dtype=float), + }, + 'Burgers' : { + 'cI' : np.array([ + [[ -1, 1, 1],[ 1, 1, 0]], + [[ -1, 1, -1],[ 1, 1, 0]], + [[ 1, 1, 1],[ 1, -1, 0]], + [[ 1, 1, -1],[ 1, -1, 0]], + + [[ 1, 1, -1],[ 1, 0, 1]], + [[ -1, 1, 1],[ 1, 0, 1]], + [[ 1, 1, 1],[ -1, 0, 1]], + [[ 1, -1, 1],[ -1, 0, 1]], + + [[ -1, 1, -1],[ 0, 1, 1]], + [[ 1, 1, -1],[ 0, 1, 1]], + [[ -1, 1, 1],[ 0, -1, 1]], + [[ 1, 1, 1],[ 0, -1, 1]], + ],dtype=float), + 'hP' : np.array([ + [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], + [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], + [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], + [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], + + [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], + [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], + [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], + [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], + + [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], + [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], + [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], + [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], + ],dtype=float), + }, + } diff --git a/python/damask/_lattice_family.py b/python/damask/_lattice_family.py index 8f352e8df..d6288cff0 100644 --- a/python/damask/_lattice_family.py +++ b/python/damask/_lattice_family.py @@ -5,7 +5,15 @@ from . import Rotation class LatticeFamily(): def __init__(self,family): - """Symmetry-related operations for crystal families.""" + """ + Symmetry-related operations for crystal families. + + Parameters + ---------- + family : {'triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic'} + Name of the crystal family. + + """ if family not in self._immutable.keys(): raise KeyError(f'invalid lattice family "{family}"') self.family = family @@ -121,41 +129,36 @@ class LatticeFamily(): _immutable = { - 'cubic': - { + 'cubic': { 'b': 1.0, 'c': 1.0, 'alpha': np.pi/2., 'beta': np.pi/2., 'gamma': np.pi/2., }, - 'hexagonal': - { + 'hexagonal': { 'b': 1.0, 'alpha': np.pi/2., 'beta': np.pi/2., 'gamma': 2.*np.pi/3., }, - 'tetragonal': - { + 'tetragonal': { 'b': 1.0, 'alpha': np.pi/2., 'beta': np.pi/2., 'gamma': np.pi/2., }, - 'orthorhombic': - { + 'orthorhombic': { 'alpha': np.pi/2., 'beta': np.pi/2., 'gamma': np.pi/2., }, - 'monoclinic': - { + 'monoclinic': { 'alpha': np.pi/2., 'gamma': np.pi/2., }, - 'triclinic': {} - } + 'triclinic': {} + } _basis = { diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 8a3f9fe24..b2ee9628b 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -4,9 +4,9 @@ import numpy as np from . import Rotation from . import LatticeFamily +from . import Lattice from . import util from . import tensor -from . import lattice as lattice_ lattice_symmetries = { @@ -130,13 +130,13 @@ class Orientation(Rotation): Defaults to no rotation. """ - Rotation.__init__(self,rotation=rotation) + super().__init__(rotation) if family in set(lattice_symmetries.values()) and lattice is None: self.family = family self.lattice = None - l = LatticeFamily(self.family) + l = LatticeFamily(self.family) # noqa self.immutable = l.immutable self.basis = l.basis self.symmetry_operations = l.symmetry_operations @@ -149,48 +149,25 @@ class Orientation(Rotation): self.family = lattice_symmetries[lattice] self.lattice = lattice - l = LatticeFamily(self.family) + l = Lattice(self.lattice, a,b,c, alpha,beta,gamma, degrees) # noqa self.immutable = l.immutable self.basis = l.basis self.symmetry_operations = l.symmetry_operations - self.a = 1 if a is None else a - self.b = b - self.c = c - self.a = float(self.a) if self.a is not None else \ - (self.b / self.ratio['b'] if self.b is not None and self.ratio['b'] is not None else - self.c / self.ratio['c'] if self.c is not None and self.ratio['c'] is not None else None) - self.b = float(self.b) if self.b is not None else \ - (self.a * self.ratio['b'] if self.a is not None and self.ratio['b'] is not None else - self.c / self.ratio['c'] * self.ratio['b'] - if self.c is not None and self.ratio['b'] is not None and self.ratio['c'] is not None else None) - self.c = float(self.c) if self.c is not None else \ - (self.a * self.ratio['c'] if self.a is not None and self.ratio['c'] is not None else - self.b / self.ratio['b'] * self.ratio['c'] - if self.c is not None and self.ratio['b'] is not None and self.ratio['c'] is not None else None) + self.a = l.a + self.b = l.b + self.c = l.c - self.alpha = np.radians(alpha) if degrees and alpha is not None else alpha - self.beta = np.radians(beta) if degrees and beta is not None else beta - self.gamma = np.radians(gamma) if degrees and gamma is not None else gamma - if self.alpha is None and 'alpha' in self.immutable: self.alpha = self.immutable['alpha'] - if self.beta is None and 'beta' in self.immutable: self.beta = self.immutable['beta'] - if self.gamma is None and 'gamma' in self.immutable: self.gamma = self.immutable['gamma'] + self.alpha = l.alpha + self.beta = l.beta + self.gamma = l.gamma - if \ - (self.a is None) \ - or (self.b is None or ('b' in self.immutable and self.b != self.immutable['b'] * self.a)) \ - or (self.c is None or ('c' in self.immutable and self.c != self.immutable['c'] * self.b)) \ - or (self.alpha is None or ('alpha' in self.immutable and self.alpha != self.immutable['alpha'])) \ - or (self.beta is None or ( 'beta' in self.immutable and self.beta != self.immutable['beta'])) \ - or (self.gamma is None or ('gamma' in self.immutable and self.gamma != self.immutable['gamma'])): - raise ValueError (f'Incompatible parameters {self.parameters} for crystal family {self.family}') + self.ratio = l.ratio - if np.any(np.array([self.alpha,self.beta,self.gamma]) <= 0): - raise ValueError ('Lattice angles must be positive') - if np.any([np.roll([self.alpha,self.beta,self.gamma],r)[0] - > np.sum(np.roll([self.alpha,self.beta,self.gamma],r)[1:]) for r in range(3)]): - raise ValueError ('Each lattice angle must be less than sum of others') + self.to_frame = l.to_frame + self.kinematics = l.kinematics + self.orientation_relationships = l.orientation_relationships else: raise KeyError(f'no valid family or lattice') @@ -590,12 +567,9 @@ class Orientation(Rotation): https://doi.org/10.1016/j.actamat.2004.11.021 """ - if model not in lattice_.relations: + if model not in self.orientation_relationships: raise KeyError(f'unknown orientation relationship "{model}"') - r = lattice_.relations[model] - - if self.lattice not in r: - raise KeyError(f'relationship "{model}" not supported for lattice "{self.lattice}"') + r = self.orientation_relationships[model] sl = self.lattice ol = (set(r)-{sl}).pop() @@ -640,50 +614,6 @@ class Orientation(Rotation): return (self.a,self.b,self.c,self.alpha,self.beta,self.gamma) - @property - def ratio(self): - """Return axes ratios of own lattice.""" - _ratio = { 'hexagonal': {'c': np.sqrt(8./3.)}} - - return dict(b = self.immutable['b'] - if 'b' in self.immutable else - _ratio[self.family]['b'] if self.family in _ratio and 'b' in _ratio[self.family] else None, - c = self.immutable['c'] - if 'c' in self.immutable else - _ratio[self.family]['c'] if self.family in _ratio and 'c' in _ratio[self.family] else None, - ) - - - @property - def basis_real(self): - """ - Calculate orthogonal real space crystal basis. - - References - ---------- - C.T. Young and J.L. Lytton, Journal of Applied Physics 43:1408–1417, 1972 - https://doi.org/10.1063/1.1661333 - - """ - if None in self.parameters: - raise KeyError('missing crystal lattice parameters') - return np.array([ - [1,0,0], - [np.cos(self.gamma),np.sin(self.gamma),0], - [np.cos(self.beta), - (np.cos(self.alpha)-np.cos(self.beta)*np.cos(self.gamma)) /np.sin(self.gamma), - np.sqrt(1 - np.cos(self.alpha)**2 - np.cos(self.beta)**2 - np.cos(self.gamma)**2 - + 2 * np.cos(self.alpha) * np.cos(self.beta) * np.cos(self.gamma))/np.sin(self.gamma)], - ],dtype=float).T \ - * np.array([self.a,self.b,self.c]) - - - @property - def basis_reciprocal(self): - """Calculate reciprocal (dual) crystal basis.""" - return np.linalg.inv(self.basis_real.T) - - def in_SST(self,vector,proper=False): """ Check whether given crystal frame vector falls into standard stereographic triangle of own symmetry. @@ -944,58 +874,6 @@ class Orientation(Rotation): ) - def to_lattice(self,*,direction=None,plane=None): - """ - Calculate lattice vector corresponding to crystal frame direction or plane normal. - - Parameters - ---------- - direction|normal : numpy.ndarray of shape (...,3) - Vector along direction or plane normal. - - Returns - ------- - Miller : numpy.ndarray of shape (...,3) - lattice vector of direction or plane. - Use util.scale_to_coprime to convert to (integer) Miller indices. - - """ - if (direction is not None) ^ (plane is None): - raise KeyError('specify either "direction" or "plane"') - axis,basis = (np.array(direction),self.basis_reciprocal.T) \ - if plane is None else \ - (np.array(plane),self.basis_real.T) - return np.einsum('il,...l',basis,axis) - - - def to_frame(self,*,uvw=None,hkl=None,with_symmetry=False): - """ - Calculate crystal frame vector along lattice direction [uvw] or plane normal (hkl). - - Parameters - ---------- - uvw|hkl : numpy.ndarray of shape (...,3) - Miller indices of crystallographic direction or plane normal. - with_symmetry : bool, optional - Calculate all N symmetrically equivalent vectors. - - Returns - ------- - vector : numpy.ndarray of shape (...,3) or (N,...,3) - Crystal frame vector (or vectors if with_symmetry) along [uvw] direction or (hkl) plane normal. - - """ - if (uvw is not None) ^ (hkl is None): - raise KeyError('specify either "uvw" or "hkl"') - axis,basis = (np.array(uvw),self.basis_real) \ - if hkl is None else \ - (np.array(hkl),self.basis_reciprocal) - return (self.symmetry_operations.broadcast_to(self.symmetry_operations.shape+axis.shape[:-1],mode='right') - @ np.broadcast_to(np.einsum('il,...l',basis,axis),self.symmetry_operations.shape+axis.shape) - if with_symmetry else - np.einsum('il,...l',basis,axis)) - - def to_pole(self,*,uvw=None,hkl=None,with_symmetry=False): """ Calculate lab frame vector along lattice direction [uvw] or plane normal (hkl). @@ -1013,7 +891,11 @@ class Orientation(Rotation): Lab frame vector (or vectors if with_symmetry) along [uvw] direction or (hkl) plane normal. """ - v = self.to_frame(uvw=uvw,hkl=hkl,with_symmetry=with_symmetry) + # ToDo: simplify 'with_symmetry' + v = self.to_frame(uvw=uvw,hkl=hkl) + if with_symmetry: + v = self.symmetry_operations.broadcast_to(self.symmetry_operations.shape+v.shape[:-1],mode='right') \ + @ np.broadcast_to(v,self.symmetry_operations.shape+v.shape) return ~(self if self.shape+v.shape[:-1] == () else self.broadcast_to(self.shape+v.shape[:-1],mode='right')) \ @ np.broadcast_to(v,self.shape+v.shape) @@ -1047,15 +929,10 @@ class Orientation(Rotation): """ try: - master = lattice_.kinematics[self.lattice][mode] - kinematics = {'direction':master[:,0:3],'plane':master[:,3:6]} \ - if master.shape[-1] == 6 else \ - {'direction':util.Bravais_to_Miller(uvtw=master[:,0:4]), - 'plane': util.Bravais_to_Miller(hkil=master[:,4:8])} + d = self.to_frame(uvw=self.kinematics(mode)['direction']) + p = self.to_frame(hkl=self.kinematics(mode)['plane']) except KeyError: raise (f'"{mode}" not defined for lattice "{self.lattice}"') - d = self.to_frame(uvw=kinematics['direction'],with_symmetry=False) - p = self.to_frame(hkl=kinematics['plane'] ,with_symmetry=False) P = np.einsum('...i,...j',d/np.linalg.norm(d,axis=-1,keepdims=True), p/np.linalg.norm(p,axis=-1,keepdims=True)) diff --git a/python/damask/lattice.py b/python/damask/lattice.py deleted file mode 100644 index 5b4621f3d..000000000 --- a/python/damask/lattice.py +++ /dev/null @@ -1,444 +0,0 @@ -import numpy as _np - -kinematics = { - 'cF': { - 'slip' : _np.array([ - [+0,+1,-1 , +1,+1,+1], - [-1,+0,+1 , +1,+1,+1], - [+1,-1,+0 , +1,+1,+1], - [+0,-1,-1 , -1,-1,+1], - [+1,+0,+1 , -1,-1,+1], - [-1,+1,+0 , -1,-1,+1], - [+0,-1,+1 , +1,-1,-1], - [-1,+0,-1 , +1,-1,-1], - [+1,+1,+0 , +1,-1,-1], - [+0,+1,+1 , -1,+1,-1], - [+1,+0,-1 , -1,+1,-1], - [-1,-1,+0 , -1,+1,-1], - [+1,+1,+0 , +1,-1,+0], - [+1,-1,+0 , +1,+1,+0], - [+1,+0,+1 , +1,+0,-1], - [+1,+0,-1 , +1,+0,+1], - [+0,+1,+1 , +0,+1,-1], - [+0,+1,-1 , +0,+1,+1], - ],'d'), - 'twin' : _np.array([ - [-2, 1, 1, 1, 1, 1], - [ 1,-2, 1, 1, 1, 1], - [ 1, 1,-2, 1, 1, 1], - [ 2,-1, 1, -1,-1, 1], - [-1, 2, 1, -1,-1, 1], - [-1,-1,-2, -1,-1, 1], - [-2,-1,-1, 1,-1,-1], - [ 1, 2,-1, 1,-1,-1], - [ 1,-1, 2, 1,-1,-1], - [ 2, 1,-1, -1, 1,-1], - [-1,-2,-1, -1, 1,-1], - [-1, 1, 2, -1, 1,-1], - ],dtype=float), - }, - 'cI': { - 'slip' : _np.array([ - [+1,-1,+1 , +0,+1,+1], - [-1,-1,+1 , +0,+1,+1], - [+1,+1,+1 , +0,-1,+1], - [-1,+1,+1 , +0,-1,+1], - [-1,+1,+1 , +1,+0,+1], - [-1,-1,+1 , +1,+0,+1], - [+1,+1,+1 , -1,+0,+1], - [+1,-1,+1 , -1,+0,+1], - [-1,+1,+1 , +1,+1,+0], - [-1,+1,-1 , +1,+1,+0], - [+1,+1,+1 , -1,+1,+0], - [+1,+1,-1 , -1,+1,+0], - [-1,+1,+1 , +2,+1,+1], - [+1,+1,+1 , -2,+1,+1], - [+1,+1,-1 , +2,-1,+1], - [+1,-1,+1 , +2,+1,-1], - [+1,-1,+1 , +1,+2,+1], - [+1,+1,-1 , -1,+2,+1], - [+1,+1,+1 , +1,-2,+1], - [-1,+1,+1 , +1,+2,-1], - [+1,+1,-1 , +1,+1,+2], - [+1,-1,+1 , -1,+1,+2], - [-1,+1,+1 , +1,-1,+2], - [+1,+1,+1 , +1,+1,-2], - [+1,+1,-1 , +1,+2,+3], - [+1,-1,+1 , -1,+2,+3], - [-1,+1,+1 , +1,-2,+3], - [+1,+1,+1 , +1,+2,-3], - [+1,-1,+1 , +1,+3,+2], - [+1,+1,-1 , -1,+3,+2], - [+1,+1,+1 , +1,-3,+2], - [-1,+1,+1 , +1,+3,-2], - [+1,+1,-1 , +2,+1,+3], - [+1,-1,+1 , -2,+1,+3], - [-1,+1,+1 , +2,-1,+3], - [+1,+1,+1 , +2,+1,-3], - [+1,-1,+1 , +2,+3,+1], - [+1,+1,-1 , -2,+3,+1], - [+1,+1,+1 , +2,-3,+1], - [-1,+1,+1 , +2,+3,-1], - [-1,+1,+1 , +3,+1,+2], - [+1,+1,+1 , -3,+1,+2], - [+1,+1,-1 , +3,-1,+2], - [+1,-1,+1 , +3,+1,-2], - [-1,+1,+1 , +3,+2,+1], - [+1,+1,+1 , -3,+2,+1], - [+1,+1,-1 , +3,-2,+1], - [+1,-1,+1 , +3,+2,-1], - ],'d'), - 'twin' : _np.array([ - [-1, 1, 1, 2, 1, 1], - [ 1, 1, 1, -2, 1, 1], - [ 1, 1,-1, 2,-1, 1], - [ 1,-1, 1, 2, 1,-1], - [ 1,-1, 1, 1, 2, 1], - [ 1, 1,-1, -1, 2, 1], - [ 1, 1, 1, 1,-2, 1], - [-1, 1, 1, 1, 2,-1], - [ 1, 1,-1, 1, 1, 2], - [ 1,-1, 1, -1, 1, 2], - [-1, 1, 1, 1,-1, 2], - [ 1, 1, 1, 1, 1,-2], - ],dtype=float), - }, - 'hP': { - 'slip' : _np.array([ - [+2,-1,-1,+0 , +0,+0,+0,+1], - [-1,+2,-1,+0 , +0,+0,+0,+1], - [-1,-1,+2,+0 , +0,+0,+0,+1], - [+2,-1,-1,+0 , +0,+1,-1,+0], - [-1,+2,-1,+0 , -1,+0,+1,+0], - [-1,-1,+2,+0 , +1,-1,+0,+0], - [-1,+1,+0,+0 , +1,+1,-2,+0], - [+0,-1,+1,+0 , -2,+1,+1,+0], - [+1,+0,-1,+0 , +1,-2,+1,+0], - [-1,+2,-1,+0 , +1,+0,-1,+1], - [-2,+1,+1,+0 , +0,+1,-1,+1], - [-1,-1,+2,+0 , -1,+1,+0,+1], - [+1,-2,+1,+0 , -1,+0,+1,+1], - [+2,-1,-1,+0 , +0,-1,+1,+1], - [+1,+1,-2,+0 , +1,-1,+0,+1], - [-2,+1,+1,+3 , +1,+0,-1,+1], - [-1,-1,+2,+3 , +1,+0,-1,+1], - [-1,-1,+2,+3 , +0,+1,-1,+1], - [+1,-2,+1,+3 , +0,+1,-1,+1], - [+1,-2,+1,+3 , -1,+1,+0,+1], - [+2,-1,-1,+3 , -1,+1,+0,+1], - [+2,-1,-1,+3 , -1,+0,+1,+1], - [+1,+1,-2,+3 , -1,+0,+1,+1], - [+1,+1,-2,+3 , +0,-1,+1,+1], - [-1,+2,-1,+3 , +0,-1,+1,+1], - [-1,+2,-1,+3 , +1,-1,+0,+1], - [-2,+1,+1,+3 , +1,-1,+0,+1], - [-1,-1,+2,+3 , +1,+1,-2,+2], - [+1,-2,+1,+3 , -1,+2,-1,+2], - [+2,-1,-1,+3 , -2,+1,+1,+2], - [+1,+1,-2,+3 , -1,-1,+2,+2], - [-1,+2,-1,+3 , +1,-2,+1,+2], - [-2,+1,+1,+3 , +2,-1,-1,+2], - ],'d'), - 'twin' : _np.array([ - [-1, 0, 1, 1, 1, 0, -1, 2], # shear = (3-(c/a)^2)/(sqrt(3) c/a) <-10.1>{10.2} - [ 0, -1, 1, 1, 0, 1, -1, 2], - [ 1, -1, 0, 1, -1, 1, 0, 2], - [ 1, 0, -1, 1, -1, 0, 1, 2], - [ 0, 1, -1, 1, 0, -1, 1, 2], - [-1, 1, 0, 1, 1, -1, 0, 2], - [-1, -1, 2, 6, 1, 1, -2, 1], # shear = 1/(c/a) <11.6>{-1-1.1} - [ 1, -2, 1, 6, -1, 2, -1, 1], - [ 2, -1, -1, 6, -2, 1, 1, 1], - [ 1, 1, -2, 6, -1, -1, 2, 1], - [-1, 2, -1, 6, 1, -2, 1, 1], - [-2, 1, 1, 6, 2, -1, -1, 1], - [ 1, 0, -1, -2, 1, 0, -1, 1], # shear = (4(c/a)^2-9)/(4 sqrt(3) c/a) <10.-2>{10.1} - [ 0, 1, -1, -2, 0, 1, -1, 1], - [-1, 1, 0, -2, -1, 1, 0, 1], - [-1, 0, 1, -2, -1, 0, 1, 1], - [ 0, -1, 1, -2, 0, -1, 1, 1], - [ 1, -1, 0, -2, 1, -1, 0, 1], - [ 1, 1, -2, -3, 1, 1, -2, 2], # shear = 2((c/a)^2-2)/(3 c/a) <11.-3>{11.2} - [-1, 2, -1, -3, -1, 2, -1, 2], - [-2, 1, 1, -3, -2, 1, 1, 2], - [-1, -1, 2, -3, -1, -1, 2, 2], - [ 1, -2, 1, -3, 1, -2, 1, 2], - [ 2, -1, -1, -3, 2, -1, -1, 2], - ],dtype=float), - }, -} - -# Kurdjomov--Sachs orientation relationship for fcc <-> bcc transformation -# from S. Morito et al., Journal of Alloys and Compounds 577:s587-s592, 2013 -# also see K. Kitahara et al., Acta Materialia 54:1279-1288, 2006 - -relations = { - 'KS': { - 'cF' : _np.array([ - [[ -1, 0, 1],[ 1, 1, 1]], - [[ -1, 0, 1],[ 1, 1, 1]], - [[ 0, 1, -1],[ 1, 1, 1]], - [[ 0, 1, -1],[ 1, 1, 1]], - [[ 1, -1, 0],[ 1, 1, 1]], - [[ 1, -1, 0],[ 1, 1, 1]], - [[ 1, 0, -1],[ 1, -1, 1]], - [[ 1, 0, -1],[ 1, -1, 1]], - [[ -1, -1, 0],[ 1, -1, 1]], - [[ -1, -1, 0],[ 1, -1, 1]], - [[ 0, 1, 1],[ 1, -1, 1]], - [[ 0, 1, 1],[ 1, -1, 1]], - [[ 0, -1, 1],[ -1, 1, 1]], - [[ 0, -1, 1],[ -1, 1, 1]], - [[ -1, 0, -1],[ -1, 1, 1]], - [[ -1, 0, -1],[ -1, 1, 1]], - [[ 1, 1, 0],[ -1, 1, 1]], - [[ 1, 1, 0],[ -1, 1, 1]], - [[ -1, 1, 0],[ 1, 1, -1]], - [[ -1, 1, 0],[ 1, 1, -1]], - [[ 0, -1, -1],[ 1, 1, -1]], - [[ 0, -1, -1],[ 1, 1, -1]], - [[ 1, 0, 1],[ 1, 1, -1]], - [[ 1, 0, 1],[ 1, 1, -1]], - ],dtype=float), - 'cI' : _np.array([ - [[ -1, -1, 1],[ 0, 1, 1]], - [[ -1, 1, -1],[ 0, 1, 1]], - [[ -1, -1, 1],[ 0, 1, 1]], - [[ -1, 1, -1],[ 0, 1, 1]], - [[ -1, -1, 1],[ 0, 1, 1]], - [[ -1, 1, -1],[ 0, 1, 1]], - [[ -1, -1, 1],[ 0, 1, 1]], - [[ -1, 1, -1],[ 0, 1, 1]], - [[ -1, -1, 1],[ 0, 1, 1]], - [[ -1, 1, -1],[ 0, 1, 1]], - [[ -1, -1, 1],[ 0, 1, 1]], - [[ -1, 1, -1],[ 0, 1, 1]], - [[ -1, -1, 1],[ 0, 1, 1]], - [[ -1, 1, -1],[ 0, 1, 1]], - [[ -1, -1, 1],[ 0, 1, 1]], - [[ -1, 1, -1],[ 0, 1, 1]], - [[ -1, -1, 1],[ 0, 1, 1]], - [[ -1, 1, -1],[ 0, 1, 1]], - [[ -1, -1, 1],[ 0, 1, 1]], - [[ -1, 1, -1],[ 0, 1, 1]], - [[ -1, -1, 1],[ 0, 1, 1]], - [[ -1, 1, -1],[ 0, 1, 1]], - [[ -1, -1, 1],[ 0, 1, 1]], - [[ -1, 1, -1],[ 0, 1, 1]], - ],dtype=float), - }, - 'GT': { - 'cF' : _np.array([ - [[ -5,-12, 17],[ 1, 1, 1]], - [[ 17, -5,-12],[ 1, 1, 1]], - [[-12, 17, -5],[ 1, 1, 1]], - [[ 5, 12, 17],[ -1, -1, 1]], - [[-17, 5,-12],[ -1, -1, 1]], - [[ 12,-17, -5],[ -1, -1, 1]], - [[ -5, 12,-17],[ -1, 1, 1]], - [[ 17, 5, 12],[ -1, 1, 1]], - [[-12,-17, 5],[ -1, 1, 1]], - [[ 5,-12,-17],[ 1, -1, 1]], - [[-17, -5, 12],[ 1, -1, 1]], - [[ 12, 17, 5],[ 1, -1, 1]], - [[ -5, 17,-12],[ 1, 1, 1]], - [[-12, -5, 17],[ 1, 1, 1]], - [[ 17,-12, -5],[ 1, 1, 1]], - [[ 5,-17,-12],[ -1, -1, 1]], - [[ 12, 5, 17],[ -1, -1, 1]], - [[-17, 12, -5],[ -1, -1, 1]], - [[ -5,-17, 12],[ -1, 1, 1]], - [[-12, 5,-17],[ -1, 1, 1]], - [[ 17, 12, 5],[ -1, 1, 1]], - [[ 5, 17, 12],[ 1, -1, 1]], - [[ 12, -5,-17],[ 1, -1, 1]], - [[-17,-12, 5],[ 1, -1, 1]], - ],dtype=float), - 'cI' : _np.array([ - [[-17, -7, 17],[ 1, 0, 1]], - [[ 17,-17, -7],[ 1, 1, 0]], - [[ -7, 17,-17],[ 0, 1, 1]], - [[ 17, 7, 17],[ -1, 0, 1]], - [[-17, 17, -7],[ -1, -1, 0]], - [[ 7,-17,-17],[ 0, -1, 1]], - [[-17, 7,-17],[ -1, 0, 1]], - [[ 17, 17, 7],[ -1, 1, 0]], - [[ -7,-17, 17],[ 0, 1, 1]], - [[ 17, -7,-17],[ 1, 0, 1]], - [[-17,-17, 7],[ 1, -1, 0]], - [[ 7, 17, 17],[ 0, -1, 1]], - [[-17, 17, -7],[ 1, 1, 0]], - [[ -7,-17, 17],[ 0, 1, 1]], - [[ 17, -7,-17],[ 1, 0, 1]], - [[ 17,-17, -7],[ -1, -1, 0]], - [[ 7, 17, 17],[ 0, -1, 1]], - [[-17, 7,-17],[ -1, 0, 1]], - [[-17,-17, 7],[ -1, 1, 0]], - [[ -7, 17,-17],[ 0, 1, 1]], - [[ 17, 7, 17],[ -1, 0, 1]], - [[ 17, 17, 7],[ 1, -1, 0]], - [[ 7,-17,-17],[ 0, -1, 1]], - [[-17, -7, 17],[ 1, 0, 1]], - ],dtype=float), - }, - 'GT_prime': { - 'cF' : _np.array([ - [[ 0, 1, -1],[ 7, 17, 17]], - [[ -1, 0, 1],[ 17, 7, 17]], - [[ 1, -1, 0],[ 17, 17, 7]], - [[ 0, -1, -1],[ -7,-17, 17]], - [[ 1, 0, 1],[-17, -7, 17]], - [[ 1, -1, 0],[-17,-17, 7]], - [[ 0, 1, -1],[ 7,-17,-17]], - [[ 1, 0, 1],[ 17, -7,-17]], - [[ -1, -1, 0],[ 17,-17, -7]], - [[ 0, -1, -1],[ -7, 17,-17]], - [[ -1, 0, 1],[-17, 7,-17]], - [[ -1, -1, 0],[-17, 17, -7]], - [[ 0, -1, 1],[ 7, 17, 17]], - [[ 1, 0, -1],[ 17, 7, 17]], - [[ -1, 1, 0],[ 17, 17, 7]], - [[ 0, 1, 1],[ -7,-17, 17]], - [[ -1, 0, -1],[-17, -7, 17]], - [[ -1, 1, 0],[-17,-17, 7]], - [[ 0, -1, 1],[ 7,-17,-17]], - [[ -1, 0, -1],[ 17, -7,-17]], - [[ 1, 1, 0],[ 17,-17, -7]], - [[ 0, 1, 1],[ -7, 17,-17]], - [[ 1, 0, -1],[-17, 7,-17]], - [[ 1, 1, 0],[-17, 17, -7]], - ],dtype=float), - 'cI' : _np.array([ - [[ 1, 1, -1],[ 12, 5, 17]], - [[ -1, 1, 1],[ 17, 12, 5]], - [[ 1, -1, 1],[ 5, 17, 12]], - [[ -1, -1, -1],[-12, -5, 17]], - [[ 1, -1, 1],[-17,-12, 5]], - [[ 1, -1, -1],[ -5,-17, 12]], - [[ -1, 1, -1],[ 12, -5,-17]], - [[ 1, 1, 1],[ 17,-12, -5]], - [[ -1, -1, 1],[ 5,-17,-12]], - [[ 1, -1, -1],[-12, 5,-17]], - [[ -1, -1, 1],[-17, 12, -5]], - [[ -1, -1, -1],[ -5, 17,-12]], - [[ 1, -1, 1],[ 12, 17, 5]], - [[ 1, 1, -1],[ 5, 12, 17]], - [[ -1, 1, 1],[ 17, 5, 12]], - [[ -1, 1, 1],[-12,-17, 5]], - [[ -1, -1, -1],[ -5,-12, 17]], - [[ -1, 1, -1],[-17, -5, 12]], - [[ -1, -1, 1],[ 12,-17, -5]], - [[ -1, 1, -1],[ 5,-12,-17]], - [[ 1, 1, 1],[ 17, -5,-12]], - [[ 1, 1, 1],[-12, 17, -5]], - [[ 1, -1, -1],[ -5, 12,-17]], - [[ 1, 1, -1],[-17, 5,-12]], - ],dtype=float), - }, - 'NW': { - 'cF' : _np.array([ - [[ 2, -1, -1],[ 1, 1, 1]], - [[ -1, 2, -1],[ 1, 1, 1]], - [[ -1, -1, 2],[ 1, 1, 1]], - [[ -2, -1, -1],[ -1, 1, 1]], - [[ 1, 2, -1],[ -1, 1, 1]], - [[ 1, -1, 2],[ -1, 1, 1]], - [[ 2, 1, -1],[ 1, -1, 1]], - [[ -1, -2, -1],[ 1, -1, 1]], - [[ -1, 1, 2],[ 1, -1, 1]], - [[ 2, -1, 1],[ -1, -1, 1]], - [[ -1, 2, 1],[ -1, -1, 1]], - [[ -1, -1, -2],[ -1, -1, 1]], - ],dtype=float), - 'cI' : _np.array([ - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - ],dtype=float), - }, - 'Pitsch': { - 'cF' : _np.array([ - [[ 1, 0, 1],[ 0, 1, 0]], - [[ 1, 1, 0],[ 0, 0, 1]], - [[ 0, 1, 1],[ 1, 0, 0]], - [[ 0, 1, -1],[ 1, 0, 0]], - [[ -1, 0, 1],[ 0, 1, 0]], - [[ 1, -1, 0],[ 0, 0, 1]], - [[ 1, 0, -1],[ 0, 1, 0]], - [[ -1, 1, 0],[ 0, 0, 1]], - [[ 0, -1, 1],[ 1, 0, 0]], - [[ 0, 1, 1],[ 1, 0, 0]], - [[ 1, 0, 1],[ 0, 1, 0]], - [[ 1, 1, 0],[ 0, 0, 1]], - ],dtype=float), - 'cI' : _np.array([ - [[ 1, -1, 1],[ -1, 0, 1]], - [[ 1, 1, -1],[ 1, -1, 0]], - [[ -1, 1, 1],[ 0, 1, -1]], - [[ -1, 1, -1],[ 0, -1, -1]], - [[ -1, -1, 1],[ -1, 0, -1]], - [[ 1, -1, -1],[ -1, -1, 0]], - [[ 1, -1, -1],[ -1, 0, -1]], - [[ -1, 1, -1],[ -1, -1, 0]], - [[ -1, -1, 1],[ 0, -1, -1]], - [[ -1, 1, 1],[ 0, -1, 1]], - [[ 1, -1, 1],[ 1, 0, -1]], - [[ 1, 1, -1],[ -1, 1, 0]], - ],dtype=float), - }, - 'Bain': { - 'cF' : _np.array([ - [[ 0, 1, 0],[ 1, 0, 0]], - [[ 0, 0, 1],[ 0, 1, 0]], - [[ 1, 0, 0],[ 0, 0, 1]], - ],dtype=float), - 'cI' : _np.array([ - [[ 0, 1, 1],[ 1, 0, 0]], - [[ 1, 0, 1],[ 0, 1, 0]], - [[ 1, 1, 0],[ 0, 0, 1]], - ],dtype=float), - }, - 'Burgers' : { - 'cI' : _np.array([ - [[ -1, 1, 1],[ 1, 1, 0]], - [[ -1, 1, -1],[ 1, 1, 0]], - [[ 1, 1, 1],[ 1, -1, 0]], - [[ 1, 1, -1],[ 1, -1, 0]], - - [[ 1, 1, -1],[ 1, 0, 1]], - [[ -1, 1, 1],[ 1, 0, 1]], - [[ 1, 1, 1],[ -1, 0, 1]], - [[ 1, -1, 1],[ -1, 0, 1]], - - [[ -1, 1, -1],[ 0, 1, 1]], - [[ 1, 1, -1],[ 0, 1, 1]], - [[ -1, 1, 1],[ 0, -1, 1]], - [[ 1, 1, 1],[ 0, -1, 1]], - ],dtype=float), - 'hP' : _np.array([ - [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], - [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], - [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], - [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], - - [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], - [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], - [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], - [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], - - [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], - [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], - [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], - [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], - ],dtype=float), - }, -} diff --git a/python/tests/test_Lattice.py b/python/tests/test_Lattice.py new file mode 100644 index 000000000..c9593301c --- /dev/null +++ b/python/tests/test_Lattice.py @@ -0,0 +1,64 @@ +import pytest +import numpy as np + +from damask import Lattice + +class TestLattice: + + def test_double_to_lattice(self): + L = Lattice('cF') + with pytest.raises(KeyError): + L.to_lattice(direction=np.ones(3),plane=np.ones(3)) + + def test_double_to_frame(self): + L = Lattice('cF') + with pytest.raises(KeyError): + L.to_frame(uvw=np.ones(3),hkl=np.ones(3)) + + @pytest.mark.parametrize('lattice,a,b,c,alpha,beta,gamma', + [ + ('aP',0.5,2.0,3.0,0.8,0.5,1.2), + ('mP',1.0,2.0,3.0,np.pi/2,0.5,np.pi/2), + ('oI',0.5,1.5,3.0,np.pi/2,np.pi/2,np.pi/2), + ('tP',0.5,0.5,3.0,np.pi/2,np.pi/2,np.pi/2), + ('hP',1.0,None,1.6,np.pi/2,np.pi/2,2*np.pi/3), + ('cF',1.0,1.0,None,np.pi/2,np.pi/2,np.pi/2), + ]) + def test_bases_contraction(self,lattice,a,b,c,alpha,beta,gamma): + L = Lattice(lattice=lattice, + a=a,b=b,c=c, + alpha=alpha,beta=beta,gamma=gamma) + assert np.allclose(np.eye(3),np.einsum('ik,jk',L.basis_real,L.basis_reciprocal)) + + + @pytest.mark.parametrize('keyFrame,keyLattice',[('uvw','direction'),('hkl','plane'),]) + @pytest.mark.parametrize('vector',np.array([ + [1.,1.,1.], + [-2.,3.,0.5], + [0.,0.,1.], + [1.,1.,1.], + [2.,2.,2.], + [0.,1.,1.], + ])) + @pytest.mark.parametrize('lattice,a,b,c,alpha,beta,gamma', + [ + ('aP',0.5,2.0,3.0,0.8,0.5,1.2), + ('mP',1.0,2.0,3.0,np.pi/2,0.5,np.pi/2), + ('oI',0.5,1.5,3.0,np.pi/2,np.pi/2,np.pi/2), + ('tP',0.5,0.5,3.0,np.pi/2,np.pi/2,np.pi/2), + ('hP',1.0,1.0,1.6,np.pi/2,np.pi/2,2*np.pi/3), + ('cF',1.0,1.0,1.0,np.pi/2,np.pi/2,np.pi/2), + ]) + def test_to_frame_to_lattice(self,lattice,a,b,c,alpha,beta,gamma,vector,keyFrame,keyLattice): + L = Lattice(lattice=lattice, + a=a,b=b,c=c, + alpha=alpha,beta=beta,gamma=gamma) + assert np.allclose(vector, + L.to_frame(**{keyFrame:L.to_lattice(**{keyLattice:vector})})) + + + @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch','Burgers']) + def test_relationship_definition(self,model): + m,o = list(Lattice._orientation_relationships[model]) + assert Lattice._orientation_relationships[model][m].shape[:-1] == \ + Lattice._orientation_relationships[model][o].shape[:-1] diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index 471dada58..a1302bcb7 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -7,7 +7,6 @@ from damask import Orientation from damask import Table from damask import util from damask import grid_filters -from damask import lattice from damask import _orientation crystal_families = set(_orientation.lattice_symmetries.values()) @@ -341,29 +340,6 @@ class TestOrientation: with pytest.raises(KeyError): Orientation(family=invalid_family) - def test_invalid_rot(self): - with pytest.raises(TypeError): - Orientation.from_random(family='cubic') * np.ones(3) - - def test_missing_symmetry_immutable(self): - with pytest.raises(KeyError): - Orientation(lattice=None).immutable # noqa - - def test_missing_symmetry_basis_real(self): - with pytest.raises(KeyError): - Orientation(lattice=None).basis_real # noqa - - def test_missing_symmetry_basis_reciprocal(self): - with pytest.raises(KeyError): - Orientation(lattice=None).basis_reciprocal # noqa - - def test_double_to_lattice(self): - with pytest.raises(KeyError): - Orientation().to_lattice(direction=np.ones(3),plane=np.ones(3)) # noqa - - def test_double_to_frame(self): - with pytest.raises(KeyError): - Orientation().to_frame(uvw=np.ones(3),hkl=np.ones(3)) # noqa @pytest.mark.parametrize('relation',[None,'Peter','Paul']) def test_unknown_relation(self,relation): @@ -395,12 +371,6 @@ class TestOrientation: o = Orientation(family='cubic') # noqa with pytest.raises(ValueError): eval(f'o.{function}(np.ones(4))') - - @pytest.mark.parametrize('model',lattice.relations) - def test_relationship_definition(self,model): - m,o = list(lattice.relations[model]) - assert lattice.relations[model][m].shape[:-1] == lattice.relations[model][o].shape[:-1] - @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) @pytest.mark.parametrize('lattice',['cF','cI']) def test_relationship_vectorize(self,set_of_quaternions,lattice,model): @@ -441,46 +411,6 @@ class TestOrientation: ) assert np.allclose(o.to_frame(uvw=np.eye(3)),basis), 'Lattice basis disagrees with initialization' - @pytest.mark.parametrize('lattice,a,b,c,alpha,beta,gamma', - [ - ('aP',0.5,2.0,3.0,0.8,0.5,1.2), - ('mP',1.0,2.0,3.0,np.pi/2,0.5,np.pi/2), - ('oI',0.5,1.5,3.0,np.pi/2,np.pi/2,np.pi/2), - ('tP',0.5,0.5,3.0,np.pi/2,np.pi/2,np.pi/2), - ('hP',1.0,None,1.6,np.pi/2,np.pi/2,2*np.pi/3), - ('cF',1.0,1.0,None,np.pi/2,np.pi/2,np.pi/2), - ]) - def test_bases_contraction(self,lattice,a,b,c,alpha,beta,gamma): - L = Orientation(lattice=lattice, - a=a,b=b,c=c, - alpha=alpha,beta=beta,gamma=gamma) - assert np.allclose(np.eye(3),np.einsum('ik,jk',L.basis_real,L.basis_reciprocal)) - - @pytest.mark.parametrize('keyFrame,keyLattice',[('uvw','direction'),('hkl','plane'),]) - @pytest.mark.parametrize('vector',np.array([ - [1.,1.,1.], - [-2.,3.,0.5], - [0.,0.,1.], - [1.,1.,1.], - [2.,2.,2.], - [0.,1.,1.], - ])) - @pytest.mark.parametrize('lattice,a,b,c,alpha,beta,gamma', - [ - ('aP',0.5,2.0,3.0,0.8,0.5,1.2), - ('mP',1.0,2.0,3.0,np.pi/2,0.5,np.pi/2), - ('oI',0.5,1.5,3.0,np.pi/2,np.pi/2,np.pi/2), - ('tP',0.5,0.5,3.0,np.pi/2,np.pi/2,np.pi/2), - ('hP',1.0,1.0,1.6,np.pi/2,np.pi/2,2*np.pi/3), - ('cF',1.0,1.0,1.0,np.pi/2,np.pi/2,np.pi/2), - ]) - def test_to_frame_to_lattice(self,lattice,a,b,c,alpha,beta,gamma,vector,keyFrame,keyLattice): - L = Orientation(lattice=lattice, - a=a,b=b,c=c, - alpha=alpha,beta=beta,gamma=gamma) - assert np.allclose(vector, - L.to_frame(**{keyFrame:L.to_lattice(**{keyLattice:vector})})) - @pytest.mark.parametrize('lattice,a,b,c,alpha,beta,gamma', [ From 87e94b6cf4341d1add582eb8bfc460b40120888c Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 2 Jun 2021 17:25:24 +0200 Subject: [PATCH 05/43] sorting most functions require only lattice family, functions that require full lattice details are at the end --- python/damask/_orientation.py | 377 +++++++++++++++++----------------- 1 file changed, 189 insertions(+), 188 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index b2ee9628b..0810c6624 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -132,7 +132,7 @@ class Orientation(Rotation): """ super().__init__(rotation) - if family in set(lattice_symmetries.values()) and lattice is None: + if family in set(lattice_symmetries.values()) and lattice is None: self.family = family self.lattice = None @@ -178,6 +178,11 @@ class Orientation(Rotation): + ([f'Crystal family {self.family}']) + [super().__repr__()]) + @property + def parameters(self): + """Return lattice parameters a, b, c, alpha, beta, gamma.""" + return (self.a,self.b,self.c,self.alpha,self.beta,self.gamma) + def __copy__(self,**kwargs): """Create deep copy.""" @@ -533,193 +538,6 @@ class Orientation(Rotation): return np.ones_like(rho[...,0],dtype=bool) - def relation_operations(self,model,return_lattice=False): - """ - Crystallographic orientation relationships for phase transformations. - - Parameters - ---------- - model : str - Name of orientation relationship. - return_lattice : bool, optional - Return the target lattice in addition. - - Returns - ------- - operations : Rotations - Rotations characterizing the orientation relationship. - - References - ---------- - S. Morito et al., Journal of Alloys and Compounds 577:s587-s592, 2013 - https://doi.org/10.1016/j.jallcom.2012.02.004 - - K. Kitahara et al., Acta Materialia 54(5):1279-1288, 2006 - https://doi.org/10.1016/j.actamat.2005.11.001 - - Y. He et al., Journal of Applied Crystallography 39:72-81, 2006 - https://doi.org/10.1107/S0021889805038276 - - H. Kitahara et al., Materials Characterization 54(4-5):378-386, 2005 - https://doi.org/10.1016/j.matchar.2004.12.015 - - Y. He et al., Acta Materialia 53(4):1179-1190, 2005 - https://doi.org/10.1016/j.actamat.2004.11.021 - - """ - if model not in self.orientation_relationships: - raise KeyError(f'unknown orientation relationship "{model}"') - r = self.orientation_relationships[model] - - sl = self.lattice - ol = (set(r)-{sl}).pop() - m = r[sl] - o = r[ol] - - p_,_p = np.zeros(m.shape[:-1]+(3,)),np.zeros(o.shape[:-1]+(3,)) - p_[...,0,:] = m[...,0,:] if m.shape[-1] == 3 else util.Bravais_to_Miller(uvtw=m[...,0,0:4]) - p_[...,1,:] = m[...,1,:] if m.shape[-1] == 3 else util.Bravais_to_Miller(hkil=m[...,1,0:4]) - _p[...,0,:] = o[...,0,:] if o.shape[-1] == 3 else util.Bravais_to_Miller(uvtw=o[...,0,0:4]) - _p[...,1,:] = o[...,1,:] if o.shape[-1] == 3 else util.Bravais_to_Miller(hkil=o[...,1,0:4]) - - return (Rotation.from_parallel(p_,_p),ol) \ - if return_lattice else \ - Rotation.from_parallel(p_,_p) - - - def related(self,model): - """ - Orientations derived from the given relationship. - - One dimension (length according to number of related orientations) - is added to the left of the Rotation array. - - """ - o,lattice = self.relation_operations(model,return_lattice=True) - target = Orientation(lattice=lattice) - o = o.broadcast_to(o.shape+self.shape,mode='right') - return self.copy(rotation=o*Rotation(self.quaternion).broadcast_to(o.shape,mode='left'), - lattice=lattice, - b = self.b if target.ratio['b'] is None else self.a*target.ratio['b'], - c = self.c if target.ratio['c'] is None else self.a*target.ratio['c'], - alpha = None if 'alpha' in target.immutable else self.alpha, - beta = None if 'beta' in target.immutable else self.beta, - gamma = None if 'gamma' in target.immutable else self.gamma, - ) - - - @property - def parameters(self): - """Return lattice parameters a, b, c, alpha, beta, gamma.""" - return (self.a,self.b,self.c,self.alpha,self.beta,self.gamma) - - - def in_SST(self,vector,proper=False): - """ - Check whether given crystal frame vector falls into standard stereographic triangle of own symmetry. - - Parameters - ---------- - vector : numpy.ndarray of shape (...,3) - Vector to check. - proper : bool, optional - Consider only vectors with z >= 0, hence combine two neighboring SSTs. - Defaults to False. - - Returns - ------- - in : numpy.ndarray of shape (...) - Boolean array indicating whether vector falls into SST. - - """ - if not isinstance(vector,np.ndarray) or vector.shape[-1] != 3: - raise ValueError('input is not a field of three-dimensional vectors') - - if self.basis is None: # direct exit for no symmetry - return np.ones_like(vector[...,0],bool) - - if proper: - components_proper = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.basis['proper'], vector.shape+(3,)), - vector), 12) - components_improper = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.basis['improper'], vector.shape+(3,)), - vector), 12) - return np.all(components_proper >= 0.0,axis=-1) \ - | np.all(components_improper >= 0.0,axis=-1) - else: - components = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.basis['improper'], vector.shape+(3,)), - np.block([vector[...,:2],np.abs(vector[...,2:3])])), 12) - - return np.all(components >= 0.0,axis=-1) - - - def IPF_color(self,vector,in_SST=True,proper=False): - """ - Map vector to RGB color within standard stereographic triangle of own symmetry. - - Parameters - ---------- - vector : numpy.ndarray of shape (...,3) - Vector to colorize. - in_SST : bool, optional - Consider symmetrically equivalent orientations such that poles are located in SST. - Defaults to True. - proper : bool, optional - Consider only vectors with z >= 0, hence combine two neighboring SSTs (with mirrored colors). - Defaults to False. - - Returns - ------- - rgb : numpy.ndarray of shape (...,3) - RGB array of IPF colors. - - Examples - -------- - Inverse pole figure color of the e_3 direction for a crystal in "Cube" orientation with cubic symmetry: - - >>> o = damask.Orientation(lattice='cubic') - >>> o.IPF_color([0,0,1]) - array([1., 0., 0.]) - - """ - if np.array(vector).shape[-1] != 3: - raise ValueError('input is not a field of three-dimensional vectors') - - vector_ = self.to_SST(vector,proper) if in_SST else \ - self @ np.broadcast_to(vector,self.shape+(3,)) - - if self.basis is None: # direct exit for no symmetry - return np.zeros_like(vector_) - - if proper: - components_proper = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.basis['proper'], vector_.shape+(3,)), - vector_), 12) - components_improper = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.basis['improper'], vector_.shape+(3,)), - vector_), 12) - in_SST = np.all(components_proper >= 0.0,axis=-1) \ - | np.all(components_improper >= 0.0,axis=-1) - components = np.where((in_SST & np.all(components_proper >= 0.0,axis=-1))[...,np.newaxis], - components_proper,components_improper) - else: - components = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.basis['improper'], vector_.shape+(3,)), - np.block([vector_[...,:2],np.abs(vector_[...,2:3])])), 12) - - in_SST = np.all(components >= 0.0,axis=-1) - - with np.errstate(invalid='ignore',divide='ignore'): - rgb = (components/np.linalg.norm(components,axis=-1,keepdims=True))**0.5 # smoothen color ramps - rgb = np.clip(rgb,0.,1.) # clip intensity - rgb /= np.max(rgb,axis=-1,keepdims=True) # normalize to (HS)V = 1 - rgb[np.broadcast_to(~in_SST[...,np.newaxis],rgb.shape)] = 0.0 - - return rgb - - def disorientation(self,other,return_operators=False): """ Calculate disorientation between myself and given other orientation. @@ -874,6 +692,114 @@ class Orientation(Rotation): ) + def in_SST(self,vector,proper=False): + """ + Check whether given crystal frame vector falls into standard stereographic triangle of own symmetry. + + Parameters + ---------- + vector : numpy.ndarray of shape (...,3) + Vector to check. + proper : bool, optional + Consider only vectors with z >= 0, hence combine two neighboring SSTs. + Defaults to False. + + Returns + ------- + in : numpy.ndarray of shape (...) + Boolean array indicating whether vector falls into SST. + + """ + if not isinstance(vector,np.ndarray) or vector.shape[-1] != 3: + raise ValueError('input is not a field of three-dimensional vectors') + + if self.basis is None: # direct exit for no symmetry + return np.ones_like(vector[...,0],bool) + + if proper: + components_proper = np.around(np.einsum('...ji,...i', + np.broadcast_to(self.basis['proper'], vector.shape+(3,)), + vector), 12) + components_improper = np.around(np.einsum('...ji,...i', + np.broadcast_to(self.basis['improper'], vector.shape+(3,)), + vector), 12) + return np.all(components_proper >= 0.0,axis=-1) \ + | np.all(components_improper >= 0.0,axis=-1) + else: + components = np.around(np.einsum('...ji,...i', + np.broadcast_to(self.basis['improper'], vector.shape+(3,)), + np.block([vector[...,:2],np.abs(vector[...,2:3])])), 12) + + return np.all(components >= 0.0,axis=-1) + + + def IPF_color(self,vector,in_SST=True,proper=False): + """ + Map vector to RGB color within standard stereographic triangle of own symmetry. + + Parameters + ---------- + vector : numpy.ndarray of shape (...,3) + Vector to colorize. + in_SST : bool, optional + Consider symmetrically equivalent orientations such that poles are located in SST. + Defaults to True. + proper : bool, optional + Consider only vectors with z >= 0, hence combine two neighboring SSTs (with mirrored colors). + Defaults to False. + + Returns + ------- + rgb : numpy.ndarray of shape (...,3) + RGB array of IPF colors. + + Examples + -------- + Inverse pole figure color of the e_3 direction for a crystal in "Cube" orientation with cubic symmetry: + + >>> o = damask.Orientation(lattice='cubic') + >>> o.IPF_color([0,0,1]) + array([1., 0., 0.]) + + """ + if np.array(vector).shape[-1] != 3: + raise ValueError('input is not a field of three-dimensional vectors') + + vector_ = self.to_SST(vector,proper) if in_SST else \ + self @ np.broadcast_to(vector,self.shape+(3,)) + + if self.basis is None: # direct exit for no symmetry + return np.zeros_like(vector_) + + if proper: + components_proper = np.around(np.einsum('...ji,...i', + np.broadcast_to(self.basis['proper'], vector_.shape+(3,)), + vector_), 12) + components_improper = np.around(np.einsum('...ji,...i', + np.broadcast_to(self.basis['improper'], vector_.shape+(3,)), + vector_), 12) + in_SST = np.all(components_proper >= 0.0,axis=-1) \ + | np.all(components_improper >= 0.0,axis=-1) + components = np.where((in_SST & np.all(components_proper >= 0.0,axis=-1))[...,np.newaxis], + components_proper,components_improper) + else: + components = np.around(np.einsum('...ji,...i', + np.broadcast_to(self.basis['improper'], vector_.shape+(3,)), + np.block([vector_[...,:2],np.abs(vector_[...,2:3])])), 12) + + in_SST = np.all(components >= 0.0,axis=-1) + + with np.errstate(invalid='ignore',divide='ignore'): + rgb = (components/np.linalg.norm(components,axis=-1,keepdims=True))**0.5 # smoothen color ramps + rgb = np.clip(rgb,0.,1.) # clip intensity + rgb /= np.max(rgb,axis=-1,keepdims=True) # normalize to (HS)V = 1 + rgb[np.broadcast_to(~in_SST[...,np.newaxis],rgb.shape)] = 0.0 + + return rgb + + + # functions that require lattice, not just family + def to_pole(self,*,uvw=None,hkl=None,with_symmetry=False): """ Calculate lab frame vector along lattice direction [uvw] or plane normal (hkl). @@ -938,3 +864,78 @@ class Orientation(Rotation): return ~self.broadcast_to( self.shape+P.shape[:-2],mode='right') \ @ np.broadcast_to(P,self.shape+P.shape) + + + def relation_operations(self,model,return_lattice=False): + """ + Crystallographic orientation relationships for phase transformations. + + Parameters + ---------- + model : str + Name of orientation relationship. + return_lattice : bool, optional + Return the target lattice in addition. + + Returns + ------- + operations : Rotations + Rotations characterizing the orientation relationship. + + References + ---------- + S. Morito et al., Journal of Alloys and Compounds 577:s587-s592, 2013 + https://doi.org/10.1016/j.jallcom.2012.02.004 + + K. Kitahara et al., Acta Materialia 54(5):1279-1288, 2006 + https://doi.org/10.1016/j.actamat.2005.11.001 + + Y. He et al., Journal of Applied Crystallography 39:72-81, 2006 + https://doi.org/10.1107/S0021889805038276 + + H. Kitahara et al., Materials Characterization 54(4-5):378-386, 2005 + https://doi.org/10.1016/j.matchar.2004.12.015 + + Y. He et al., Acta Materialia 53(4):1179-1190, 2005 + https://doi.org/10.1016/j.actamat.2004.11.021 + + """ + if model not in self.orientation_relationships: + raise KeyError(f'unknown orientation relationship "{model}"') + r = self.orientation_relationships[model] + + sl = self.lattice + ol = (set(r)-{sl}).pop() + m = r[sl] + o = r[ol] + + p_,_p = np.zeros(m.shape[:-1]+(3,)),np.zeros(o.shape[:-1]+(3,)) + p_[...,0,:] = m[...,0,:] if m.shape[-1] == 3 else util.Bravais_to_Miller(uvtw=m[...,0,0:4]) + p_[...,1,:] = m[...,1,:] if m.shape[-1] == 3 else util.Bravais_to_Miller(hkil=m[...,1,0:4]) + _p[...,0,:] = o[...,0,:] if o.shape[-1] == 3 else util.Bravais_to_Miller(uvtw=o[...,0,0:4]) + _p[...,1,:] = o[...,1,:] if o.shape[-1] == 3 else util.Bravais_to_Miller(hkil=o[...,1,0:4]) + + return (Rotation.from_parallel(p_,_p),ol) \ + if return_lattice else \ + Rotation.from_parallel(p_,_p) + + + def related(self,model): + """ + Orientations derived from the given relationship. + + One dimension (length according to number of related orientations) + is added to the left of the Rotation array. + + """ + o,lattice = self.relation_operations(model,return_lattice=True) + target = Orientation(lattice=lattice) + o = o.broadcast_to(o.shape+self.shape,mode='right') + return self.copy(rotation=o*Rotation(self.quaternion).broadcast_to(o.shape,mode='left'), + lattice=lattice, + b = self.b if target.ratio['b'] is None else self.a*target.ratio['b'], + c = self.c if target.ratio['c'] is None else self.a*target.ratio['c'], + alpha = None if 'alpha' in target.immutable else self.alpha, + beta = None if 'beta' in target.immutable else self.beta, + gamma = None if 'gamma' in target.immutable else self.gamma, + ) From 3b150ddbea0aabc2d748a3efa06aeff49fb1e393 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 3 Jun 2021 09:15:12 +0200 Subject: [PATCH 06/43] simplified/separated --- python/damask/_lattice.py | 59 ++++++++++++++++++++++++++++++++--- python/damask/_orientation.py | 58 ++-------------------------------- 2 files changed, 57 insertions(+), 60 deletions(-) diff --git a/python/damask/_lattice.py b/python/damask/_lattice.py index c9303968c..de72e7a50 100644 --- a/python/damask/_lattice.py +++ b/python/damask/_lattice.py @@ -2,6 +2,7 @@ import numpy as np from . import util from . import LatticeFamily +from . import Rotation lattice_symmetries = { 'aP': 'triclinic', @@ -205,6 +206,56 @@ class Lattice(LatticeFamily): 'plane': master[:,3:6]} + def relation_operations(self,model): + """ + Crystallographic orientation relationships for phase transformations. + + Parameters + ---------- + model : str + Name of orientation relationship. + + Returns + ------- + operations : (string, damask.Rotation) + Rotations characterizing the orientation relationship. + + References + ---------- + S. Morito et al., Journal of Alloys and Compounds 577:s587-s592, 2013 + https://doi.org/10.1016/j.jallcom.2012.02.004 + + K. Kitahara et al., Acta Materialia 54(5):1279-1288, 2006 + https://doi.org/10.1016/j.actamat.2005.11.001 + + Y. He et al., Journal of Applied Crystallography 39:72-81, 2006 + https://doi.org/10.1107/S0021889805038276 + + H. Kitahara et al., Materials Characterization 54(4-5):378-386, 2005 + https://doi.org/10.1016/j.matchar.2004.12.015 + + Y. He et al., Acta Materialia 53(4):1179-1190, 2005 + https://doi.org/10.1016/j.actamat.2004.11.021 + + """ + if model not in self.orientation_relationships: + raise KeyError(f'unknown orientation relationship "{model}"') + r = self.orientation_relationships[model] + + sl = self.lattice + ol = (set(r)-{sl}).pop() + m = r[sl] + o = r[ol] + + p_,_p = np.zeros(m.shape[:-1]+(3,)),np.zeros(o.shape[:-1]+(3,)) + p_[...,0,:] = m[...,0,:] if m.shape[-1] == 3 else util.Bravais_to_Miller(uvtw=m[...,0,0:4]) + p_[...,1,:] = m[...,1,:] if m.shape[-1] == 3 else util.Bravais_to_Miller(hkil=m[...,1,0:4]) + _p[...,0,:] = o[...,0,:] if o.shape[-1] == 3 else util.Bravais_to_Miller(uvtw=o[...,0,0:4]) + _p[...,1,:] = o[...,1,:] if o.shape[-1] == 3 else util.Bravais_to_Miller(hkil=o[...,1,0:4]) + + return (ol,Rotation.from_parallel(p_,_p)) + + @property def orientation_relationships(self): return {k:v for k,v in self._orientation_relationships.items() if self.lattice in v} @@ -620,12 +671,12 @@ class Lattice(LatticeFamily): [[ -1, 1, -1],[ 1, 1, 0]], [[ 1, 1, 1],[ 1, -1, 0]], [[ 1, 1, -1],[ 1, -1, 0]], - + [[ 1, 1, -1],[ 1, 0, 1]], [[ -1, 1, 1],[ 1, 0, 1]], [[ 1, 1, 1],[ -1, 0, 1]], [[ 1, -1, 1],[ -1, 0, 1]], - + [[ -1, 1, -1],[ 0, 1, 1]], [[ 1, 1, -1],[ 0, 1, 1]], [[ -1, 1, 1],[ 0, -1, 1]], @@ -636,12 +687,12 @@ class Lattice(LatticeFamily): [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], - + [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], - + [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 0810c6624..bcadea06c 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -167,7 +167,7 @@ class Orientation(Rotation): self.to_frame = l.to_frame self.kinematics = l.kinematics - self.orientation_relationships = l.orientation_relationships + self.relation_operations = l.relation_operations else: raise KeyError(f'no valid family or lattice') @@ -866,60 +866,6 @@ class Orientation(Rotation): @ np.broadcast_to(P,self.shape+P.shape) - def relation_operations(self,model,return_lattice=False): - """ - Crystallographic orientation relationships for phase transformations. - - Parameters - ---------- - model : str - Name of orientation relationship. - return_lattice : bool, optional - Return the target lattice in addition. - - Returns - ------- - operations : Rotations - Rotations characterizing the orientation relationship. - - References - ---------- - S. Morito et al., Journal of Alloys and Compounds 577:s587-s592, 2013 - https://doi.org/10.1016/j.jallcom.2012.02.004 - - K. Kitahara et al., Acta Materialia 54(5):1279-1288, 2006 - https://doi.org/10.1016/j.actamat.2005.11.001 - - Y. He et al., Journal of Applied Crystallography 39:72-81, 2006 - https://doi.org/10.1107/S0021889805038276 - - H. Kitahara et al., Materials Characterization 54(4-5):378-386, 2005 - https://doi.org/10.1016/j.matchar.2004.12.015 - - Y. He et al., Acta Materialia 53(4):1179-1190, 2005 - https://doi.org/10.1016/j.actamat.2004.11.021 - - """ - if model not in self.orientation_relationships: - raise KeyError(f'unknown orientation relationship "{model}"') - r = self.orientation_relationships[model] - - sl = self.lattice - ol = (set(r)-{sl}).pop() - m = r[sl] - o = r[ol] - - p_,_p = np.zeros(m.shape[:-1]+(3,)),np.zeros(o.shape[:-1]+(3,)) - p_[...,0,:] = m[...,0,:] if m.shape[-1] == 3 else util.Bravais_to_Miller(uvtw=m[...,0,0:4]) - p_[...,1,:] = m[...,1,:] if m.shape[-1] == 3 else util.Bravais_to_Miller(hkil=m[...,1,0:4]) - _p[...,0,:] = o[...,0,:] if o.shape[-1] == 3 else util.Bravais_to_Miller(uvtw=o[...,0,0:4]) - _p[...,1,:] = o[...,1,:] if o.shape[-1] == 3 else util.Bravais_to_Miller(hkil=o[...,1,0:4]) - - return (Rotation.from_parallel(p_,_p),ol) \ - if return_lattice else \ - Rotation.from_parallel(p_,_p) - - def related(self,model): """ Orientations derived from the given relationship. @@ -928,7 +874,7 @@ class Orientation(Rotation): is added to the left of the Rotation array. """ - o,lattice = self.relation_operations(model,return_lattice=True) + lattice,o = self.relation_operations(model) target = Orientation(lattice=lattice) o = o.broadcast_to(o.shape+self.shape,mode='right') return self.copy(rotation=o*Rotation(self.quaternion).broadcast_to(o.shape,mode='left'), From 03d3f362e670357bcb1c56d7b8967f2804cfc386 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 3 Jun 2021 09:31:24 +0200 Subject: [PATCH 07/43] equality checks note: doing this type of comparison means: - LatticeFamily('cubic') == Lattice('cF') - Lattice('cF') != LatticeFamily('cubic') we have the same behavior for comparison between Orientation and Rotation --- python/damask/_lattice.py | 12 ++++++++++++ python/damask/_lattice_family.py | 13 +++++++++++++ python/damask/_orientation.py | 12 ++++++------ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/python/damask/_lattice.py b/python/damask/_lattice.py index de72e7a50..1aa3d6e65 100644 --- a/python/damask/_lattice.py +++ b/python/damask/_lattice.py @@ -99,6 +99,18 @@ class Lattice(LatticeFamily): raise ValueError ('Each lattice angle must be less than sum of others') + def __eq__(self,other): + """ + Equal to other. + + Parameters + ---------- + other : Lattice + Lattice to check for equality. + + """ + return self.lattice == other.lattice and self.parameters == other.parameters + @property def parameters(self): """Return lattice parameters a, b, c, alpha, beta, gamma.""" diff --git a/python/damask/_lattice_family.py b/python/damask/_lattice_family.py index d6288cff0..24d91490d 100644 --- a/python/damask/_lattice_family.py +++ b/python/damask/_lattice_family.py @@ -19,6 +19,19 @@ class LatticeFamily(): self.family = family + def __eq__(self,other): + """ + Equal to other. + + Parameters + ---------- + other : LatticeFamily + Lattice family to check for equality. + + """ + return self.family == other.family + + @property def symmetry_operations(self): """Symmetry operations as Rotations.""" diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index bcadea06c..a3aa44564 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -136,7 +136,7 @@ class Orientation(Rotation): self.family = family self.lattice = None - l = LatticeFamily(self.family) # noqa + self.structure = l = LatticeFamily(self.family) # noqa self.immutable = l.immutable self.basis = l.basis self.symmetry_operations = l.symmetry_operations @@ -149,7 +149,7 @@ class Orientation(Rotation): self.family = lattice_symmetries[lattice] self.lattice = lattice - l = Lattice(self.lattice, a,b,c, alpha,beta,gamma, degrees) # noqa + self.structure = l = Lattice(self.lattice, a,b,c, alpha,beta,gamma, degrees) # noqa self.immutable = l.immutable self.basis = l.basis self.symmetry_operations = l.symmetry_operations @@ -210,8 +210,8 @@ class Orientation(Rotation): Orientation to check for equality. """ - matching_type = all([hasattr(other,attr) and getattr(self,attr) == getattr(other,attr) - for attr in ['family','lattice','parameters']]) + matching_type = self.structure == other.structure if hasattr(other,'structure') else \ + False return np.logical_and(matching_type,super(self.__class__,self.reduced).__eq__(other.reduced)) def __ne__(self,other): @@ -248,8 +248,8 @@ class Orientation(Rotation): Mask indicating where corresponding orientations are close. """ - matching_type = all([hasattr(other,attr) and getattr(self,attr) == getattr(other,attr) - for attr in ['family','lattice','parameters']]) + matching_type = self.structure == other.structure if hasattr(other,'structure') else \ + False return np.logical_and(matching_type,super(self.__class__,self.reduced).isclose(other.reduced)) From 4701eea10f450ea61627eb5b4118a5f1060b75ea Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 3 Jun 2021 10:02:49 +0200 Subject: [PATCH 08/43] decoupling Orientation and Lattice/LatticeFamily --- python/damask/_orientation.py | 19 +++++++------------ python/tests/test_Orientation.py | 4 ++-- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index a3aa44564..d661dd475 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -32,9 +32,9 @@ lattice_symmetries = { _parameter_doc = \ """ - family : {'triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic'} + family : {'triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic'} Crystal family. Mutual exclusive with 'lattice' parameter. - lattice : {'aP', 'mP', 'mS', 'oP', 'oS', 'oI', 'oF', 'tP', 'tI', 'hP', 'cP', 'cI', 'cF'}. + lattice : {'aP', 'mP', 'mS', 'oP', 'oS', 'oI', 'oF', 'tP', 'tI', 'hP', 'cP', 'cI', 'cF'}. Bravais lattice in Pearson notation. Mutual exclusive with 'family' parameter. a : float, optional @@ -139,7 +139,6 @@ class Orientation(Rotation): self.structure = l = LatticeFamily(self.family) # noqa self.immutable = l.immutable self.basis = l.basis - self.symmetry_operations = l.symmetry_operations self.a = self.b = self.c = None self.alpha = self.beta = self.gamma = None @@ -152,7 +151,6 @@ class Orientation(Rotation): self.structure = l = Lattice(self.lattice, a,b,c, alpha,beta,gamma, degrees) # noqa self.immutable = l.immutable self.basis = l.basis - self.symmetry_operations = l.symmetry_operations self.a = l.a self.b = l.b @@ -178,11 +176,6 @@ class Orientation(Rotation): + ([f'Crystal family {self.family}']) + [super().__repr__()]) - @property - def parameters(self): - """Return lattice parameters a, b, c, alpha, beta, gamma.""" - return (self.a,self.b,self.c,self.alpha,self.beta,self.gamma) - def __copy__(self,**kwargs): """Create deep copy.""" @@ -437,7 +430,8 @@ class Orientation(Rotation): is added to the left of the Rotation array. """ - o = self.symmetry_operations.broadcast_to(self.symmetry_operations.shape+self.shape,mode='right') + sym_ops = self.structure.symmetry_operations + o = sym_ops.broadcast_to(sym_ops.shape+self.shape,mode='right') return self.copy(rotation=o*Rotation(self.quaternion).broadcast_to(o.shape,mode='left')) @@ -817,11 +811,12 @@ class Orientation(Rotation): Lab frame vector (or vectors if with_symmetry) along [uvw] direction or (hkl) plane normal. """ + sym_ops = self.structure.symmetry_operations # ToDo: simplify 'with_symmetry' v = self.to_frame(uvw=uvw,hkl=hkl) if with_symmetry: - v = self.symmetry_operations.broadcast_to(self.symmetry_operations.shape+v.shape[:-1],mode='right') \ - @ np.broadcast_to(v,self.symmetry_operations.shape+v.shape) + v = sym_ops.broadcast_to(sym_ops.shape+v.shape[:-1],mode='right') \ + @ np.broadcast_to(v,sym_ops.shape+v.shape) return ~(self if self.shape+v.shape[:-1] == () else self.broadcast_to(self.shape+v.shape[:-1],mode='right')) \ @ np.broadcast_to(v,self.shape+v.shape) diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index a1302bcb7..ced7022f1 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -75,7 +75,7 @@ class TestOrientation: ]) def test_invalid_init(self,kwargs): with pytest.raises(ValueError): - Orientation(**kwargs).parameters # noqa + Orientation(**kwargs) @pytest.mark.parametrize('kwargs',[ dict(lattice='aP',a=1.0,b=1.1,c=1.2,alpha=np.pi/4,beta=np.pi/3,gamma=np.pi/2), @@ -436,7 +436,7 @@ class TestOrientation: a=a,b=b,c=c, alpha=alpha,beta=beta,gamma=gamma) assert o.to_pole(**{kw:vector,'with_symmetry':with_symmetry}).shape \ - == o.shape + (o.symmetry_operations.shape if with_symmetry else ()) + vector.shape + == o.shape + (o.structure.symmetry_operations.shape if with_symmetry else ()) + vector.shape @pytest.mark.parametrize('lattice',['hP','cI','cF']) def test_Schmid(self,update,ref_path,lattice): From 3c8d06b6f19e4e122878c2e688c2422984efd907 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 3 Jun 2021 10:18:27 +0200 Subject: [PATCH 09/43] encapsulating all lattice(-family) related data --- python/damask/_orientation.py | 94 +++++++++++--------------------- python/tests/test_Orientation.py | 2 +- 2 files changed, 33 insertions(+), 63 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index d661dd475..9edacd790 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -1,4 +1,5 @@ import inspect +import copy import numpy as np @@ -135,37 +136,11 @@ class Orientation(Rotation): if family in set(lattice_symmetries.values()) and lattice is None: self.family = family self.lattice = None - - self.structure = l = LatticeFamily(self.family) # noqa - self.immutable = l.immutable - self.basis = l.basis - - self.a = self.b = self.c = None - self.alpha = self.beta = self.gamma = None - + self.structure = LatticeFamily(self.family) elif lattice in lattice_symmetries: - self.family = lattice_symmetries[lattice] self.lattice = lattice - - self.structure = l = Lattice(self.lattice, a,b,c, alpha,beta,gamma, degrees) # noqa - self.immutable = l.immutable - self.basis = l.basis - - self.a = l.a - self.b = l.b - self.c = l.c - - self.alpha = l.alpha - self.beta = l.beta - self.gamma = l.gamma - - self.ratio = l.ratio - - self.to_frame = l.to_frame - - self.kinematics = l.kinematics - self.relation_operations = l.relation_operations + self.structure = l = Lattice(self.lattice, a,b,c, alpha,beta,gamma, degrees) else: raise KeyError(f'no valid family or lattice') @@ -177,18 +152,12 @@ class Orientation(Rotation): + [super().__repr__()]) - def __copy__(self,**kwargs): + def __copy__(self,rotation=None): """Create deep copy.""" - return self.__class__(rotation= kwargs['rotation'] if 'rotation' in kwargs else self.quaternion, - family = kwargs['family'] if 'family' in kwargs else self.family, - lattice = kwargs['lattice'] if 'lattice' in kwargs else self.lattice, - a = kwargs['a'] if 'a' in kwargs else self.a, - b = kwargs['b'] if 'b' in kwargs else self.b, - c = kwargs['c'] if 'c' in kwargs else self.c, - alpha = kwargs['alpha'] if 'alpha' in kwargs else self.alpha, - beta = kwargs['beta'] if 'beta' in kwargs else self.beta, - gamma = kwargs['gamma'] if 'gamma' in kwargs else self.gamma, - ) + dup = copy.deepcopy(self) + if rotation is not None: + dup.quaternion = Orientation(rotation,family='cubic').quaternion + return dup copy = __copy__ @@ -415,8 +384,8 @@ class Orientation(Rotation): """ o = cls(**kwargs) - x = o.to_frame(uvw=uvw) - z = o.to_frame(hkl=hkl) + x = o.structure.to_frame(uvw=uvw) + z = o.structure.to_frame(hkl=hkl) om = np.stack([x,np.cross(z,x),z],axis=-2) return o.copy(rotation=Rotation.from_matrix(tensor.transpose(om/np.linalg.norm(om,axis=-1,keepdims=True)))) @@ -707,21 +676,21 @@ class Orientation(Rotation): if not isinstance(vector,np.ndarray) or vector.shape[-1] != 3: raise ValueError('input is not a field of three-dimensional vectors') - if self.basis is None: # direct exit for no symmetry + if self.structure.basis is None: # direct exit for no symmetry return np.ones_like(vector[...,0],bool) if proper: components_proper = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.basis['proper'], vector.shape+(3,)), + np.broadcast_to(self.structure.basis['proper'], vector.shape+(3,)), vector), 12) components_improper = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.basis['improper'], vector.shape+(3,)), + np.broadcast_to(self.structure.basis['improper'], vector.shape+(3,)), vector), 12) return np.all(components_proper >= 0.0,axis=-1) \ | np.all(components_improper >= 0.0,axis=-1) else: components = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.basis['improper'], vector.shape+(3,)), + np.broadcast_to(self.structure.basis['improper'], vector.shape+(3,)), np.block([vector[...,:2],np.abs(vector[...,2:3])])), 12) return np.all(components >= 0.0,axis=-1) @@ -762,15 +731,15 @@ class Orientation(Rotation): vector_ = self.to_SST(vector,proper) if in_SST else \ self @ np.broadcast_to(vector,self.shape+(3,)) - if self.basis is None: # direct exit for no symmetry + if self.structure.basis is None: # direct exit for no symmetry return np.zeros_like(vector_) if proper: components_proper = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.basis['proper'], vector_.shape+(3,)), + np.broadcast_to(self.structure.basis['proper'], vector_.shape+(3,)), vector_), 12) components_improper = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.basis['improper'], vector_.shape+(3,)), + np.broadcast_to(self.structure.basis['improper'], vector_.shape+(3,)), vector_), 12) in_SST = np.all(components_proper >= 0.0,axis=-1) \ | np.all(components_improper >= 0.0,axis=-1) @@ -778,7 +747,7 @@ class Orientation(Rotation): components_proper,components_improper) else: components = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.basis['improper'], vector_.shape+(3,)), + np.broadcast_to(self.structure.basis['improper'], vector_.shape+(3,)), np.block([vector_[...,:2],np.abs(vector_[...,2:3])])), 12) in_SST = np.all(components >= 0.0,axis=-1) @@ -813,7 +782,7 @@ class Orientation(Rotation): """ sym_ops = self.structure.symmetry_operations # ToDo: simplify 'with_symmetry' - v = self.to_frame(uvw=uvw,hkl=hkl) + v = self.structure.to_frame(uvw=uvw,hkl=hkl) if with_symmetry: v = sym_ops.broadcast_to(sym_ops.shape+v.shape[:-1],mode='right') \ @ np.broadcast_to(v,sym_ops.shape+v.shape) @@ -850,8 +819,8 @@ class Orientation(Rotation): """ try: - d = self.to_frame(uvw=self.kinematics(mode)['direction']) - p = self.to_frame(hkl=self.kinematics(mode)['plane']) + d = self.structure.to_frame(uvw=self.structure.kinematics(mode)['direction']) + p = self.structure.to_frame(hkl=self.structure.kinematics(mode)['plane']) except KeyError: raise (f'"{mode}" not defined for lattice "{self.lattice}"') P = np.einsum('...i,...j',d/np.linalg.norm(d,axis=-1,keepdims=True), @@ -869,14 +838,15 @@ class Orientation(Rotation): is added to the left of the Rotation array. """ - lattice,o = self.relation_operations(model) - target = Orientation(lattice=lattice) + lattice,o = self.structure.relation_operations(model) + target = Lattice(lattice=lattice) + struct = self.structure o = o.broadcast_to(o.shape+self.shape,mode='right') - return self.copy(rotation=o*Rotation(self.quaternion).broadcast_to(o.shape,mode='left'), - lattice=lattice, - b = self.b if target.ratio['b'] is None else self.a*target.ratio['b'], - c = self.c if target.ratio['c'] is None else self.a*target.ratio['c'], - alpha = None if 'alpha' in target.immutable else self.alpha, - beta = None if 'beta' in target.immutable else self.beta, - gamma = None if 'gamma' in target.immutable else self.gamma, - ) + return Orientation(rotation=o*Rotation(self.quaternion).broadcast_to(o.shape,mode='left'), + lattice=lattice, + b = struct.b if target.ratio['b'] is None else struct.a*target.ratio['b'], + c = struct.c if target.ratio['c'] is None else struct.a*target.ratio['c'], + alpha = None if 'alpha' in target.immutable else struct.alpha, + beta = None if 'beta' in target.immutable else struct.beta, + gamma = None if 'gamma' in target.immutable else struct.gamma, + ) diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index ced7022f1..5317ada19 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -409,7 +409,7 @@ class TestOrientation: **dict(zip(['a','b','c'],lengths)), **dict(zip(['alpha','beta','gamma'],np.arccos(cosines))), ) - assert np.allclose(o.to_frame(uvw=np.eye(3)),basis), 'Lattice basis disagrees with initialization' + assert np.allclose(o.structure.to_frame(uvw=np.eye(3)),basis), 'Lattice basis disagrees with initialization' @pytest.mark.parametrize('lattice,a,b,c,alpha,beta,gamma', From 0722f4f7543be36704e582fee4eed9a72bc34d30 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 3 Jun 2021 11:20:38 +0200 Subject: [PATCH 10/43] disable if not possible --- python/damask/_orientation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 9edacd790..ff419117c 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -137,10 +137,11 @@ class Orientation(Rotation): self.family = family self.lattice = None self.structure = LatticeFamily(self.family) + self.related = self.Schmid = self.to_pole = None elif lattice in lattice_symmetries: self.family = lattice_symmetries[lattice] self.lattice = lattice - self.structure = l = Lattice(self.lattice, a,b,c, alpha,beta,gamma, degrees) + self.structure = Lattice(self.lattice, a,b,c, alpha,beta,gamma, degrees) else: raise KeyError(f'no valid family or lattice') From 41e5f0c06ce4ee33836c47b74d82d663ff44f0b1 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 6 Jun 2021 19:49:29 +0200 Subject: [PATCH 11/43] re-introduced multiple inheritance --- python/damask/_lattice.py | 77 +++++++++++++++++--------------- python/damask/_orientation.py | 72 +++++++++++++---------------- python/tests/test_Lattice.py | 4 +- python/tests/test_Orientation.py | 4 +- 4 files changed, 76 insertions(+), 81 deletions(-) diff --git a/python/damask/_lattice.py b/python/damask/_lattice.py index 1aa3d6e65..51e29c282 100644 --- a/python/damask/_lattice.py +++ b/python/damask/_lattice.py @@ -29,7 +29,8 @@ lattice_symmetries = { class Lattice(LatticeFamily): """Lattice.""" - def __init__(self, + def __init__(self,*, + family = None, lattice = None, a = None,b = None,c = None, alpha = None,beta = None,gamma = None, @@ -57,46 +58,50 @@ class Lattice(LatticeFamily): Angles are given in degrees. Defaults to False. """ - super().__init__(lattice_symmetries[lattice]) + super().__init__(family = lattice_symmetries[lattice] if family is None else family) + self.lattice = lattice + if self.lattice is not None: + self.a = 1 if a is None else a + self.b = b + self.c = c + self.a = float(self.a) if self.a is not None else \ + (self.b / self.ratio['b'] if self.b is not None and self.ratio['b'] is not None else + self.c / self.ratio['c'] if self.c is not None and self.ratio['c'] is not None else None) + self.b = float(self.b) if self.b is not None else \ + (self.a * self.ratio['b'] if self.a is not None and self.ratio['b'] is not None else + self.c / self.ratio['c'] * self.ratio['b'] + if self.c is not None and self.ratio['b'] is not None and self.ratio['c'] is not None else None) + self.c = float(self.c) if self.c is not None else \ + (self.a * self.ratio['c'] if self.a is not None and self.ratio['c'] is not None else + self.b / self.ratio['b'] * self.ratio['c'] + if self.c is not None and self.ratio['b'] is not None and self.ratio['c'] is not None else None) - self.a = 1 if a is None else a - self.b = b - self.c = c - self.a = float(self.a) if self.a is not None else \ - (self.b / self.ratio['b'] if self.b is not None and self.ratio['b'] is not None else - self.c / self.ratio['c'] if self.c is not None and self.ratio['c'] is not None else None) - self.b = float(self.b) if self.b is not None else \ - (self.a * self.ratio['b'] if self.a is not None and self.ratio['b'] is not None else - self.c / self.ratio['c'] * self.ratio['b'] - if self.c is not None and self.ratio['b'] is not None and self.ratio['c'] is not None else None) - self.c = float(self.c) if self.c is not None else \ - (self.a * self.ratio['c'] if self.a is not None and self.ratio['c'] is not None else - self.b / self.ratio['b'] * self.ratio['c'] - if self.c is not None and self.ratio['b'] is not None and self.ratio['c'] is not None else None) + self.alpha = np.radians(alpha) if degrees and alpha is not None else alpha + self.beta = np.radians(beta) if degrees and beta is not None else beta + self.gamma = np.radians(gamma) if degrees and gamma is not None else gamma + if self.alpha is None and 'alpha' in self.immutable: self.alpha = self.immutable['alpha'] + if self.beta is None and 'beta' in self.immutable: self.beta = self.immutable['beta'] + if self.gamma is None and 'gamma' in self.immutable: self.gamma = self.immutable['gamma'] - self.alpha = np.radians(alpha) if degrees and alpha is not None else alpha - self.beta = np.radians(beta) if degrees and beta is not None else beta - self.gamma = np.radians(gamma) if degrees and gamma is not None else gamma - if self.alpha is None and 'alpha' in self.immutable: self.alpha = self.immutable['alpha'] - if self.beta is None and 'beta' in self.immutable: self.beta = self.immutable['beta'] - if self.gamma is None and 'gamma' in self.immutable: self.gamma = self.immutable['gamma'] + if \ + (self.a is None) \ + or (self.b is None or ('b' in self.immutable and self.b != self.immutable['b'] * self.a)) \ + or (self.c is None or ('c' in self.immutable and self.c != self.immutable['c'] * self.b)) \ + or (self.alpha is None or ('alpha' in self.immutable and self.alpha != self.immutable['alpha'])) \ + or (self.beta is None or ( 'beta' in self.immutable and self.beta != self.immutable['beta'])) \ + or (self.gamma is None or ('gamma' in self.immutable and self.gamma != self.immutable['gamma'])): + raise ValueError (f'Incompatible parameters {self.parameters} for crystal family {self.family}') - if \ - (self.a is None) \ - or (self.b is None or ('b' in self.immutable and self.b != self.immutable['b'] * self.a)) \ - or (self.c is None or ('c' in self.immutable and self.c != self.immutable['c'] * self.b)) \ - or (self.alpha is None or ('alpha' in self.immutable and self.alpha != self.immutable['alpha'])) \ - or (self.beta is None or ( 'beta' in self.immutable and self.beta != self.immutable['beta'])) \ - or (self.gamma is None or ('gamma' in self.immutable and self.gamma != self.immutable['gamma'])): - raise ValueError (f'Incompatible parameters {self.parameters} for crystal family {self.family}') - - if np.any(np.array([self.alpha,self.beta,self.gamma]) <= 0): - raise ValueError ('Lattice angles must be positive') - if np.any([np.roll([self.alpha,self.beta,self.gamma],r)[0] - > np.sum(np.roll([self.alpha,self.beta,self.gamma],r)[1:]) for r in range(3)]): - raise ValueError ('Each lattice angle must be less than sum of others') + if np.any(np.array([self.alpha,self.beta,self.gamma]) <= 0): + raise ValueError ('Lattice angles must be positive') + if np.any([np.roll([self.alpha,self.beta,self.gamma],r)[0] + > np.sum(np.roll([self.alpha,self.beta,self.gamma],r)[1:]) for r in range(3)]): + raise ValueError ('Each lattice angle must be less than sum of others') + else: + self.a = self.b = self.c = None + self.alpha = self.beta = self.gamma = None def __eq__(self,other): diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index ff419117c..c26c0b5f9 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -4,7 +4,6 @@ import copy import numpy as np from . import Rotation -from . import LatticeFamily from . import Lattice from . import util from . import tensor @@ -56,7 +55,7 @@ _parameter_doc = \ """ -class Orientation(Rotation): +class Orientation(Rotation,Lattice): """ Representation of crystallographic orientation as combination of rotation and either crystal family or Bravais lattice. @@ -131,19 +130,9 @@ class Orientation(Rotation): Defaults to no rotation. """ - super().__init__(rotation) - - if family in set(lattice_symmetries.values()) and lattice is None: - self.family = family - self.lattice = None - self.structure = LatticeFamily(self.family) - self.related = self.Schmid = self.to_pole = None - elif lattice in lattice_symmetries: - self.family = lattice_symmetries[lattice] - self.lattice = lattice - self.structure = Lattice(self.lattice, a,b,c, alpha,beta,gamma, degrees) - else: - raise KeyError(f'no valid family or lattice') + Rotation.__init__(self,rotation) + Lattice.__init__(self,family=family, lattice=lattice, + a=a,b=b,c=c, alpha=alpha,beta=beta,gamma=gamma, degrees=degrees) def __repr__(self): @@ -173,8 +162,9 @@ class Orientation(Rotation): Orientation to check for equality. """ - matching_type = self.structure == other.structure if hasattr(other,'structure') else \ - False + matching_type = self.family == other.family and \ + self.lattice == other.lattice and \ + self.parameters == other.parameters return np.logical_and(matching_type,super(self.__class__,self.reduced).__eq__(other.reduced)) def __ne__(self,other): @@ -211,8 +201,9 @@ class Orientation(Rotation): Mask indicating where corresponding orientations are close. """ - matching_type = self.structure == other.structure if hasattr(other,'structure') else \ - False + matching_type = self.family == other.family and \ + self.lattice == other.lattice and \ + self.parameters == other.parameters return np.logical_and(matching_type,super(self.__class__,self.reduced).isclose(other.reduced)) @@ -385,8 +376,8 @@ class Orientation(Rotation): """ o = cls(**kwargs) - x = o.structure.to_frame(uvw=uvw) - z = o.structure.to_frame(hkl=hkl) + x = o.to_frame(uvw=uvw) + z = o.to_frame(hkl=hkl) om = np.stack([x,np.cross(z,x),z],axis=-2) return o.copy(rotation=Rotation.from_matrix(tensor.transpose(om/np.linalg.norm(om,axis=-1,keepdims=True)))) @@ -400,7 +391,7 @@ class Orientation(Rotation): is added to the left of the Rotation array. """ - sym_ops = self.structure.symmetry_operations + sym_ops = self.symmetry_operations o = sym_ops.broadcast_to(sym_ops.shape+self.shape,mode='right') return self.copy(rotation=o*Rotation(self.quaternion).broadcast_to(o.shape,mode='left')) @@ -677,21 +668,21 @@ class Orientation(Rotation): if not isinstance(vector,np.ndarray) or vector.shape[-1] != 3: raise ValueError('input is not a field of three-dimensional vectors') - if self.structure.basis is None: # direct exit for no symmetry + if self.basis is None: # direct exit for no symmetry return np.ones_like(vector[...,0],bool) if proper: components_proper = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.structure.basis['proper'], vector.shape+(3,)), + np.broadcast_to(self.basis['proper'], vector.shape+(3,)), vector), 12) components_improper = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.structure.basis['improper'], vector.shape+(3,)), + np.broadcast_to(self.basis['improper'], vector.shape+(3,)), vector), 12) return np.all(components_proper >= 0.0,axis=-1) \ | np.all(components_improper >= 0.0,axis=-1) else: components = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.structure.basis['improper'], vector.shape+(3,)), + np.broadcast_to(self.basis['improper'], vector.shape+(3,)), np.block([vector[...,:2],np.abs(vector[...,2:3])])), 12) return np.all(components >= 0.0,axis=-1) @@ -732,15 +723,15 @@ class Orientation(Rotation): vector_ = self.to_SST(vector,proper) if in_SST else \ self @ np.broadcast_to(vector,self.shape+(3,)) - if self.structure.basis is None: # direct exit for no symmetry + if self.basis is None: # direct exit for no symmetry return np.zeros_like(vector_) if proper: components_proper = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.structure.basis['proper'], vector_.shape+(3,)), + np.broadcast_to(self.basis['proper'], vector_.shape+(3,)), vector_), 12) components_improper = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.structure.basis['improper'], vector_.shape+(3,)), + np.broadcast_to(self.basis['improper'], vector_.shape+(3,)), vector_), 12) in_SST = np.all(components_proper >= 0.0,axis=-1) \ | np.all(components_improper >= 0.0,axis=-1) @@ -748,7 +739,7 @@ class Orientation(Rotation): components_proper,components_improper) else: components = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.structure.basis['improper'], vector_.shape+(3,)), + np.broadcast_to(self .basis['improper'], vector_.shape+(3,)), np.block([vector_[...,:2],np.abs(vector_[...,2:3])])), 12) in_SST = np.all(components >= 0.0,axis=-1) @@ -781,9 +772,9 @@ class Orientation(Rotation): Lab frame vector (or vectors if with_symmetry) along [uvw] direction or (hkl) plane normal. """ - sym_ops = self.structure.symmetry_operations + sym_ops = self.symmetry_operations # ToDo: simplify 'with_symmetry' - v = self.structure.to_frame(uvw=uvw,hkl=hkl) + v = self.to_frame(uvw=uvw,hkl=hkl) if with_symmetry: v = sym_ops.broadcast_to(sym_ops.shape+v.shape[:-1],mode='right') \ @ np.broadcast_to(v,sym_ops.shape+v.shape) @@ -820,8 +811,8 @@ class Orientation(Rotation): """ try: - d = self.structure.to_frame(uvw=self.structure.kinematics(mode)['direction']) - p = self.structure.to_frame(hkl=self.structure.kinematics(mode)['plane']) + d = self.to_frame(uvw=self.kinematics(mode)['direction']) + p = self.to_frame(hkl=self.kinematics(mode)['plane']) except KeyError: raise (f'"{mode}" not defined for lattice "{self.lattice}"') P = np.einsum('...i,...j',d/np.linalg.norm(d,axis=-1,keepdims=True), @@ -839,15 +830,14 @@ class Orientation(Rotation): is added to the left of the Rotation array. """ - lattice,o = self.structure.relation_operations(model) + lattice,o = self.relation_operations(model) target = Lattice(lattice=lattice) - struct = self.structure o = o.broadcast_to(o.shape+self.shape,mode='right') return Orientation(rotation=o*Rotation(self.quaternion).broadcast_to(o.shape,mode='left'), lattice=lattice, - b = struct.b if target.ratio['b'] is None else struct.a*target.ratio['b'], - c = struct.c if target.ratio['c'] is None else struct.a*target.ratio['c'], - alpha = None if 'alpha' in target.immutable else struct.alpha, - beta = None if 'beta' in target.immutable else struct.beta, - gamma = None if 'gamma' in target.immutable else struct.gamma, + b = self.b if target.ratio['b'] is None else self.a*target.ratio['b'], + c = self.c if target.ratio['c'] is None else self.a*target.ratio['c'], + alpha = None if 'alpha' in target.immutable else self.alpha, + beta = None if 'beta' in target.immutable else self.beta, + gamma = None if 'gamma' in target.immutable else self.gamma, ) diff --git a/python/tests/test_Lattice.py b/python/tests/test_Lattice.py index c9593301c..0e820e1ce 100644 --- a/python/tests/test_Lattice.py +++ b/python/tests/test_Lattice.py @@ -6,12 +6,12 @@ from damask import Lattice class TestLattice: def test_double_to_lattice(self): - L = Lattice('cF') + L = Lattice(lattice='cF') with pytest.raises(KeyError): L.to_lattice(direction=np.ones(3),plane=np.ones(3)) def test_double_to_frame(self): - L = Lattice('cF') + L = Lattice(lattice='cF') with pytest.raises(KeyError): L.to_frame(uvw=np.ones(3),hkl=np.ones(3)) diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index 5317ada19..38559c662 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -409,7 +409,7 @@ class TestOrientation: **dict(zip(['a','b','c'],lengths)), **dict(zip(['alpha','beta','gamma'],np.arccos(cosines))), ) - assert np.allclose(o.structure.to_frame(uvw=np.eye(3)),basis), 'Lattice basis disagrees with initialization' + assert np.allclose(o.to_frame(uvw=np.eye(3)),basis), 'Lattice basis disagrees with initialization' @pytest.mark.parametrize('lattice,a,b,c,alpha,beta,gamma', @@ -436,7 +436,7 @@ class TestOrientation: a=a,b=b,c=c, alpha=alpha,beta=beta,gamma=gamma) assert o.to_pole(**{kw:vector,'with_symmetry':with_symmetry}).shape \ - == o.shape + (o.structure.symmetry_operations.shape if with_symmetry else ()) + vector.shape + == o.shape + (o.symmetry_operations.shape if with_symmetry else ()) + vector.shape @pytest.mark.parametrize('lattice',['hP','cI','cF']) def test_Schmid(self,update,ref_path,lattice): From 5202da13ea4976833d3ca03630595cc89327671e Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 7 Jun 2021 21:49:04 +0200 Subject: [PATCH 12/43] better not have a "Lattice" object with "lattice" parameter --- python/damask/__init__.py | 2 +- python/damask/{_lattice.py => _crystal.py} | 2 +- python/damask/_orientation.py | 8 +++--- .../{test_Lattice.py => test_Crystal.py} | 26 +++++++++---------- 4 files changed, 19 insertions(+), 19 deletions(-) rename python/damask/{_lattice.py => _crystal.py} (99%) rename python/tests/{test_Lattice.py => test_Crystal.py} (78%) diff --git a/python/damask/__init__.py b/python/damask/__init__.py index eb333487d..de87444b3 100644 --- a/python/damask/__init__.py +++ b/python/damask/__init__.py @@ -18,7 +18,7 @@ from . import grid_filters # noqa #For example, '_colormap' containsa class called 'Colormap' which is imported as 'damask.Colormap'. from ._rotation import Rotation # noqa from ._lattice_family import LatticeFamily # noqa -from ._lattice import Lattice # noqa +from ._crystal import Crystal # noqa from ._orientation import Orientation # noqa from ._table import Table # noqa from ._vtk import VTK # noqa diff --git a/python/damask/_lattice.py b/python/damask/_crystal.py similarity index 99% rename from python/damask/_lattice.py rename to python/damask/_crystal.py index 51e29c282..9d76bc3ab 100644 --- a/python/damask/_lattice.py +++ b/python/damask/_crystal.py @@ -26,7 +26,7 @@ lattice_symmetries = { } -class Lattice(LatticeFamily): +class Crystal(LatticeFamily): """Lattice.""" def __init__(self,*, diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index c26c0b5f9..fec73c551 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -4,7 +4,7 @@ import copy import numpy as np from . import Rotation -from . import Lattice +from . import Crystal from . import util from . import tensor @@ -55,7 +55,7 @@ _parameter_doc = \ """ -class Orientation(Rotation,Lattice): +class Orientation(Rotation,Crystal): """ Representation of crystallographic orientation as combination of rotation and either crystal family or Bravais lattice. @@ -131,7 +131,7 @@ class Orientation(Rotation,Lattice): """ Rotation.__init__(self,rotation) - Lattice.__init__(self,family=family, lattice=lattice, + Crystal.__init__(self,family=family, lattice=lattice, a=a,b=b,c=c, alpha=alpha,beta=beta,gamma=gamma, degrees=degrees) @@ -831,7 +831,7 @@ class Orientation(Rotation,Lattice): """ lattice,o = self.relation_operations(model) - target = Lattice(lattice=lattice) + target = Crystal(lattice=lattice) o = o.broadcast_to(o.shape+self.shape,mode='right') return Orientation(rotation=o*Rotation(self.quaternion).broadcast_to(o.shape,mode='left'), lattice=lattice, diff --git a/python/tests/test_Lattice.py b/python/tests/test_Crystal.py similarity index 78% rename from python/tests/test_Lattice.py rename to python/tests/test_Crystal.py index 0e820e1ce..8142e03c5 100644 --- a/python/tests/test_Lattice.py +++ b/python/tests/test_Crystal.py @@ -1,19 +1,19 @@ import pytest import numpy as np -from damask import Lattice +from damask import Crystal -class TestLattice: +class TestCrystal: def test_double_to_lattice(self): - L = Lattice(lattice='cF') + c = Crystal(lattice='cF') with pytest.raises(KeyError): - L.to_lattice(direction=np.ones(3),plane=np.ones(3)) + c.to_lattice(direction=np.ones(3),plane=np.ones(3)) def test_double_to_frame(self): - L = Lattice(lattice='cF') + c = Crystal(lattice='cF') with pytest.raises(KeyError): - L.to_frame(uvw=np.ones(3),hkl=np.ones(3)) + c.to_frame(uvw=np.ones(3),hkl=np.ones(3)) @pytest.mark.parametrize('lattice,a,b,c,alpha,beta,gamma', [ @@ -25,10 +25,10 @@ class TestLattice: ('cF',1.0,1.0,None,np.pi/2,np.pi/2,np.pi/2), ]) def test_bases_contraction(self,lattice,a,b,c,alpha,beta,gamma): - L = Lattice(lattice=lattice, + c = Crystal(lattice=lattice, a=a,b=b,c=c, alpha=alpha,beta=beta,gamma=gamma) - assert np.allclose(np.eye(3),np.einsum('ik,jk',L.basis_real,L.basis_reciprocal)) + assert np.allclose(np.eye(3),np.einsum('ik,jk',c.basis_real,c.basis_reciprocal)) @pytest.mark.parametrize('keyFrame,keyLattice',[('uvw','direction'),('hkl','plane'),]) @@ -50,15 +50,15 @@ class TestLattice: ('cF',1.0,1.0,1.0,np.pi/2,np.pi/2,np.pi/2), ]) def test_to_frame_to_lattice(self,lattice,a,b,c,alpha,beta,gamma,vector,keyFrame,keyLattice): - L = Lattice(lattice=lattice, + c = Crystal(lattice=lattice, a=a,b=b,c=c, alpha=alpha,beta=beta,gamma=gamma) assert np.allclose(vector, - L.to_frame(**{keyFrame:L.to_lattice(**{keyLattice:vector})})) + c.to_frame(**{keyFrame:c.to_lattice(**{keyLattice:vector})})) @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch','Burgers']) def test_relationship_definition(self,model): - m,o = list(Lattice._orientation_relationships[model]) - assert Lattice._orientation_relationships[model][m].shape[:-1] == \ - Lattice._orientation_relationships[model][o].shape[:-1] + m,o = list(Crystal._orientation_relationships[model]) + assert Crystal._orientation_relationships[model][m].shape[:-1] == \ + Crystal._orientation_relationships[model][o].shape[:-1] From 92ca010b7c8afa8fa1f5151fb294504edb88ea47 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 4 Jul 2021 23:25:00 +0200 Subject: [PATCH 13/43] Philip's corrections --- python/damask/_crystal.py | 10 +++++----- python/damask/_lattice_family.py | 6 +++--- python/damask/_orientation.py | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/python/damask/_crystal.py b/python/damask/_crystal.py index 9d76bc3ab..3df338a9e 100644 --- a/python/damask/_crystal.py +++ b/python/damask/_crystal.py @@ -90,7 +90,7 @@ class Crystal(LatticeFamily): or (self.b is None or ('b' in self.immutable and self.b != self.immutable['b'] * self.a)) \ or (self.c is None or ('c' in self.immutable and self.c != self.immutable['c'] * self.b)) \ or (self.alpha is None or ('alpha' in self.immutable and self.alpha != self.immutable['alpha'])) \ - or (self.beta is None or ( 'beta' in self.immutable and self.beta != self.immutable['beta'])) \ + or (self.beta is None or ('beta' in self.immutable and self.beta != self.immutable['beta'])) \ or (self.gamma is None or ('gamma' in self.immutable and self.gamma != self.immutable['gamma'])): raise ValueError (f'Incompatible parameters {self.parameters} for crystal family {self.family}') @@ -139,7 +139,7 @@ class Crystal(LatticeFamily): @property def basis_real(self): """ - Calculate orthogonal real space crystal basis. + Return orthogonal real space crystal basis. References ---------- @@ -162,7 +162,7 @@ class Crystal(LatticeFamily): @property def basis_reciprocal(self): - """Calculate reciprocal (dual) crystal basis.""" + """Return reciprocal (dual) crystal basis.""" return np.linalg.inv(self.basis_real.T) @@ -172,13 +172,13 @@ class Crystal(LatticeFamily): Parameters ---------- - direction|normal : numpy.ndarray of shape (...,3) + direction|plane : numpy.ndarray of shape (...,3) Vector along direction or plane normal. Returns ------- Miller : numpy.ndarray of shape (...,3) - lattice vector of direction or plane. + Lattice vector of direction or plane. Use util.scale_to_coprime to convert to (integer) Miller indices. """ diff --git a/python/damask/_lattice_family.py b/python/damask/_lattice_family.py index 24d91490d..e34c02e10 100644 --- a/python/damask/_lattice_family.py +++ b/python/damask/_lattice_family.py @@ -6,7 +6,7 @@ class LatticeFamily(): def __init__(self,family): """ - Symmetry-related operations for crystal families. + Symmetry-related operations for crystal family. Parameters ---------- @@ -15,7 +15,7 @@ class LatticeFamily(): """ if family not in self._immutable.keys(): - raise KeyError(f'invalid lattice family "{family}"') + raise KeyError(f'invalid crystal family "{family}"') self.family = family @@ -40,7 +40,7 @@ class LatticeFamily(): @property def immutable(self): - """Return immutable parameters lattice parameters.""" + """Return immutable lattice parameters.""" return self._immutable[self.family] diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index fec73c551..d1a5e9c7a 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -33,10 +33,10 @@ lattice_symmetries = { _parameter_doc = \ """ family : {'triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic'} - Crystal family. Mutual exclusive with 'lattice' parameter. + Crystal family. Mutually exclusive with 'lattice' parameter. lattice : {'aP', 'mP', 'mS', 'oP', 'oS', 'oI', 'oF', 'tP', 'tI', 'hP', 'cP', 'cI', 'cF'}. Bravais lattice in Pearson notation. - Mutual exclusive with 'family' parameter. + Mutually exclusive with 'family' parameter. a : float, optional Length of lattice parameter 'a'. b : float, optional @@ -76,7 +76,7 @@ class Orientation(Rotation,Crystal): - triclinic - aP : primitive - - monoclininic + - monoclinic - mP : primitive - mS : base-centered From 2826e61ea171cdbf45fd9d0cfda916caaac5bd09 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 13 Jul 2021 00:14:13 +0200 Subject: [PATCH 14/43] no need to split code over two files --- python/damask/__init__.py | 1 - python/damask/_crystal.py | 114 ++++++++++++++++- python/damask/_lattice_family.py | 208 ------------------------------- python/damask/_orientation.py | 71 +++++++++++ 4 files changed, 181 insertions(+), 213 deletions(-) delete mode 100644 python/damask/_lattice_family.py diff --git a/python/damask/__init__.py b/python/damask/__init__.py index de87444b3..761fd5621 100644 --- a/python/damask/__init__.py +++ b/python/damask/__init__.py @@ -17,7 +17,6 @@ from . import grid_filters # noqa #Modules that contain only one class (of the same name), are prefixed by a '_'. #For example, '_colormap' containsa class called 'Colormap' which is imported as 'damask.Colormap'. from ._rotation import Rotation # noqa -from ._lattice_family import LatticeFamily # noqa from ._crystal import Crystal # noqa from ._orientation import Orientation # noqa from ._table import Table # noqa diff --git a/python/damask/_crystal.py b/python/damask/_crystal.py index 3df338a9e..525ce9682 100644 --- a/python/damask/_crystal.py +++ b/python/damask/_crystal.py @@ -1,7 +1,6 @@ import numpy as np from . import util -from . import LatticeFamily from . import Rotation lattice_symmetries = { @@ -26,7 +25,7 @@ lattice_symmetries = { } -class Crystal(LatticeFamily): +class Crystal(): """Lattice.""" def __init__(self,*, @@ -42,6 +41,8 @@ class Crystal(LatticeFamily): ---------- lattice : {'aP', 'mP', 'mS', 'oP', 'oS', 'oI', 'oF', 'tP', 'tI', 'hP', 'cP', 'cI', 'cF'}. Name of the Bravais lattice in Pearson notation. + family : {'triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic'} + Name of the crystal family. a : float, optional Length of lattice parameter 'a'. b : float, optional @@ -58,8 +59,10 @@ class Crystal(LatticeFamily): Angles are given in degrees. Defaults to False. """ - super().__init__(family = lattice_symmetries[lattice] if family is None else family) + if family not in [None] + list(self._immutable.keys()): + raise KeyError(f'invalid crystal family "{family}"') + self.family = lattice_symmetries[lattice] if family is None else family self.lattice = lattice if self.lattice is not None: @@ -114,7 +117,9 @@ class Crystal(LatticeFamily): Lattice to check for equality. """ - return self.lattice == other.lattice and self.parameters == other.parameters + return self.lattice == other.lattice and \ + self.parameters == other.parameters and \ + self.family == other.family @property def parameters(self): @@ -122,6 +127,75 @@ class Crystal(LatticeFamily): return (self.a,self.b,self.c,self.alpha,self.beta,self.gamma) + @property + def immutable(self): + """Return immutable lattice parameters.""" + return self._immutable[self.family] + + + @property + def basis(self): + """ + Corners of the standard triangle. + + Not yet defined for monoclinic. + + + References + ---------- + Bases are computed from + + >>> basis = { + ... 'cubic' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red + ... [1.,0.,1.]/np.sqrt(2.), # green + ... [1.,1.,1.]/np.sqrt(3.)]).T), # blue + ... 'hexagonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red + ... [1.,0.,0.], # green + ... [np.sqrt(3.),1.,0.]/np.sqrt(4.)]).T), # blue + ... 'tetragonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red + ... [1.,0.,0.], # green + ... [1.,1.,0.]/np.sqrt(2.)]).T), # blue + ... 'orthorhombic': np.linalg.inv(np.array([[0.,0.,1.], # direction of red + ... [1.,0.,0.], # green + ... [0.,1.,0.]]).T), # blue + ... } + + """ + _basis = { + 'cubic': {'improper':np.array([ [-1. , 0. , 1. ], + [ np.sqrt(2.) , -np.sqrt(2.) , 0. ], + [ 0. , np.sqrt(3.) , 0. ] ]), + 'proper':np.array([ [ 0. , -1. , 1. ], + [-np.sqrt(2.) , np.sqrt(2.) , 0. ], + [ np.sqrt(3.) , 0. , 0. ] ]), + }, + 'hexagonal': + {'improper':np.array([ [ 0. , 0. , 1. ], + [ 1. , -np.sqrt(3.) , 0. ], + [ 0. , 2. , 0. ] ]), + 'proper':np.array([ [ 0. , 0. , 1. ], + [-1. , np.sqrt(3.) , 0. ], + [ np.sqrt(3.) , -1. , 0. ] ]), + }, + 'tetragonal': + {'improper':np.array([ [ 0. , 0. , 1. ], + [ 1. , -1. , 0. ], + [ 0. , np.sqrt(2.) , 0. ] ]), + 'proper':np.array([ [ 0. , 0. , 1. ], + [-1. , 1. , 0. ], + [ np.sqrt(2.) , 0. , 0. ] ]), + }, + 'orthorhombic': + {'improper':np.array([ [ 0., 0., 1.], + [ 1., 0., 0.], + [ 0., 1., 0.] ]), + 'proper':np.array([ [ 0., 0., 1.], + [-1., 0., 0.], + [ 0., 1., 0.] ]), + }} + return _basis.get(self.family,None) + + @property def ratio(self): """Return axes ratios of own lattice.""" @@ -717,3 +791,35 @@ class Crystal(LatticeFamily): ],dtype=float), }, } + + _immutable = { + 'cubic': { + 'b': 1.0, + 'c': 1.0, + 'alpha': np.pi/2., + 'beta': np.pi/2., + 'gamma': np.pi/2., + }, + 'hexagonal': { + 'b': 1.0, + 'alpha': np.pi/2., + 'beta': np.pi/2., + 'gamma': 2.*np.pi/3., + }, + 'tetragonal': { + 'b': 1.0, + 'alpha': np.pi/2., + 'beta': np.pi/2., + 'gamma': np.pi/2., + }, + 'orthorhombic': { + 'alpha': np.pi/2., + 'beta': np.pi/2., + 'gamma': np.pi/2., + }, + 'monoclinic': { + 'alpha': np.pi/2., + 'gamma': np.pi/2., + }, + 'triclinic': {} + } diff --git a/python/damask/_lattice_family.py b/python/damask/_lattice_family.py deleted file mode 100644 index e34c02e10..000000000 --- a/python/damask/_lattice_family.py +++ /dev/null @@ -1,208 +0,0 @@ -import numpy as np - -from . import Rotation - -class LatticeFamily(): - - def __init__(self,family): - """ - Symmetry-related operations for crystal family. - - Parameters - ---------- - family : {'triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic'} - Name of the crystal family. - - """ - if family not in self._immutable.keys(): - raise KeyError(f'invalid crystal family "{family}"') - self.family = family - - - def __eq__(self,other): - """ - Equal to other. - - Parameters - ---------- - other : LatticeFamily - Lattice family to check for equality. - - """ - return self.family == other.family - - - @property - def symmetry_operations(self): - """Symmetry operations as Rotations.""" - return Rotation.from_quaternion(self._symmetry_operations[self.family],accept_homomorph=True) - - - @property - def immutable(self): - """Return immutable lattice parameters.""" - return self._immutable[self.family] - - - @property - def basis(self): - """ - Corners of the standard triangle. - - Not yet defined for monoclinic. - - - References - ---------- - Bases are computed from - - >>> basis = { - ... 'cubic' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,1.]/np.sqrt(2.), # green - ... [1.,1.,1.]/np.sqrt(3.)]).T), # blue - ... 'hexagonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,0.], # green - ... [np.sqrt(3.),1.,0.]/np.sqrt(4.)]).T), # blue - ... 'tetragonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,0.], # green - ... [1.,1.,0.]/np.sqrt(2.)]).T), # blue - ... 'orthorhombic': np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,0.], # green - ... [0.,1.,0.]]).T), # blue - ... } - - """ - return self._basis.get(self.family,None) - - - _symmetry_operations = { - 'cubic': [ - [ 1.0, 0.0, 0.0, 0.0 ], - [ 0.0, 1.0, 0.0, 0.0 ], - [ 0.0, 0.0, 1.0, 0.0 ], - [ 0.0, 0.0, 0.0, 1.0 ], - [ 0.0, 0.0, 0.5*np.sqrt(2), 0.5*np.sqrt(2) ], - [ 0.0, 0.0, 0.5*np.sqrt(2),-0.5*np.sqrt(2) ], - [ 0.0, 0.5*np.sqrt(2), 0.0, 0.5*np.sqrt(2) ], - [ 0.0, 0.5*np.sqrt(2), 0.0, -0.5*np.sqrt(2) ], - [ 0.0, 0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0 ], - [ 0.0, -0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0 ], - [ 0.5, 0.5, 0.5, 0.5 ], - [-0.5, 0.5, 0.5, 0.5 ], - [-0.5, 0.5, 0.5, -0.5 ], - [-0.5, 0.5, -0.5, 0.5 ], - [-0.5, -0.5, 0.5, 0.5 ], - [-0.5, -0.5, 0.5, -0.5 ], - [-0.5, -0.5, -0.5, 0.5 ], - [-0.5, 0.5, -0.5, -0.5 ], - [-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], - [ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], - [-0.5*np.sqrt(2), 0.0, 0.5*np.sqrt(2), 0.0 ], - [-0.5*np.sqrt(2), 0.0, -0.5*np.sqrt(2), 0.0 ], - [-0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0, 0.0 ], - [-0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0, 0.0 ], - ], - 'hexagonal': [ - [ 1.0, 0.0, 0.0, 0.0 ], - [-0.5*np.sqrt(3), 0.0, 0.0, -0.5 ], - [ 0.5, 0.0, 0.0, 0.5*np.sqrt(3) ], - [ 0.0, 0.0, 0.0, 1.0 ], - [-0.5, 0.0, 0.0, 0.5*np.sqrt(3) ], - [-0.5*np.sqrt(3), 0.0, 0.0, 0.5 ], - [ 0.0, 1.0, 0.0, 0.0 ], - [ 0.0, -0.5*np.sqrt(3), 0.5, 0.0 ], - [ 0.0, 0.5, -0.5*np.sqrt(3), 0.0 ], - [ 0.0, 0.0, 1.0, 0.0 ], - [ 0.0, -0.5, -0.5*np.sqrt(3), 0.0 ], - [ 0.0, 0.5*np.sqrt(3), 0.5, 0.0 ], - ], - 'tetragonal': [ - [ 1.0, 0.0, 0.0, 0.0 ], - [ 0.0, 1.0, 0.0, 0.0 ], - [ 0.0, 0.0, 1.0, 0.0 ], - [ 0.0, 0.0, 0.0, 1.0 ], - [ 0.0, 0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0 ], - [ 0.0, -0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0 ], - [ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], - [-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], - ], - 'orthorhombic': [ - [ 1.0,0.0,0.0,0.0 ], - [ 0.0,1.0,0.0,0.0 ], - [ 0.0,0.0,1.0,0.0 ], - [ 0.0,0.0,0.0,1.0 ], - ], - 'monoclinic': [ - [ 1.0,0.0,0.0,0.0 ], - [ 0.0,0.0,1.0,0.0 ], - ], - 'triclinic': [ - [ 1.0,0.0,0.0,0.0 ], - ]} - - - _immutable = { - 'cubic': { - 'b': 1.0, - 'c': 1.0, - 'alpha': np.pi/2., - 'beta': np.pi/2., - 'gamma': np.pi/2., - }, - 'hexagonal': { - 'b': 1.0, - 'alpha': np.pi/2., - 'beta': np.pi/2., - 'gamma': 2.*np.pi/3., - }, - 'tetragonal': { - 'b': 1.0, - 'alpha': np.pi/2., - 'beta': np.pi/2., - 'gamma': np.pi/2., - }, - 'orthorhombic': { - 'alpha': np.pi/2., - 'beta': np.pi/2., - 'gamma': np.pi/2., - }, - 'monoclinic': { - 'alpha': np.pi/2., - 'gamma': np.pi/2., - }, - 'triclinic': {} - } - - - _basis = { - 'cubic': {'improper':np.array([ [-1. , 0. , 1. ], - [ np.sqrt(2.) , -np.sqrt(2.) , 0. ], - [ 0. , np.sqrt(3.) , 0. ] ]), - 'proper':np.array([ [ 0. , -1. , 1. ], - [-np.sqrt(2.) , np.sqrt(2.) , 0. ], - [ np.sqrt(3.) , 0. , 0. ] ]), - }, - 'hexagonal': - {'improper':np.array([ [ 0. , 0. , 1. ], - [ 1. , -np.sqrt(3.) , 0. ], - [ 0. , 2. , 0. ] ]), - 'proper':np.array([ [ 0. , 0. , 1. ], - [-1. , np.sqrt(3.) , 0. ], - [ np.sqrt(3.) , -1. , 0. ] ]), - }, - 'tetragonal': - {'improper':np.array([ [ 0. , 0. , 1. ], - [ 1. , -1. , 0. ], - [ 0. , np.sqrt(2.) , 0. ] ]), - 'proper':np.array([ [ 0. , 0. , 1. ], - [-1. , 1. , 0. ], - [ np.sqrt(2.) , 0. , 0. ] ]), - }, - 'orthorhombic': - {'improper':np.array([ [ 0., 0., 1.], - [ 1., 0., 0.], - [ 0., 1., 0.] ]), - 'proper':np.array([ [ 0., 0., 1.], - [-1., 0., 0.], - [ 0., 1., 0.] ]), - }} diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index d1a5e9c7a..f6f489b44 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -753,6 +753,77 @@ class Orientation(Rotation,Crystal): return rgb + @property + def symmetry_operations(self): + """Symmetry operations as Rotations.""" + _symmetry_operations = { + 'cubic': [ + [ 1.0, 0.0, 0.0, 0.0 ], + [ 0.0, 1.0, 0.0, 0.0 ], + [ 0.0, 0.0, 1.0, 0.0 ], + [ 0.0, 0.0, 0.0, 1.0 ], + [ 0.0, 0.0, 0.5*np.sqrt(2), 0.5*np.sqrt(2) ], + [ 0.0, 0.0, 0.5*np.sqrt(2),-0.5*np.sqrt(2) ], + [ 0.0, 0.5*np.sqrt(2), 0.0, 0.5*np.sqrt(2) ], + [ 0.0, 0.5*np.sqrt(2), 0.0, -0.5*np.sqrt(2) ], + [ 0.0, 0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0 ], + [ 0.0, -0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0 ], + [ 0.5, 0.5, 0.5, 0.5 ], + [-0.5, 0.5, 0.5, 0.5 ], + [-0.5, 0.5, 0.5, -0.5 ], + [-0.5, 0.5, -0.5, 0.5 ], + [-0.5, -0.5, 0.5, 0.5 ], + [-0.5, -0.5, 0.5, -0.5 ], + [-0.5, -0.5, -0.5, 0.5 ], + [-0.5, 0.5, -0.5, -0.5 ], + [-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], + [ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], + [-0.5*np.sqrt(2), 0.0, 0.5*np.sqrt(2), 0.0 ], + [-0.5*np.sqrt(2), 0.0, -0.5*np.sqrt(2), 0.0 ], + [-0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0, 0.0 ], + [-0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0, 0.0 ], + ], + 'hexagonal': [ + [ 1.0, 0.0, 0.0, 0.0 ], + [-0.5*np.sqrt(3), 0.0, 0.0, -0.5 ], + [ 0.5, 0.0, 0.0, 0.5*np.sqrt(3) ], + [ 0.0, 0.0, 0.0, 1.0 ], + [-0.5, 0.0, 0.0, 0.5*np.sqrt(3) ], + [-0.5*np.sqrt(3), 0.0, 0.0, 0.5 ], + [ 0.0, 1.0, 0.0, 0.0 ], + [ 0.0, -0.5*np.sqrt(3), 0.5, 0.0 ], + [ 0.0, 0.5, -0.5*np.sqrt(3), 0.0 ], + [ 0.0, 0.0, 1.0, 0.0 ], + [ 0.0, -0.5, -0.5*np.sqrt(3), 0.0 ], + [ 0.0, 0.5*np.sqrt(3), 0.5, 0.0 ], + ], + 'tetragonal': [ + [ 1.0, 0.0, 0.0, 0.0 ], + [ 0.0, 1.0, 0.0, 0.0 ], + [ 0.0, 0.0, 1.0, 0.0 ], + [ 0.0, 0.0, 0.0, 1.0 ], + [ 0.0, 0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0 ], + [ 0.0, -0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0 ], + [ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], + [-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], + ], + 'orthorhombic': [ + [ 1.0,0.0,0.0,0.0 ], + [ 0.0,1.0,0.0,0.0 ], + [ 0.0,0.0,1.0,0.0 ], + [ 0.0,0.0,0.0,1.0 ], + ], + 'monoclinic': [ + [ 1.0,0.0,0.0,0.0 ], + [ 0.0,0.0,1.0,0.0 ], + ], + 'triclinic': [ + [ 1.0,0.0,0.0,0.0 ], + ]} + return Rotation.from_quaternion(_symmetry_operations[self.family],accept_homomorph=True) + + +#################################################################################################### # functions that require lattice, not just family def to_pole(self,*,uvw=None,hkl=None,with_symmetry=False): From 2217c3d1437572240fba996713eef627a29630bb Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 18 Jul 2021 15:42:36 +0200 Subject: [PATCH 15/43] unified help --- python/damask/_crystal.py | 11 +++++++---- python/damask/_orientation.py | 10 +++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/python/damask/_crystal.py b/python/damask/_crystal.py index 525ce9682..5d3fa24ea 100644 --- a/python/damask/_crystal.py +++ b/python/damask/_crystal.py @@ -35,14 +35,15 @@ class Crystal(): alpha = None,beta = None,gamma = None, degrees = False): """ - Lattice. + Representation of crystal in terms of crystal family or Bravais lattice. Parameters ---------- - lattice : {'aP', 'mP', 'mS', 'oP', 'oS', 'oI', 'oF', 'tP', 'tI', 'hP', 'cP', 'cI', 'cF'}. - Name of the Bravais lattice in Pearson notation. - family : {'triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic'} + family : {'triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic'}, optional. Name of the crystal family. + Will be infered if 'lattice' is given. + lattice : {'aP', 'mP', 'mS', 'oP', 'oS', 'oI', 'oF', 'tP', 'tI', 'hP', 'cP', 'cI', 'cF'}, optional. + Name of the Bravais lattice in Pearson notation. a : float, optional Length of lattice parameter 'a'. b : float, optional @@ -61,6 +62,8 @@ class Crystal(): """ if family not in [None] + list(self._immutable.keys()): raise KeyError(f'invalid crystal family "{family}"') + if lattice is not None and family is not None and family != lattice_symmetries[lattice]: + raise KeyError(f'incompatible family "{family}" for lattice "{lattice}"') self.family = lattice_symmetries[lattice] if family is None else family self.lattice = lattice diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index f6f489b44..a78b6a189 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -32,11 +32,11 @@ lattice_symmetries = { _parameter_doc = \ """ - family : {'triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic'} - Crystal family. Mutually exclusive with 'lattice' parameter. - lattice : {'aP', 'mP', 'mS', 'oP', 'oS', 'oI', 'oF', 'tP', 'tI', 'hP', 'cP', 'cI', 'cF'}. - Bravais lattice in Pearson notation. - Mutually exclusive with 'family' parameter. + family : {'triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic'}, optional. + Name of the crystal family. + Will be infered if 'lattice' is given. + lattice : {'aP', 'mP', 'mS', 'oP', 'oS', 'oI', 'oF', 'tP', 'tI', 'hP', 'cP', 'cI', 'cF'}, optional. + Name of the Bravais lattice in Pearson notation. a : float, optional Length of lattice parameter 'a'. b : float, optional From 212a4ed63c344cf4102c691fbb3d9beebc4e663a Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 18 Jul 2021 15:57:51 +0200 Subject: [PATCH 16/43] don't test implementation details --- python/damask/_crystal.py | 67 ++++++++++++++++++------------------ python/tests/test_Crystal.py | 7 ---- 2 files changed, 34 insertions(+), 40 deletions(-) diff --git a/python/damask/_crystal.py b/python/damask/_crystal.py index 5d3fa24ea..4c9a5cc34 100644 --- a/python/damask/_crystal.py +++ b/python/damask/_crystal.py @@ -60,7 +60,7 @@ class Crystal(): Angles are given in degrees. Defaults to False. """ - if family not in [None] + list(self._immutable.keys()): + if family not in [None] + list(lattice_symmetries.values()): raise KeyError(f'invalid crystal family "{family}"') if lattice is not None and family is not None and family != lattice_symmetries[lattice]: raise KeyError(f'incompatible family "{family}" for lattice "{lattice}"') @@ -133,7 +133,38 @@ class Crystal(): @property def immutable(self): """Return immutable lattice parameters.""" - return self._immutable[self.family] + _immutable = { + 'cubic': { + 'b': 1.0, + 'c': 1.0, + 'alpha': np.pi/2., + 'beta': np.pi/2., + 'gamma': np.pi/2., + }, + 'hexagonal': { + 'b': 1.0, + 'alpha': np.pi/2., + 'beta': np.pi/2., + 'gamma': 2.*np.pi/3., + }, + 'tetragonal': { + 'b': 1.0, + 'alpha': np.pi/2., + 'beta': np.pi/2., + 'gamma': np.pi/2., + }, + 'orthorhombic': { + 'alpha': np.pi/2., + 'beta': np.pi/2., + 'gamma': np.pi/2., + }, + 'monoclinic': { + 'alpha': np.pi/2., + 'gamma': np.pi/2., + }, + 'triclinic': {} + } + return _immutable[self.family] @property @@ -795,34 +826,4 @@ class Crystal(): }, } - _immutable = { - 'cubic': { - 'b': 1.0, - 'c': 1.0, - 'alpha': np.pi/2., - 'beta': np.pi/2., - 'gamma': np.pi/2., - }, - 'hexagonal': { - 'b': 1.0, - 'alpha': np.pi/2., - 'beta': np.pi/2., - 'gamma': 2.*np.pi/3., - }, - 'tetragonal': { - 'b': 1.0, - 'alpha': np.pi/2., - 'beta': np.pi/2., - 'gamma': np.pi/2., - }, - 'orthorhombic': { - 'alpha': np.pi/2., - 'beta': np.pi/2., - 'gamma': np.pi/2., - }, - 'monoclinic': { - 'alpha': np.pi/2., - 'gamma': np.pi/2., - }, - 'triclinic': {} - } + diff --git a/python/tests/test_Crystal.py b/python/tests/test_Crystal.py index 8142e03c5..5481003c9 100644 --- a/python/tests/test_Crystal.py +++ b/python/tests/test_Crystal.py @@ -55,10 +55,3 @@ class TestCrystal: alpha=alpha,beta=beta,gamma=gamma) assert np.allclose(vector, c.to_frame(**{keyFrame:c.to_lattice(**{keyLattice:vector})})) - - - @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch','Burgers']) - def test_relationship_definition(self,model): - m,o = list(Crystal._orientation_relationships[model]) - assert Crystal._orientation_relationships[model][m].shape[:-1] == \ - Crystal._orientation_relationships[model][o].shape[:-1] From 86a60283c37eba182e8e20c51c1e33fcc5e7ce05 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 18 Jul 2021 16:02:05 +0200 Subject: [PATCH 17/43] keep data close to where it is used --- python/damask/_crystal.py | 918 +++++++++++++++++++------------------- 1 file changed, 455 insertions(+), 463 deletions(-) diff --git a/python/damask/_crystal.py b/python/damask/_crystal.py index 4c9a5cc34..5769fc9c2 100644 --- a/python/damask/_crystal.py +++ b/python/damask/_crystal.py @@ -172,6 +172,8 @@ class Crystal(): """ Corners of the standard triangle. + Notes + ----- Not yet defined for monoclinic. @@ -180,18 +182,18 @@ class Crystal(): Bases are computed from >>> basis = { - ... 'cubic' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,1.]/np.sqrt(2.), # green - ... [1.,1.,1.]/np.sqrt(3.)]).T), # blue - ... 'hexagonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,0.], # green - ... [np.sqrt(3.),1.,0.]/np.sqrt(4.)]).T), # blue - ... 'tetragonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,0.], # green - ... [1.,1.,0.]/np.sqrt(2.)]).T), # blue - ... 'orthorhombic': np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,0.], # green - ... [0.,1.,0.]]).T), # blue + ... 'cubic' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red + ... [1.,0.,1.]/np.sqrt(2.), # green + ... [1.,1.,1.]/np.sqrt(3.)]).T), # blue + ... 'hexagonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red + ... [1.,0.,0.], # green + ... [np.sqrt(3.),1.,0.]/np.sqrt(4.)]).T), # blue + ... 'tetragonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red + ... [1.,0.,0.], # green + ... [1.,1.,0.]/np.sqrt(2.)]).T), # blue + ... 'orthorhombic': np.linalg.inv(np.array([[0.,0.,1.], # direction of red + ... [1.,0.,0.], # green + ... [0.,1.,0.]]).T), # blue ... } """ @@ -322,7 +324,174 @@ class Crystal(): def kinematics(self,mode): - master = self._kinematics[self.lattice][mode] + _kinematics = { + 'cF': { + 'slip' : np.array([ + [+0,+1,-1, +1,+1,+1], + [-1,+0,+1, +1,+1,+1], + [+1,-1,+0, +1,+1,+1], + [+0,-1,-1, -1,-1,+1], + [+1,+0,+1, -1,-1,+1], + [-1,+1,+0, -1,-1,+1], + [+0,-1,+1, +1,-1,-1], + [-1,+0,-1, +1,-1,-1], + [+1,+1,+0, +1,-1,-1], + [+0,+1,+1, -1,+1,-1], + [+1,+0,-1, -1,+1,-1], + [-1,-1,+0, -1,+1,-1], + [+1,+1,+0, +1,-1,+0], + [+1,-1,+0, +1,+1,+0], + [+1,+0,+1, +1,+0,-1], + [+1,+0,-1, +1,+0,+1], + [+0,+1,+1, +0,+1,-1], + [+0,+1,-1, +0,+1,+1], + ],dtype=float), + 'twin' : np.array([ + [-2, 1, 1, 1, 1, 1], + [ 1,-2, 1, 1, 1, 1], + [ 1, 1,-2, 1, 1, 1], + [ 2,-1, 1, -1,-1, 1], + [-1, 2, 1, -1,-1, 1], + [-1,-1,-2, -1,-1, 1], + [-2,-1,-1, 1,-1,-1], + [ 1, 2,-1, 1,-1,-1], + [ 1,-1, 2, 1,-1,-1], + [ 2, 1,-1, -1, 1,-1], + [-1,-2,-1, -1, 1,-1], + [-1, 1, 2, -1, 1,-1], + ],dtype=float), + }, + 'cI': { + 'slip' : np.array([ + [+1,-1,+1, +0,+1,+1], + [-1,-1,+1, +0,+1,+1], + [+1,+1,+1, +0,-1,+1], + [-1,+1,+1, +0,-1,+1], + [-1,+1,+1, +1,+0,+1], + [-1,-1,+1, +1,+0,+1], + [+1,+1,+1, -1,+0,+1], + [+1,-1,+1, -1,+0,+1], + [-1,+1,+1, +1,+1,+0], + [-1,+1,-1, +1,+1,+0], + [+1,+1,+1, -1,+1,+0], + [+1,+1,-1, -1,+1,+0], + [-1,+1,+1, +2,+1,+1], + [+1,+1,+1, -2,+1,+1], + [+1,+1,-1, +2,-1,+1], + [+1,-1,+1, +2,+1,-1], + [+1,-1,+1, +1,+2,+1], + [+1,+1,-1, -1,+2,+1], + [+1,+1,+1, +1,-2,+1], + [-1,+1,+1, +1,+2,-1], + [+1,+1,-1, +1,+1,+2], + [+1,-1,+1, -1,+1,+2], + [-1,+1,+1, +1,-1,+2], + [+1,+1,+1, +1,+1,-2], + [+1,+1,-1, +1,+2,+3], + [+1,-1,+1, -1,+2,+3], + [-1,+1,+1, +1,-2,+3], + [+1,+1,+1, +1,+2,-3], + [+1,-1,+1, +1,+3,+2], + [+1,+1,-1, -1,+3,+2], + [+1,+1,+1, +1,-3,+2], + [-1,+1,+1, +1,+3,-2], + [+1,+1,-1, +2,+1,+3], + [+1,-1,+1, -2,+1,+3], + [-1,+1,+1, +2,-1,+3], + [+1,+1,+1, +2,+1,-3], + [+1,-1,+1, +2,+3,+1], + [+1,+1,-1, -2,+3,+1], + [+1,+1,+1, +2,-3,+1], + [-1,+1,+1, +2,+3,-1], + [-1,+1,+1, +3,+1,+2], + [+1,+1,+1, -3,+1,+2], + [+1,+1,-1, +3,-1,+2], + [+1,-1,+1, +3,+1,-2], + [-1,+1,+1, +3,+2,+1], + [+1,+1,+1, -3,+2,+1], + [+1,+1,-1, +3,-2,+1], + [+1,-1,+1, +3,+2,-1], + ],dtype=float), + 'twin' : np.array([ + [-1, 1, 1, 2, 1, 1], + [ 1, 1, 1, -2, 1, 1], + [ 1, 1,-1, 2,-1, 1], + [ 1,-1, 1, 2, 1,-1], + [ 1,-1, 1, 1, 2, 1], + [ 1, 1,-1, -1, 2, 1], + [ 1, 1, 1, 1,-2, 1], + [-1, 1, 1, 1, 2,-1], + [ 1, 1,-1, 1, 1, 2], + [ 1,-1, 1, -1, 1, 2], + [-1, 1, 1, 1,-1, 2], + [ 1, 1, 1, 1, 1,-2], + ],dtype=float), + }, + 'hP': { + 'slip' : np.array([ + [+2,-1,-1,+0, +0,+0,+0,+1], + [-1,+2,-1,+0, +0,+0,+0,+1], + [-1,-1,+2,+0, +0,+0,+0,+1], + [+2,-1,-1,+0, +0,+1,-1,+0], + [-1,+2,-1,+0, -1,+0,+1,+0], + [-1,-1,+2,+0, +1,-1,+0,+0], + [-1,+1,+0,+0, +1,+1,-2,+0], + [+0,-1,+1,+0, -2,+1,+1,+0], + [+1,+0,-1,+0, +1,-2,+1,+0], + [-1,+2,-1,+0, +1,+0,-1,+1], + [-2,+1,+1,+0, +0,+1,-1,+1], + [-1,-1,+2,+0, -1,+1,+0,+1], + [+1,-2,+1,+0, -1,+0,+1,+1], + [+2,-1,-1,+0, +0,-1,+1,+1], + [+1,+1,-2,+0, +1,-1,+0,+1], + [-2,+1,+1,+3, +1,+0,-1,+1], + [-1,-1,+2,+3, +1,+0,-1,+1], + [-1,-1,+2,+3, +0,+1,-1,+1], + [+1,-2,+1,+3, +0,+1,-1,+1], + [+1,-2,+1,+3, -1,+1,+0,+1], + [+2,-1,-1,+3, -1,+1,+0,+1], + [+2,-1,-1,+3, -1,+0,+1,+1], + [+1,+1,-2,+3, -1,+0,+1,+1], + [+1,+1,-2,+3, +0,-1,+1,+1], + [-1,+2,-1,+3, +0,-1,+1,+1], + [-1,+2,-1,+3, +1,-1,+0,+1], + [-2,+1,+1,+3, +1,-1,+0,+1], + [-1,-1,+2,+3, +1,+1,-2,+2], + [+1,-2,+1,+3, -1,+2,-1,+2], + [+2,-1,-1,+3, -2,+1,+1,+2], + [+1,+1,-2,+3, -1,-1,+2,+2], + [-1,+2,-1,+3, +1,-2,+1,+2], + [-2,+1,+1,+3, +2,-1,-1,+2], + ],dtype=float), + 'twin' : np.array([ + [-1, 0, 1, 1, 1, 0,-1, 2], # shear = (3-(c/a)^2)/(sqrt(3) c/a) <-10.1>{10.2} + [ 0,-1, 1, 1, 0, 1,-1, 2], + [ 1,-1, 0, 1, -1, 1, 0, 2], + [ 1, 0,-1, 1, -1, 0, 1, 2], + [ 0, 1,-1, 1, 0,-1, 1, 2], + [-1, 1, 0, 1, 1,-1, 0, 2], + [-1,-1, 2, 6, 1, 1,-2, 1], # shear = 1/(c/a) <11.6>{-1-1.1} + [ 1,-2, 1, 6, -1, 2,-1, 1], + [ 2,-1,-1, 6, -2, 1, 1, 1], + [ 1, 1,-2, 6, -1,-1, 2, 1], + [-1, 2,-1, 6, 1,-2, 1, 1], + [-2, 1, 1, 6, 2,-1,-1, 1], + [ 1, 0,-1,-2, 1, 0,-1, 1], # shear = (4(c/a)^2-9)/(4 sqrt(3) c/a) <10.-2>{10.1} + [ 0, 1,-1,-2, 0, 1,-1, 1], + [-1, 1, 0,-2, -1, 1, 0, 1], + [-1, 0, 1,-2, -1, 0, 1, 1], + [ 0,-1, 1,-2, 0,-1, 1, 1], + [ 1,-1, 0,-2, 1,-1, 0, 1], + [ 1, 1,-2,-3, 1, 1,-2, 2], # shear = 2((c/a)^2-2)/(3 c/a) <11.-3>{11.2} + [-1, 2,-1,-3, -1, 2,-1, 2], + [-2, 1, 1,-3, -2, 1, 1, 2], + [-1,-1, 2,-3, -1,-1, 2, 2], + [ 1,-2, 1,-3, 1,-2, 1, 2], + [ 2,-1,-1,-3, 2,-1,-1, 2], + ],dtype=float), + }, + } + master = _kinematics[self.lattice][mode] if self.lattice == 'hP': return {'direction':util.Bravais_to_Miller(uvtw=master[:,0:4]), 'plane': util.Bravais_to_Miller(hkil=master[:,4:8])} @@ -363,9 +532,280 @@ class Crystal(): https://doi.org/10.1016/j.actamat.2004.11.021 """ - if model not in self.orientation_relationships: + _orientation_relationships = { + 'KS': { + 'cF' : np.array([ + [[-1, 0, 1],[ 1, 1, 1]], + [[-1, 0, 1],[ 1, 1, 1]], + [[ 0, 1,-1],[ 1, 1, 1]], + [[ 0, 1,-1],[ 1, 1, 1]], + [[ 1,-1, 0],[ 1, 1, 1]], + [[ 1,-1, 0],[ 1, 1, 1]], + [[ 1, 0,-1],[ 1,-1, 1]], + [[ 1, 0,-1],[ 1,-1, 1]], + [[-1,-1, 0],[ 1,-1, 1]], + [[-1,-1, 0],[ 1,-1, 1]], + [[ 0, 1, 1],[ 1,-1, 1]], + [[ 0, 1, 1],[ 1,-1, 1]], + [[ 0,-1, 1],[-1, 1, 1]], + [[ 0,-1, 1],[-1, 1, 1]], + [[-1, 0,-1],[-1, 1, 1]], + [[-1, 0,-1],[-1, 1, 1]], + [[ 1, 1, 0],[-1, 1, 1]], + [[ 1, 1, 0],[-1, 1, 1]], + [[-1, 1, 0],[ 1, 1,-1]], + [[-1, 1, 0],[ 1, 1,-1]], + [[ 0,-1,-1],[ 1, 1,-1]], + [[ 0,-1,-1],[ 1, 1,-1]], + [[ 1, 0, 1],[ 1, 1,-1]], + [[ 1, 0, 1],[ 1, 1,-1]], + ],dtype=float), + 'cI' : np.array([ + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + [[-1,-1, 1],[ 0, 1, 1]], + [[-1, 1,-1],[ 0, 1, 1]], + ],dtype=float), + }, + 'GT': { + 'cF' : np.array([ + [[ -5,-12, 17],[ 1, 1, 1]], + [[ 17, -5,-12],[ 1, 1, 1]], + [[-12, 17, -5],[ 1, 1, 1]], + [[ 5, 12, 17],[ -1, -1, 1]], + [[-17, 5,-12],[ -1, -1, 1]], + [[ 12,-17, -5],[ -1, -1, 1]], + [[ -5, 12,-17],[ -1, 1, 1]], + [[ 17, 5, 12],[ -1, 1, 1]], + [[-12,-17, 5],[ -1, 1, 1]], + [[ 5,-12,-17],[ 1, -1, 1]], + [[-17, -5, 12],[ 1, -1, 1]], + [[ 12, 17, 5],[ 1, -1, 1]], + [[ -5, 17,-12],[ 1, 1, 1]], + [[-12, -5, 17],[ 1, 1, 1]], + [[ 17,-12, -5],[ 1, 1, 1]], + [[ 5,-17,-12],[ -1, -1, 1]], + [[ 12, 5, 17],[ -1, -1, 1]], + [[-17, 12, -5],[ -1, -1, 1]], + [[ -5,-17, 12],[ -1, 1, 1]], + [[-12, 5,-17],[ -1, 1, 1]], + [[ 17, 12, 5],[ -1, 1, 1]], + [[ 5, 17, 12],[ 1, -1, 1]], + [[ 12, -5,-17],[ 1, -1, 1]], + [[-17,-12, 5],[ 1, -1, 1]], + ],dtype=float), + 'cI' : np.array([ + [[-17, -7, 17],[ 1, 0, 1]], + [[ 17,-17, -7],[ 1, 1, 0]], + [[ -7, 17,-17],[ 0, 1, 1]], + [[ 17, 7, 17],[ -1, 0, 1]], + [[-17, 17, -7],[ -1, -1, 0]], + [[ 7,-17,-17],[ 0, -1, 1]], + [[-17, 7,-17],[ -1, 0, 1]], + [[ 17, 17, 7],[ -1, 1, 0]], + [[ -7,-17, 17],[ 0, 1, 1]], + [[ 17, -7,-17],[ 1, 0, 1]], + [[-17,-17, 7],[ 1, -1, 0]], + [[ 7, 17, 17],[ 0, -1, 1]], + [[-17, 17, -7],[ 1, 1, 0]], + [[ -7,-17, 17],[ 0, 1, 1]], + [[ 17, -7,-17],[ 1, 0, 1]], + [[ 17,-17, -7],[ -1, -1, 0]], + [[ 7, 17, 17],[ 0, -1, 1]], + [[-17, 7,-17],[ -1, 0, 1]], + [[-17,-17, 7],[ -1, 1, 0]], + [[ -7, 17,-17],[ 0, 1, 1]], + [[ 17, 7, 17],[ -1, 0, 1]], + [[ 17, 17, 7],[ 1, -1, 0]], + [[ 7,-17,-17],[ 0, -1, 1]], + [[-17, -7, 17],[ 1, 0, 1]], + ],dtype=float), + }, + 'GT_prime': { + 'cF' : np.array([ + [[ 0, 1, -1],[ 7, 17, 17]], + [[ -1, 0, 1],[ 17, 7, 17]], + [[ 1, -1, 0],[ 17, 17, 7]], + [[ 0, -1, -1],[ -7,-17, 17]], + [[ 1, 0, 1],[-17, -7, 17]], + [[ 1, -1, 0],[-17,-17, 7]], + [[ 0, 1, -1],[ 7,-17,-17]], + [[ 1, 0, 1],[ 17, -7,-17]], + [[ -1, -1, 0],[ 17,-17, -7]], + [[ 0, -1, -1],[ -7, 17,-17]], + [[ -1, 0, 1],[-17, 7,-17]], + [[ -1, -1, 0],[-17, 17, -7]], + [[ 0, -1, 1],[ 7, 17, 17]], + [[ 1, 0, -1],[ 17, 7, 17]], + [[ -1, 1, 0],[ 17, 17, 7]], + [[ 0, 1, 1],[ -7,-17, 17]], + [[ -1, 0, -1],[-17, -7, 17]], + [[ -1, 1, 0],[-17,-17, 7]], + [[ 0, -1, 1],[ 7,-17,-17]], + [[ -1, 0, -1],[ 17, -7,-17]], + [[ 1, 1, 0],[ 17,-17, -7]], + [[ 0, 1, 1],[ -7, 17,-17]], + [[ 1, 0, -1],[-17, 7,-17]], + [[ 1, 1, 0],[-17, 17, -7]], + ],dtype=float), + 'cI' : np.array([ + [[ 1, 1, -1],[ 12, 5, 17]], + [[ -1, 1, 1],[ 17, 12, 5]], + [[ 1, -1, 1],[ 5, 17, 12]], + [[ -1, -1, -1],[-12, -5, 17]], + [[ 1, -1, 1],[-17,-12, 5]], + [[ 1, -1, -1],[ -5,-17, 12]], + [[ -1, 1, -1],[ 12, -5,-17]], + [[ 1, 1, 1],[ 17,-12, -5]], + [[ -1, -1, 1],[ 5,-17,-12]], + [[ 1, -1, -1],[-12, 5,-17]], + [[ -1, -1, 1],[-17, 12, -5]], + [[ -1, -1, -1],[ -5, 17,-12]], + [[ 1, -1, 1],[ 12, 17, 5]], + [[ 1, 1, -1],[ 5, 12, 17]], + [[ -1, 1, 1],[ 17, 5, 12]], + [[ -1, 1, 1],[-12,-17, 5]], + [[ -1, -1, -1],[ -5,-12, 17]], + [[ -1, 1, -1],[-17, -5, 12]], + [[ -1, -1, 1],[ 12,-17, -5]], + [[ -1, 1, -1],[ 5,-12,-17]], + [[ 1, 1, 1],[ 17, -5,-12]], + [[ 1, 1, 1],[-12, 17, -5]], + [[ 1, -1, -1],[ -5, 12,-17]], + [[ 1, 1, -1],[-17, 5,-12]], + ],dtype=float), + }, + 'NW': { + 'cF' : np.array([ + [[ 2, -1, -1],[ 1, 1, 1]], + [[ -1, 2, -1],[ 1, 1, 1]], + [[ -1, -1, 2],[ 1, 1, 1]], + [[ -2, -1, -1],[ -1, 1, 1]], + [[ 1, 2, -1],[ -1, 1, 1]], + [[ 1, -1, 2],[ -1, 1, 1]], + [[ 2, 1, -1],[ 1, -1, 1]], + [[ -1, -2, -1],[ 1, -1, 1]], + [[ -1, 1, 2],[ 1, -1, 1]], + [[ 2, -1, 1],[ -1, -1, 1]], + [[ -1, 2, 1],[ -1, -1, 1]], + [[ -1, -1, -2],[ -1, -1, 1]], + ],dtype=float), + 'cI' : np.array([ + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + [[ 0, -1, 1],[ 0, 1, 1]], + ],dtype=float), + }, + 'Pitsch': { + 'cF' : np.array([ + [[ 1, 0, 1],[ 0, 1, 0]], + [[ 1, 1, 0],[ 0, 0, 1]], + [[ 0, 1, 1],[ 1, 0, 0]], + [[ 0, 1, -1],[ 1, 0, 0]], + [[ -1, 0, 1],[ 0, 1, 0]], + [[ 1, -1, 0],[ 0, 0, 1]], + [[ 1, 0, -1],[ 0, 1, 0]], + [[ -1, 1, 0],[ 0, 0, 1]], + [[ 0, -1, 1],[ 1, 0, 0]], + [[ 0, 1, 1],[ 1, 0, 0]], + [[ 1, 0, 1],[ 0, 1, 0]], + [[ 1, 1, 0],[ 0, 0, 1]], + ],dtype=float), + 'cI' : np.array([ + [[ 1, -1, 1],[ -1, 0, 1]], + [[ 1, 1, -1],[ 1, -1, 0]], + [[ -1, 1, 1],[ 0, 1, -1]], + [[ -1, 1, -1],[ 0, -1, -1]], + [[ -1, -1, 1],[ -1, 0, -1]], + [[ 1, -1, -1],[ -1, -1, 0]], + [[ 1, -1, -1],[ -1, 0, -1]], + [[ -1, 1, -1],[ -1, -1, 0]], + [[ -1, -1, 1],[ 0, -1, -1]], + [[ -1, 1, 1],[ 0, -1, 1]], + [[ 1, -1, 1],[ 1, 0, -1]], + [[ 1, 1, -1],[ -1, 1, 0]], + ],dtype=float), + }, + 'Bain': { + 'cF' : np.array([ + [[ 0, 1, 0],[ 1, 0, 0]], + [[ 0, 0, 1],[ 0, 1, 0]], + [[ 1, 0, 0],[ 0, 0, 1]], + ],dtype=float), + 'cI' : np.array([ + [[ 0, 1, 1],[ 1, 0, 0]], + [[ 1, 0, 1],[ 0, 1, 0]], + [[ 1, 1, 0],[ 0, 0, 1]], + ],dtype=float), + }, + 'Burgers' : { + 'cI' : np.array([ + [[ -1, 1, 1],[ 1, 1, 0]], + [[ -1, 1, -1],[ 1, 1, 0]], + [[ 1, 1, 1],[ 1, -1, 0]], + [[ 1, 1, -1],[ 1, -1, 0]], + + [[ 1, 1, -1],[ 1, 0, 1]], + [[ -1, 1, 1],[ 1, 0, 1]], + [[ 1, 1, 1],[ -1, 0, 1]], + [[ 1, -1, 1],[ -1, 0, 1]], + + [[ -1, 1, -1],[ 0, 1, 1]], + [[ 1, 1, -1],[ 0, 1, 1]], + [[ -1, 1, 1],[ 0, -1, 1]], + [[ 1, 1, 1],[ 0, -1, 1]], + ],dtype=float), + 'hP' : np.array([ + [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], + [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], + [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], + [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], + + [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], + [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], + [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], + [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], + + [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], + [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], + [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], + [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], + ],dtype=float), + }, + } + orientation_relationships = {k:v for k,v in _orientation_relationships.items() if self.lattice in v} + if model not in orientation_relationships: raise KeyError(f'unknown orientation relationship "{model}"') - r = self.orientation_relationships[model] + r = orientation_relationships[model] sl = self.lattice ol = (set(r)-{sl}).pop() @@ -379,451 +819,3 @@ class Crystal(): _p[...,1,:] = o[...,1,:] if o.shape[-1] == 3 else util.Bravais_to_Miller(hkil=o[...,1,0:4]) return (ol,Rotation.from_parallel(p_,_p)) - - - @property - def orientation_relationships(self): - return {k:v for k,v in self._orientation_relationships.items() if self.lattice in v} - - - _kinematics = { - 'cF': { - 'slip' : np.array([ - [+0,+1,-1, +1,+1,+1], - [-1,+0,+1, +1,+1,+1], - [+1,-1,+0, +1,+1,+1], - [+0,-1,-1, -1,-1,+1], - [+1,+0,+1, -1,-1,+1], - [-1,+1,+0, -1,-1,+1], - [+0,-1,+1, +1,-1,-1], - [-1,+0,-1, +1,-1,-1], - [+1,+1,+0, +1,-1,-1], - [+0,+1,+1, -1,+1,-1], - [+1,+0,-1, -1,+1,-1], - [-1,-1,+0, -1,+1,-1], - [+1,+1,+0, +1,-1,+0], - [+1,-1,+0, +1,+1,+0], - [+1,+0,+1, +1,+0,-1], - [+1,+0,-1, +1,+0,+1], - [+0,+1,+1, +0,+1,-1], - [+0,+1,-1, +0,+1,+1], - ],'d'), - 'twin' : np.array([ - [-2, 1, 1, 1, 1, 1], - [ 1,-2, 1, 1, 1, 1], - [ 1, 1,-2, 1, 1, 1], - [ 2,-1, 1, -1,-1, 1], - [-1, 2, 1, -1,-1, 1], - [-1,-1,-2, -1,-1, 1], - [-2,-1,-1, 1,-1,-1], - [ 1, 2,-1, 1,-1,-1], - [ 1,-1, 2, 1,-1,-1], - [ 2, 1,-1, -1, 1,-1], - [-1,-2,-1, -1, 1,-1], - [-1, 1, 2, -1, 1,-1], - ],dtype=float), - }, - 'cI': { - 'slip' : np.array([ - [+1,-1,+1, +0,+1,+1], - [-1,-1,+1, +0,+1,+1], - [+1,+1,+1, +0,-1,+1], - [-1,+1,+1, +0,-1,+1], - [-1,+1,+1, +1,+0,+1], - [-1,-1,+1, +1,+0,+1], - [+1,+1,+1, -1,+0,+1], - [+1,-1,+1, -1,+0,+1], - [-1,+1,+1, +1,+1,+0], - [-1,+1,-1, +1,+1,+0], - [+1,+1,+1, -1,+1,+0], - [+1,+1,-1, -1,+1,+0], - [-1,+1,+1, +2,+1,+1], - [+1,+1,+1, -2,+1,+1], - [+1,+1,-1, +2,-1,+1], - [+1,-1,+1, +2,+1,-1], - [+1,-1,+1, +1,+2,+1], - [+1,+1,-1, -1,+2,+1], - [+1,+1,+1, +1,-2,+1], - [-1,+1,+1, +1,+2,-1], - [+1,+1,-1, +1,+1,+2], - [+1,-1,+1, -1,+1,+2], - [-1,+1,+1, +1,-1,+2], - [+1,+1,+1, +1,+1,-2], - [+1,+1,-1, +1,+2,+3], - [+1,-1,+1, -1,+2,+3], - [-1,+1,+1, +1,-2,+3], - [+1,+1,+1, +1,+2,-3], - [+1,-1,+1, +1,+3,+2], - [+1,+1,-1, -1,+3,+2], - [+1,+1,+1, +1,-3,+2], - [-1,+1,+1, +1,+3,-2], - [+1,+1,-1, +2,+1,+3], - [+1,-1,+1, -2,+1,+3], - [-1,+1,+1, +2,-1,+3], - [+1,+1,+1, +2,+1,-3], - [+1,-1,+1, +2,+3,+1], - [+1,+1,-1, -2,+3,+1], - [+1,+1,+1, +2,-3,+1], - [-1,+1,+1, +2,+3,-1], - [-1,+1,+1, +3,+1,+2], - [+1,+1,+1, -3,+1,+2], - [+1,+1,-1, +3,-1,+2], - [+1,-1,+1, +3,+1,-2], - [-1,+1,+1, +3,+2,+1], - [+1,+1,+1, -3,+2,+1], - [+1,+1,-1, +3,-2,+1], - [+1,-1,+1, +3,+2,-1], - ],'d'), - 'twin' : np.array([ - [-1, 1, 1, 2, 1, 1], - [ 1, 1, 1, -2, 1, 1], - [ 1, 1,-1, 2,-1, 1], - [ 1,-1, 1, 2, 1,-1], - [ 1,-1, 1, 1, 2, 1], - [ 1, 1,-1, -1, 2, 1], - [ 1, 1, 1, 1,-2, 1], - [-1, 1, 1, 1, 2,-1], - [ 1, 1,-1, 1, 1, 2], - [ 1,-1, 1, -1, 1, 2], - [-1, 1, 1, 1,-1, 2], - [ 1, 1, 1, 1, 1,-2], - ],dtype=float), - }, - 'hP': { - 'slip' : np.array([ - [+2,-1,-1,+0, +0,+0,+0,+1], - [-1,+2,-1,+0, +0,+0,+0,+1], - [-1,-1,+2,+0, +0,+0,+0,+1], - [+2,-1,-1,+0, +0,+1,-1,+0], - [-1,+2,-1,+0, -1,+0,+1,+0], - [-1,-1,+2,+0, +1,-1,+0,+0], - [-1,+1,+0,+0, +1,+1,-2,+0], - [+0,-1,+1,+0, -2,+1,+1,+0], - [+1,+0,-1,+0, +1,-2,+1,+0], - [-1,+2,-1,+0, +1,+0,-1,+1], - [-2,+1,+1,+0, +0,+1,-1,+1], - [-1,-1,+2,+0, -1,+1,+0,+1], - [+1,-2,+1,+0, -1,+0,+1,+1], - [+2,-1,-1,+0, +0,-1,+1,+1], - [+1,+1,-2,+0, +1,-1,+0,+1], - [-2,+1,+1,+3, +1,+0,-1,+1], - [-1,-1,+2,+3, +1,+0,-1,+1], - [-1,-1,+2,+3, +0,+1,-1,+1], - [+1,-2,+1,+3, +0,+1,-1,+1], - [+1,-2,+1,+3, -1,+1,+0,+1], - [+2,-1,-1,+3, -1,+1,+0,+1], - [+2,-1,-1,+3, -1,+0,+1,+1], - [+1,+1,-2,+3, -1,+0,+1,+1], - [+1,+1,-2,+3, +0,-1,+1,+1], - [-1,+2,-1,+3, +0,-1,+1,+1], - [-1,+2,-1,+3, +1,-1,+0,+1], - [-2,+1,+1,+3, +1,-1,+0,+1], - [-1,-1,+2,+3, +1,+1,-2,+2], - [+1,-2,+1,+3, -1,+2,-1,+2], - [+2,-1,-1,+3, -2,+1,+1,+2], - [+1,+1,-2,+3, -1,-1,+2,+2], - [-1,+2,-1,+3, +1,-2,+1,+2], - [-2,+1,+1,+3, +2,-1,-1,+2], - ],'d'), - 'twin' : np.array([ - [-1, 0, 1, 1, 1, 0,-1, 2], # shear = (3-(c/a)^2)/(sqrt(3) c/a) <-10.1>{10.2} - [ 0,-1, 1, 1, 0, 1,-1, 2], - [ 1,-1, 0, 1, -1, 1, 0, 2], - [ 1, 0,-1, 1, -1, 0, 1, 2], - [ 0, 1,-1, 1, 0,-1, 1, 2], - [-1, 1, 0, 1, 1,-1, 0, 2], - [-1,-1, 2, 6, 1, 1,-2, 1], # shear = 1/(c/a) <11.6>{-1-1.1} - [ 1,-2, 1, 6, -1, 2,-1, 1], - [ 2,-1,-1, 6, -2, 1, 1, 1], - [ 1, 1,-2, 6, -1,-1, 2, 1], - [-1, 2,-1, 6, 1,-2, 1, 1], - [-2, 1, 1, 6, 2,-1,-1, 1], - [ 1, 0,-1,-2, 1, 0,-1, 1], # shear = (4(c/a)^2-9)/(4 sqrt(3) c/a) <10.-2>{10.1} - [ 0, 1,-1,-2, 0, 1,-1, 1], - [-1, 1, 0,-2, -1, 1, 0, 1], - [-1, 0, 1,-2, -1, 0, 1, 1], - [ 0,-1, 1,-2, 0,-1, 1, 1], - [ 1,-1, 0,-2, 1,-1, 0, 1], - [ 1, 1,-2,-3, 1, 1,-2, 2], # shear = 2((c/a)^2-2)/(3 c/a) <11.-3>{11.2} - [-1, 2,-1,-3, -1, 2,-1, 2], - [-2, 1, 1,-3, -2, 1, 1, 2], - [-1,-1, 2,-3, -1,-1, 2, 2], - [ 1,-2, 1,-3, 1,-2, 1, 2], - [ 2,-1,-1,-3, 2,-1,-1, 2], - ],dtype=float), - }, - } - - - _orientation_relationships = { - 'KS': { - 'cF' : np.array([ - [[-1, 0, 1],[ 1, 1, 1]], - [[-1, 0, 1],[ 1, 1, 1]], - [[ 0, 1,-1],[ 1, 1, 1]], - [[ 0, 1,-1],[ 1, 1, 1]], - [[ 1,-1, 0],[ 1, 1, 1]], - [[ 1,-1, 0],[ 1, 1, 1]], - [[ 1, 0,-1],[ 1,-1, 1]], - [[ 1, 0,-1],[ 1,-1, 1]], - [[-1,-1, 0],[ 1,-1, 1]], - [[-1,-1, 0],[ 1,-1, 1]], - [[ 0, 1, 1],[ 1,-1, 1]], - [[ 0, 1, 1],[ 1,-1, 1]], - [[ 0,-1, 1],[-1, 1, 1]], - [[ 0,-1, 1],[-1, 1, 1]], - [[-1, 0,-1],[-1, 1, 1]], - [[-1, 0,-1],[-1, 1, 1]], - [[ 1, 1, 0],[-1, 1, 1]], - [[ 1, 1, 0],[-1, 1, 1]], - [[-1, 1, 0],[ 1, 1,-1]], - [[-1, 1, 0],[ 1, 1,-1]], - [[ 0,-1,-1],[ 1, 1,-1]], - [[ 0,-1,-1],[ 1, 1,-1]], - [[ 1, 0, 1],[ 1, 1,-1]], - [[ 1, 0, 1],[ 1, 1,-1]], - ],dtype=float), - 'cI' : np.array([ - [[-1,-1, 1],[ 0, 1, 1]], - [[-1, 1,-1],[ 0, 1, 1]], - [[-1,-1, 1],[ 0, 1, 1]], - [[-1, 1,-1],[ 0, 1, 1]], - [[-1,-1, 1],[ 0, 1, 1]], - [[-1, 1,-1],[ 0, 1, 1]], - [[-1,-1, 1],[ 0, 1, 1]], - [[-1, 1,-1],[ 0, 1, 1]], - [[-1,-1, 1],[ 0, 1, 1]], - [[-1, 1,-1],[ 0, 1, 1]], - [[-1,-1, 1],[ 0, 1, 1]], - [[-1, 1,-1],[ 0, 1, 1]], - [[-1,-1, 1],[ 0, 1, 1]], - [[-1, 1,-1],[ 0, 1, 1]], - [[-1,-1, 1],[ 0, 1, 1]], - [[-1, 1,-1],[ 0, 1, 1]], - [[-1,-1, 1],[ 0, 1, 1]], - [[-1, 1,-1],[ 0, 1, 1]], - [[-1,-1, 1],[ 0, 1, 1]], - [[-1, 1,-1],[ 0, 1, 1]], - [[-1,-1, 1],[ 0, 1, 1]], - [[-1, 1,-1],[ 0, 1, 1]], - [[-1,-1, 1],[ 0, 1, 1]], - [[-1, 1,-1],[ 0, 1, 1]], - ],dtype=float), - }, - 'GT': { - 'cF' : np.array([ - [[ -5,-12, 17],[ 1, 1, 1]], - [[ 17, -5,-12],[ 1, 1, 1]], - [[-12, 17, -5],[ 1, 1, 1]], - [[ 5, 12, 17],[ -1, -1, 1]], - [[-17, 5,-12],[ -1, -1, 1]], - [[ 12,-17, -5],[ -1, -1, 1]], - [[ -5, 12,-17],[ -1, 1, 1]], - [[ 17, 5, 12],[ -1, 1, 1]], - [[-12,-17, 5],[ -1, 1, 1]], - [[ 5,-12,-17],[ 1, -1, 1]], - [[-17, -5, 12],[ 1, -1, 1]], - [[ 12, 17, 5],[ 1, -1, 1]], - [[ -5, 17,-12],[ 1, 1, 1]], - [[-12, -5, 17],[ 1, 1, 1]], - [[ 17,-12, -5],[ 1, 1, 1]], - [[ 5,-17,-12],[ -1, -1, 1]], - [[ 12, 5, 17],[ -1, -1, 1]], - [[-17, 12, -5],[ -1, -1, 1]], - [[ -5,-17, 12],[ -1, 1, 1]], - [[-12, 5,-17],[ -1, 1, 1]], - [[ 17, 12, 5],[ -1, 1, 1]], - [[ 5, 17, 12],[ 1, -1, 1]], - [[ 12, -5,-17],[ 1, -1, 1]], - [[-17,-12, 5],[ 1, -1, 1]], - ],dtype=float), - 'cI' : np.array([ - [[-17, -7, 17],[ 1, 0, 1]], - [[ 17,-17, -7],[ 1, 1, 0]], - [[ -7, 17,-17],[ 0, 1, 1]], - [[ 17, 7, 17],[ -1, 0, 1]], - [[-17, 17, -7],[ -1, -1, 0]], - [[ 7,-17,-17],[ 0, -1, 1]], - [[-17, 7,-17],[ -1, 0, 1]], - [[ 17, 17, 7],[ -1, 1, 0]], - [[ -7,-17, 17],[ 0, 1, 1]], - [[ 17, -7,-17],[ 1, 0, 1]], - [[-17,-17, 7],[ 1, -1, 0]], - [[ 7, 17, 17],[ 0, -1, 1]], - [[-17, 17, -7],[ 1, 1, 0]], - [[ -7,-17, 17],[ 0, 1, 1]], - [[ 17, -7,-17],[ 1, 0, 1]], - [[ 17,-17, -7],[ -1, -1, 0]], - [[ 7, 17, 17],[ 0, -1, 1]], - [[-17, 7,-17],[ -1, 0, 1]], - [[-17,-17, 7],[ -1, 1, 0]], - [[ -7, 17,-17],[ 0, 1, 1]], - [[ 17, 7, 17],[ -1, 0, 1]], - [[ 17, 17, 7],[ 1, -1, 0]], - [[ 7,-17,-17],[ 0, -1, 1]], - [[-17, -7, 17],[ 1, 0, 1]], - ],dtype=float), - }, - 'GT_prime': { - 'cF' : np.array([ - [[ 0, 1, -1],[ 7, 17, 17]], - [[ -1, 0, 1],[ 17, 7, 17]], - [[ 1, -1, 0],[ 17, 17, 7]], - [[ 0, -1, -1],[ -7,-17, 17]], - [[ 1, 0, 1],[-17, -7, 17]], - [[ 1, -1, 0],[-17,-17, 7]], - [[ 0, 1, -1],[ 7,-17,-17]], - [[ 1, 0, 1],[ 17, -7,-17]], - [[ -1, -1, 0],[ 17,-17, -7]], - [[ 0, -1, -1],[ -7, 17,-17]], - [[ -1, 0, 1],[-17, 7,-17]], - [[ -1, -1, 0],[-17, 17, -7]], - [[ 0, -1, 1],[ 7, 17, 17]], - [[ 1, 0, -1],[ 17, 7, 17]], - [[ -1, 1, 0],[ 17, 17, 7]], - [[ 0, 1, 1],[ -7,-17, 17]], - [[ -1, 0, -1],[-17, -7, 17]], - [[ -1, 1, 0],[-17,-17, 7]], - [[ 0, -1, 1],[ 7,-17,-17]], - [[ -1, 0, -1],[ 17, -7,-17]], - [[ 1, 1, 0],[ 17,-17, -7]], - [[ 0, 1, 1],[ -7, 17,-17]], - [[ 1, 0, -1],[-17, 7,-17]], - [[ 1, 1, 0],[-17, 17, -7]], - ],dtype=float), - 'cI' : np.array([ - [[ 1, 1, -1],[ 12, 5, 17]], - [[ -1, 1, 1],[ 17, 12, 5]], - [[ 1, -1, 1],[ 5, 17, 12]], - [[ -1, -1, -1],[-12, -5, 17]], - [[ 1, -1, 1],[-17,-12, 5]], - [[ 1, -1, -1],[ -5,-17, 12]], - [[ -1, 1, -1],[ 12, -5,-17]], - [[ 1, 1, 1],[ 17,-12, -5]], - [[ -1, -1, 1],[ 5,-17,-12]], - [[ 1, -1, -1],[-12, 5,-17]], - [[ -1, -1, 1],[-17, 12, -5]], - [[ -1, -1, -1],[ -5, 17,-12]], - [[ 1, -1, 1],[ 12, 17, 5]], - [[ 1, 1, -1],[ 5, 12, 17]], - [[ -1, 1, 1],[ 17, 5, 12]], - [[ -1, 1, 1],[-12,-17, 5]], - [[ -1, -1, -1],[ -5,-12, 17]], - [[ -1, 1, -1],[-17, -5, 12]], - [[ -1, -1, 1],[ 12,-17, -5]], - [[ -1, 1, -1],[ 5,-12,-17]], - [[ 1, 1, 1],[ 17, -5,-12]], - [[ 1, 1, 1],[-12, 17, -5]], - [[ 1, -1, -1],[ -5, 12,-17]], - [[ 1, 1, -1],[-17, 5,-12]], - ],dtype=float), - }, - 'NW': { - 'cF' : np.array([ - [[ 2, -1, -1],[ 1, 1, 1]], - [[ -1, 2, -1],[ 1, 1, 1]], - [[ -1, -1, 2],[ 1, 1, 1]], - [[ -2, -1, -1],[ -1, 1, 1]], - [[ 1, 2, -1],[ -1, 1, 1]], - [[ 1, -1, 2],[ -1, 1, 1]], - [[ 2, 1, -1],[ 1, -1, 1]], - [[ -1, -2, -1],[ 1, -1, 1]], - [[ -1, 1, 2],[ 1, -1, 1]], - [[ 2, -1, 1],[ -1, -1, 1]], - [[ -1, 2, 1],[ -1, -1, 1]], - [[ -1, -1, -2],[ -1, -1, 1]], - ],dtype=float), - 'cI' : np.array([ - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - [[ 0, -1, 1],[ 0, 1, 1]], - ],dtype=float), - }, - 'Pitsch': { - 'cF' : np.array([ - [[ 1, 0, 1],[ 0, 1, 0]], - [[ 1, 1, 0],[ 0, 0, 1]], - [[ 0, 1, 1],[ 1, 0, 0]], - [[ 0, 1, -1],[ 1, 0, 0]], - [[ -1, 0, 1],[ 0, 1, 0]], - [[ 1, -1, 0],[ 0, 0, 1]], - [[ 1, 0, -1],[ 0, 1, 0]], - [[ -1, 1, 0],[ 0, 0, 1]], - [[ 0, -1, 1],[ 1, 0, 0]], - [[ 0, 1, 1],[ 1, 0, 0]], - [[ 1, 0, 1],[ 0, 1, 0]], - [[ 1, 1, 0],[ 0, 0, 1]], - ],dtype=float), - 'cI' : np.array([ - [[ 1, -1, 1],[ -1, 0, 1]], - [[ 1, 1, -1],[ 1, -1, 0]], - [[ -1, 1, 1],[ 0, 1, -1]], - [[ -1, 1, -1],[ 0, -1, -1]], - [[ -1, -1, 1],[ -1, 0, -1]], - [[ 1, -1, -1],[ -1, -1, 0]], - [[ 1, -1, -1],[ -1, 0, -1]], - [[ -1, 1, -1],[ -1, -1, 0]], - [[ -1, -1, 1],[ 0, -1, -1]], - [[ -1, 1, 1],[ 0, -1, 1]], - [[ 1, -1, 1],[ 1, 0, -1]], - [[ 1, 1, -1],[ -1, 1, 0]], - ],dtype=float), - }, - 'Bain': { - 'cF' : np.array([ - [[ 0, 1, 0],[ 1, 0, 0]], - [[ 0, 0, 1],[ 0, 1, 0]], - [[ 1, 0, 0],[ 0, 0, 1]], - ],dtype=float), - 'cI' : np.array([ - [[ 0, 1, 1],[ 1, 0, 0]], - [[ 1, 0, 1],[ 0, 1, 0]], - [[ 1, 1, 0],[ 0, 0, 1]], - ],dtype=float), - }, - 'Burgers' : { - 'cI' : np.array([ - [[ -1, 1, 1],[ 1, 1, 0]], - [[ -1, 1, -1],[ 1, 1, 0]], - [[ 1, 1, 1],[ 1, -1, 0]], - [[ 1, 1, -1],[ 1, -1, 0]], - - [[ 1, 1, -1],[ 1, 0, 1]], - [[ -1, 1, 1],[ 1, 0, 1]], - [[ 1, 1, 1],[ -1, 0, 1]], - [[ 1, -1, 1],[ -1, 0, 1]], - - [[ -1, 1, -1],[ 0, 1, 1]], - [[ 1, 1, -1],[ 0, 1, 1]], - [[ -1, 1, 1],[ 0, -1, 1]], - [[ 1, 1, 1],[ 0, -1, 1]], - ],dtype=float), - 'hP' : np.array([ - [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], - [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], - [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], - [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], - - [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], - [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], - [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], - [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], - - [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], - [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], - [[ -1, 2, -1, 0],[ 0, 0, 0, 1]], - [[ -1, -1, 2, 0],[ 0, 0, 0, 1]], - ],dtype=float), - }, - } - - From 19ca99e0331273d2714e1718fc4a276c0ef10e1c Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 18 Jul 2021 16:39:52 +0200 Subject: [PATCH 18/43] matching name --- python/damask/_crystal.py | 2 +- python/damask/_orientation.py | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/python/damask/_crystal.py b/python/damask/_crystal.py index 5769fc9c2..09e480709 100644 --- a/python/damask/_crystal.py +++ b/python/damask/_crystal.py @@ -168,7 +168,7 @@ class Crystal(): @property - def basis(self): + def standard_triangle(self): """ Corners of the standard triangle. diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index a78b6a189..545205c0a 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -668,21 +668,21 @@ class Orientation(Rotation,Crystal): if not isinstance(vector,np.ndarray) or vector.shape[-1] != 3: raise ValueError('input is not a field of three-dimensional vectors') - if self.basis is None: # direct exit for no symmetry + if self.standard_triangle is None: # direct exit for no symmetry return np.ones_like(vector[...,0],bool) if proper: components_proper = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.basis['proper'], vector.shape+(3,)), + np.broadcast_to(self.standard_triangle['proper'], vector.shape+(3,)), vector), 12) components_improper = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.basis['improper'], vector.shape+(3,)), + np.broadcast_to(self.standard_triangle['improper'], vector.shape+(3,)), vector), 12) return np.all(components_proper >= 0.0,axis=-1) \ | np.all(components_improper >= 0.0,axis=-1) else: components = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.basis['improper'], vector.shape+(3,)), + np.broadcast_to(self.standard_triangle['improper'], vector.shape+(3,)), np.block([vector[...,:2],np.abs(vector[...,2:3])])), 12) return np.all(components >= 0.0,axis=-1) @@ -723,15 +723,15 @@ class Orientation(Rotation,Crystal): vector_ = self.to_SST(vector,proper) if in_SST else \ self @ np.broadcast_to(vector,self.shape+(3,)) - if self.basis is None: # direct exit for no symmetry + if self.standard_triangle is None: # direct exit for no symmetry return np.zeros_like(vector_) if proper: components_proper = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.basis['proper'], vector_.shape+(3,)), + np.broadcast_to(self.standard_triangle['proper'], vector_.shape+(3,)), vector_), 12) components_improper = np.around(np.einsum('...ji,...i', - np.broadcast_to(self.basis['improper'], vector_.shape+(3,)), + np.broadcast_to(self.standard_triangle['improper'], vector_.shape+(3,)), vector_), 12) in_SST = np.all(components_proper >= 0.0,axis=-1) \ | np.all(components_improper >= 0.0,axis=-1) @@ -739,7 +739,7 @@ class Orientation(Rotation,Crystal): components_proper,components_improper) else: components = np.around(np.einsum('...ji,...i', - np.broadcast_to(self .basis['improper'], vector_.shape+(3,)), + np.broadcast_to(self .standard_triangle['improper'], vector_.shape+(3,)), np.block([vector_[...,:2],np.abs(vector_[...,2:3])])), 12) in_SST = np.all(components >= 0.0,axis=-1) From de428efca5104616192cb90a4434b6ef860dbe36 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 18 Jul 2021 18:03:36 +0200 Subject: [PATCH 19/43] add_pole is working again --- python/damask/_orientation.py | 3 +- python/damask/_result.py | 68 +++++++++++++++-------------------- python/tests/test_Result.py | 21 +++++------ 3 files changed, 38 insertions(+), 54 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 545205c0a..448ba3159 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -843,10 +843,9 @@ class Orientation(Rotation,Crystal): Lab frame vector (or vectors if with_symmetry) along [uvw] direction or (hkl) plane normal. """ - sym_ops = self.symmetry_operations - # ToDo: simplify 'with_symmetry' v = self.to_frame(uvw=uvw,hkl=hkl) if with_symmetry: + sym_ops = self.symmetry_operations v = sym_ops.broadcast_to(sym_ops.shape+v.shape[:-1],mode='right') \ @ np.broadcast_to(v,sym_ops.shape+v.shape) return ~(self if self.shape+v.shape[:-1] == () else self.broadcast_to(self.shape+v.shape[:-1],mode='right')) \ diff --git a/python/damask/_result.py b/python/damask/_result.py index 1fa376f63..03bb34926 100644 --- a/python/damask/_result.py +++ b/python/damask/_result.py @@ -979,47 +979,35 @@ class Result: self._add_generic_pointwise(self._add_stress_second_Piola_Kirchhoff,{'P':P,'F':F}) -# The add_pole functionality needs discussion. -# The new Crystal object can perform such a calculation but the outcome depends on the lattice parameters -# as well as on whether a direction or plane is concerned (see the DAMASK_examples/pole_figure notebook). -# Below code appears to be too simplistic. - # @staticmethod - # def _add_pole(q,p,polar): - # pole = np.array(p) - # unit_pole = pole/np.linalg.norm(pole) - # m = util.scale_to_coprime(pole) - # rot = Rotation(q['data'].view(np.double).reshape(-1,4)) - # - # rotatedPole = rot @ np.broadcast_to(unit_pole,rot.shape+(3,)) # rotate pole according to crystal orientation - # xy = rotatedPole[:,0:2]/(1.+abs(unit_pole[2])) # stereographic projection - # coords = xy if not polar else \ - # np.block([np.sqrt(xy[:,0:1]*xy[:,0:1]+xy[:,1:2]*xy[:,1:2]),np.arctan2(xy[:,1:2],xy[:,0:1])]) - # return { - # 'data': coords, - # 'label': 'p^{}_[{} {} {})'.format(u'rφ' if polar else 'xy',*m), - # 'meta' : { - # 'unit': '1', - # 'description': '{} coordinates of stereographic projection of pole (direction/plane) in crystal frame'\ - # .format('Polar' if polar else 'Cartesian'), - # 'creator': 'add_pole' - # } - # } - # def add_pole(self,q,p,polar=False): - # """ - # Add coordinates of stereographic projection of given pole in crystal frame. - # - # Parameters - # ---------- - # q : str - # Name of the dataset containing the crystallographic orientation as quaternions. - # p : numpy.array of shape (3) - # Crystallographic direction or plane. - # polar : bool, optional - # Give pole in polar coordinates. Defaults to False. - # - # """ - # self._add_generic_pointwise(self._add_pole,{'q':q},{'p':p,'polar':polar}) + @staticmethod + def _add_pole(q,uvw,hkl): + c = q['meta']['c/a'] if 'c/a' in q['meta'] else 1 + pole = Orientation(q['data'], lattice=q['meta']['lattice'], a=1, c=c).to_pole(uvw=uvw,hkl=hkl) + + return { + 'data': pole, + 'label': 'p^[{} {} {}]'.format(*uvw) if uvw else 'p^({} {} {})'.format(*hkl), + 'meta' : { + 'unit': '1', + 'description': f"lab frame vector along lattice {'direction' if uvw else 'plane'}", + 'creator': 'add_pole' + } + } + def add_pole(self,q='O',*,uvw=None,hkl=None): + """ + Add lab frame vector along lattice direction [uvw] or plane normal (hkl). + + Parameters + ---------- + q : str + Name of the dataset containing the crystallographic orientation as quaternions. + Defaults to 'O'. + uvw|hkl : numpy.ndarray of shape (...,3) + Miller indices of crystallographic direction or plane normal. + + """ + self._add_generic_pointwise(self._add_pole,{'q':q},{'uvw':uvw,'hkl':hkl}) @staticmethod diff --git a/python/tests/test_Result.py b/python/tests/test_Result.py index 5f543861d..7126b4ab0 100644 --- a/python/tests/test_Result.py +++ b/python/tests/test_Result.py @@ -12,7 +12,6 @@ import vtk import numpy as np from damask import Result -from damask import Rotation from damask import Orientation from damask import tensor from damask import mechanics @@ -220,17 +219,15 @@ class TestResult: in_file = default.place('S') assert np.allclose(in_memory,in_file) - @pytest.mark.skip(reason='requires rework of lattice.f90') - @pytest.mark.parametrize('polar',[True,False]) - def test_add_pole(self,default,polar): - pole = np.array([1.,0.,0.]) - default.add_pole('O',pole,polar) - rot = Rotation(default.place('O')) - rotated_pole = rot * np.broadcast_to(pole,rot.shape+(3,)) - xy = rotated_pole[:,0:2]/(1.+abs(pole[2])) - in_memory = xy if not polar else \ - np.block([np.sqrt(xy[:,0:1]*xy[:,0:1]+xy[:,1:2]*xy[:,1:2]),np.arctan2(xy[:,1:2],xy[:,0:1])]) - in_file = default.place('p^{}_[1 0 0)'.format(u'rφ' if polar else 'xy')) + @pytest.mark.parametrize('options',[{'uvw':[1,0,0]},{'hkl':[0,1,1]}]) + def test_add_pole(self,default,options): + default.add_pole(**options) + rot = default.place('O') + in_memory = Orientation(rot,lattice=rot.dtype.metadata['lattice']).to_pole(**options) + brackets = ['[[]','[]]'] if 'uvw' in options.keys() else ['(',')'] # escape fnmatch + label = '{}{} {} {}{}'.format(brackets[0],*(list(options.values())[0]),brackets[1]) + in_file = default.place(f'p^{label}') + print(in_file - in_memory) assert np.allclose(in_memory,in_file) def test_add_rotation(self,default): From 1c1dc9383ed81feabf7dc4075fe7a047017964b0 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 19 Jul 2021 22:30:20 +0200 Subject: [PATCH 20/43] symbolic names --- src/grid/DAMASK_grid.f90 | 28 ++++++++++---------- src/grid/grid_damage_spectral.f90 | 10 +++---- src/grid/grid_mech_FEM.f90 | 4 +-- src/grid/grid_mech_spectral_basic.f90 | 4 +-- src/grid/grid_mech_spectral_polarisation.f90 | 4 +-- src/grid/grid_thermal_spectral.f90 | 14 +++++----- src/grid/spectral_utilities.f90 | 24 ++++++++--------- 7 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/grid/DAMASK_grid.f90 b/src/grid/DAMASK_grid.f90 index e98b2d818..9d8db1b82 100644 --- a/src/grid/DAMASK_grid.f90 +++ b/src/grid/DAMASK_grid.f90 @@ -56,8 +56,8 @@ program DAMASK_grid T_0 = 300.0_pReal, & time = 0.0_pReal, & !< elapsed time time0 = 0.0_pReal, & !< begin of interval - timeinc = 1.0_pReal, & !< current time interval - timeIncOld = 0.0_pReal, & !< previous time interval + Delta_t = 1.0_pReal, & !< current time interval + Delta_t_prev = 0.0_pReal, & !< previous time interval remainingLoadCaseTime = 0.0_pReal !< remaining time of current load case logical :: & guess, & !< guess along former trajectory @@ -326,24 +326,24 @@ program DAMASK_grid !-------------------------------------------------------------------------------------------------- ! forwarding time - timeIncOld = timeinc ! last timeinc that brought former inc to an end + Delta_t_prev = Delta_t ! last time intervall that brought former inc to an end if (dEq(loadCases(l)%r,1.0_pReal,1.e-9_pReal)) then ! linear scale - timeinc = loadCases(l)%t/real(loadCases(l)%N,pReal) + Delta_t = loadCases(l)%t/real(loadCases(l)%N,pReal) else - timeinc = loadCases(l)%t * (loadCases(l)%r**(inc-1)-loadCases(l)%r**inc) & + Delta_t = loadCases(l)%t * (loadCases(l)%r**(inc-1)-loadCases(l)%r**inc) & / (1.0_pReal-loadCases(l)%r**loadCases(l)%N) endif - timeinc = timeinc * real(subStepFactor,pReal)**real(-cutBackLevel,pReal) ! depending on cut back level, decrease time step + Delta_t = Delta_t * real(subStepFactor,pReal)**real(-cutBackLevel,pReal) ! depending on cut back level, decrease time step skipping: if (totalIncsCounter <= interface_restartInc) then ! not yet at restart inc? - time = time + timeinc ! just advance time, skip already performed calculation + time = time + Delta_t ! just advance time, skip already performed calculation guess = .true. ! QUESTION:why forced guessing instead of inheriting loadcase preference else skipping stepFraction = 0 ! fraction scaled by stepFactor**cutLevel subStepLooping: do while (stepFraction < subStepFactor**cutBackLevel) remainingLoadCaseTime = loadCases(l)%t+time0 - time - time = time + timeinc ! forward target time + time = time + Delta_t ! forward target time stepFraction = stepFraction + 1 ! count step !-------------------------------------------------------------------------------------------------- @@ -365,7 +365,7 @@ program DAMASK_grid select case(ID(field)) case(FIELD_MECH_ID) call mechanical_forward (& - cutBack,guess,timeinc,timeIncOld,remainingLoadCaseTime, & + cutBack,guess,Delta_t,Delta_t_prev,remainingLoadCaseTime, & deformation_BC = loadCases(l)%deformation, & stress_BC = loadCases(l)%stress, & rotation_BC = loadCases(l)%rot) @@ -386,9 +386,9 @@ program DAMASK_grid case(FIELD_MECH_ID) solres(field) = mechanical_solution(incInfo) case(FIELD_THERMAL_ID) - solres(field) = grid_thermal_spectral_solution(timeinc) + solres(field) = grid_thermal_spectral_solution(Delta_t) case(FIELD_DAMAGE_ID) - solres(field) = grid_damage_spectral_solution(timeinc) + solres(field) = grid_damage_spectral_solution(Delta_t) end select if (.not. solres(field)%converged) exit ! no solution found @@ -406,7 +406,7 @@ program DAMASK_grid if ( (all(solres(:)%converged .and. solres(:)%stagConverged)) & ! converged .and. .not. solres(1)%termIll) then ! and acceptable solution found call mechanical_updateCoords - timeIncOld = timeinc + Delta_t_prev = Delta_t cutBack = .false. guess = .true. ! start guessing after first converged (sub)inc if (worldrank == 0) then @@ -418,8 +418,8 @@ program DAMASK_grid cutBack = .true. stepFraction = (stepFraction - 1) * subStepFactor ! adjust to new denominator cutBackLevel = cutBackLevel + 1 - time = time - timeinc ! rewind time - timeinc = timeinc/real(subStepFactor,pReal) ! cut timestep + time = time - Delta_t ! rewind time + Delta_t = Delta_t/real(subStepFactor,pReal) ! cut timestep print'(/,a)', ' cutting back ' else ! no more options to continue if (worldrank == 0) close(statUnit) diff --git a/src/grid/grid_damage_spectral.f90 b/src/grid/grid_damage_spectral.f90 index 162d665cb..f43a6cb18 100644 --- a/src/grid/grid_damage_spectral.f90 +++ b/src/grid/grid_damage_spectral.f90 @@ -160,10 +160,10 @@ end subroutine grid_damage_spectral_init !-------------------------------------------------------------------------------------------------- !> @brief solution for the spectral damage scheme with internal iterations !-------------------------------------------------------------------------------------------------- -function grid_damage_spectral_solution(timeinc) result(solution) +function grid_damage_spectral_solution(Delta_t) result(solution) real(pReal), intent(in) :: & - timeinc !< increment in time for current solution + Delta_t !< increment in time for current solution integer :: i, j, k, ce type(tSolutionState) :: solution PetscInt :: devNull @@ -176,7 +176,7 @@ function grid_damage_spectral_solution(timeinc) result(solution) !-------------------------------------------------------------------------------------------------- ! set module wide availabe data - params%timeinc = timeinc + params%Delta_t = Delta_t call SNESSolve(damage_snes,PETSC_NULL_VEC,solution_vec,ierr); CHKERRQ(ierr) call SNESGetConvergedReason(damage_snes,reason,ierr); CHKERRQ(ierr) @@ -284,7 +284,7 @@ subroutine formResidual(in,x_scal,f_scal,dummy,ierr) ce = 0 do k = 1, grid3; do j = 1, grid(2); do i = 1,grid(1) ce = ce + 1 - scalarField_real(i,j,k) = params%timeinc*(scalarField_real(i,j,k) + homogenization_f_phi(phi_current(i,j,k),ce)) & + scalarField_real(i,j,k) = params%Delta_t*(scalarField_real(i,j,k) + homogenization_f_phi(phi_current(i,j,k),ce)) & + homogenization_mu_phi(ce)*(phi_lastInc(i,j,k) - phi_current(i,j,k)) & + mu_ref*phi_current(i,j,k) enddo; enddo; enddo @@ -292,7 +292,7 @@ subroutine formResidual(in,x_scal,f_scal,dummy,ierr) !-------------------------------------------------------------------------------------------------- ! convolution of damage field with green operator call utilities_FFTscalarForward - call utilities_fourierGreenConvolution(K_ref, mu_ref, params%timeinc) + call utilities_fourierGreenConvolution(K_ref, mu_ref, params%Delta_t) call utilities_FFTscalarBackward where(scalarField_real(1:grid(1),1:grid(2),1:grid3) > phi_lastInc) & diff --git a/src/grid/grid_mech_FEM.f90 b/src/grid/grid_mech_FEM.f90 index 111e9fece..abb4a3705 100644 --- a/src/grid/grid_mech_FEM.f90 +++ b/src/grid/grid_mech_FEM.f90 @@ -412,7 +412,7 @@ subroutine grid_mechanical_FEM_forward(cutBack,guess,Delta_t,Delta_t_old,t_remai ! set module wide available data params%stress_mask = stress_BC%mask params%rotation_BC = rotation_BC - params%timeinc = Delta_t + params%Delta_t = Delta_t end subroutine grid_mechanical_FEM_forward @@ -568,7 +568,7 @@ subroutine formResidual(da_local,x_local, & ! evaluate constitutive response call utilities_constitutiveResponse(P_current,& P_av,C_volAvg,devNull, & - F,params%timeinc,params%rotation_BC) + F,params%Delta_t,params%rotation_BC) call MPI_Allreduce(MPI_IN_PLACE,terminallyIll,1,MPI_LOGICAL,MPI_LOR,MPI_COMM_WORLD,ierr) !-------------------------------------------------------------------------------------------------- diff --git a/src/grid/grid_mech_spectral_basic.f90 b/src/grid/grid_mech_spectral_basic.f90 index c4fce61a7..8b707c39d 100644 --- a/src/grid/grid_mech_spectral_basic.f90 +++ b/src/grid/grid_mech_spectral_basic.f90 @@ -354,7 +354,7 @@ subroutine grid_mechanical_spectral_basic_forward(cutBack,guess,Delta_t,Delta_t_ ! set module wide available data params%stress_mask = stress_BC%mask params%rotation_BC = rotation_BC - params%timeinc = Delta_t + params%Delta_t = Delta_t end subroutine grid_mechanical_spectral_basic_forward @@ -492,7 +492,7 @@ subroutine formResidual(in, F, & ! evaluate constitutive response call utilities_constitutiveResponse(residuum, & ! "residuum" gets field of first PK stress (to save memory) P_av,C_volAvg,C_minMaxAvg, & - F,params%timeinc,params%rotation_BC) + F,params%Delta_t,params%rotation_BC) call MPI_Allreduce(MPI_IN_PLACE,terminallyIll,1,MPI_LOGICAL,MPI_LOR,MPI_COMM_WORLD,ierr) !-------------------------------------------------------------------------------------------------- diff --git a/src/grid/grid_mech_spectral_polarisation.f90 b/src/grid/grid_mech_spectral_polarisation.f90 index a52554e3c..59f4b6108 100644 --- a/src/grid/grid_mech_spectral_polarisation.f90 +++ b/src/grid/grid_mech_spectral_polarisation.f90 @@ -408,7 +408,7 @@ subroutine grid_mechanical_spectral_polarisation_forward(cutBack,guess,Delta_t,D ! set module wide available data params%stress_mask = stress_BC%mask params%rotation_BC = rotation_BC - params%timeinc = Delta_t + params%Delta_t = Delta_t end subroutine grid_mechanical_spectral_polarisation_forward @@ -592,7 +592,7 @@ subroutine formResidual(in, FandF_tau, & ! evaluate constitutive response call utilities_constitutiveResponse(residual_F, & ! "residuum" gets field of first PK stress (to save memory) P_av,C_volAvg,C_minMaxAvg, & - F - residual_F_tau/num%beta,params%timeinc,params%rotation_BC) + F - residual_F_tau/num%beta,params%Delta_t,params%rotation_BC) call MPI_Allreduce(MPI_IN_PLACE,terminallyIll,1,MPI_LOGICAL,MPI_LOR,MPI_COMM_WORLD,ierr) !-------------------------------------------------------------------------------------------------- diff --git a/src/grid/grid_thermal_spectral.f90 b/src/grid/grid_thermal_spectral.f90 index 47c49e76f..41fca2133 100644 --- a/src/grid/grid_thermal_spectral.f90 +++ b/src/grid/grid_thermal_spectral.f90 @@ -155,10 +155,10 @@ end subroutine grid_thermal_spectral_init !-------------------------------------------------------------------------------------------------- !> @brief solution for the spectral thermal scheme with internal iterations !-------------------------------------------------------------------------------------------------- -function grid_thermal_spectral_solution(timeinc) result(solution) +function grid_thermal_spectral_solution(Delta_t) result(solution) real(pReal), intent(in) :: & - timeinc !< increment in time for current solution + Delta_t !< increment in time for current solution integer :: i, j, k, ce type(tSolutionState) :: solution PetscInt :: devNull @@ -171,7 +171,7 @@ function grid_thermal_spectral_solution(timeinc) result(solution) !-------------------------------------------------------------------------------------------------- ! set module wide availabe data - params%timeinc = timeinc + params%Delta_t = Delta_t call SNESSolve(thermal_snes,PETSC_NULL_VEC,solution_vec,ierr); CHKERRQ(ierr) call SNESGetConvergedReason(thermal_snes,reason,ierr); CHKERRQ(ierr) @@ -195,7 +195,7 @@ function grid_thermal_spectral_solution(timeinc) result(solution) ce = 0 do k = 1, grid3; do j = 1, grid(2); do i = 1,grid(1) ce = ce + 1 - call homogenization_thermal_setField(T_current(i,j,k),(T_current(i,j,k)-T_lastInc(i,j,k))/params%timeinc,ce) + call homogenization_thermal_setField(T_current(i,j,k),(T_current(i,j,k)-T_lastInc(i,j,k))/params%Delta_t,ce) enddo; enddo; enddo call VecMin(solution_vec,devNull,T_min,ierr); CHKERRQ(ierr) @@ -233,7 +233,7 @@ subroutine grid_thermal_spectral_forward(cutBack) ce = 0 do k = 1, grid3; do j = 1, grid(2); do i = 1,grid(1) ce = ce + 1 - call homogenization_thermal_setField(T_current(i,j,k),(T_current(i,j,k)-T_lastInc(i,j,k))/params%timeinc,ce) + call homogenization_thermal_setField(T_current(i,j,k),(T_current(i,j,k)-T_lastInc(i,j,k))/params%Delta_t,ce) enddo; enddo; enddo else T_lastInc = T_current @@ -279,7 +279,7 @@ subroutine formResidual(in,x_scal,f_scal,dummy,ierr) ce = 0 do k = 1, grid3; do j = 1, grid(2); do i = 1,grid(1) ce = ce + 1 - scalarField_real(i,j,k) = params%timeinc*(scalarField_real(i,j,k) + homogenization_f_T(ce)) & + scalarField_real(i,j,k) = params%Delta_t*(scalarField_real(i,j,k) + homogenization_f_T(ce)) & + homogenization_mu_T(ce) * (T_lastInc(i,j,k) - T_current(i,j,k)) & + mu_ref*T_current(i,j,k) enddo; enddo; enddo @@ -287,7 +287,7 @@ subroutine formResidual(in,x_scal,f_scal,dummy,ierr) !-------------------------------------------------------------------------------------------------- ! convolution of temperature field with green operator call utilities_FFTscalarForward - call utilities_fourierGreenConvolution(K_ref, mu_ref, params%timeinc) + call utilities_fourierGreenConvolution(K_ref, mu_ref, params%Delta_t) call utilities_FFTscalarBackward !-------------------------------------------------------------------------------------------------- diff --git a/src/grid/spectral_utilities.f90 b/src/grid/spectral_utilities.f90 index bd37e2654..7fae47d99 100644 --- a/src/grid/spectral_utilities.f90 +++ b/src/grid/spectral_utilities.f90 @@ -96,7 +96,7 @@ module spectral_utilities real(pReal), dimension(3,3) :: stress_BC logical, dimension(3,3) :: stress_mask type(rotation) :: rotation_BC - real(pReal) :: timeinc + real(pReal) :: Delta_t end type tSolutionParams type :: tNumerics @@ -791,16 +791,16 @@ end subroutine utilities_fourierTensorDivergence !-------------------------------------------------------------------------------------------------- -!> @brief calculate constitutive response from homogenization_F0 to F during timeinc +!> @brief calculate constitutive response from homogenization_F0 to F during Delta_t !-------------------------------------------------------------------------------------------------- subroutine utilities_constitutiveResponse(P,P_av,C_volAvg,C_minmaxAvg,& - F,timeinc,rotation_BC) + F,Delta_t,rotation_BC) real(pReal), intent(out), dimension(3,3,3,3) :: C_volAvg, C_minmaxAvg !< average stiffness real(pReal), intent(out), dimension(3,3) :: P_av !< average PK stress real(pReal), intent(out), dimension(3,3,grid(1),grid(2),grid3) :: P !< PK stress real(pReal), intent(in), dimension(3,3,grid(1),grid(2),grid3) :: F !< deformation gradient target - real(pReal), intent(in) :: timeinc !< loading time + real(pReal), intent(in) :: Delta_t !< loading time type(rotation), intent(in), optional :: rotation_BC !< rotation of load frame @@ -815,11 +815,11 @@ subroutine utilities_constitutiveResponse(P,P_av,C_volAvg,C_minmaxAvg,& homogenization_F = reshape(F,[3,3,product(grid(1:2))*grid3]) ! set materialpoint target F to estimated field - call homogenization_mechanical_response(timeinc,[1,1],[1,product(grid(1:2))*grid3]) ! calculate P field + call homogenization_mechanical_response(Delta_t,[1,1],[1,product(grid(1:2))*grid3]) ! calculate P field if (.not. terminallyIll) & - call homogenization_thermal_response(timeinc,[1,1],[1,product(grid(1:2))*grid3]) + call homogenization_thermal_response(Delta_t,[1,1],[1,product(grid(1:2))*grid3]) if (.not. terminallyIll) & - call homogenization_mechanical_response2(timeinc,[1,1],[1,product(grid(1:2))*grid3]) + call homogenization_mechanical_response2(Delta_t,[1,1],[1,product(grid(1:2))*grid3]) P = reshape(homogenization_P, [3,3,grid(1),grid(2),grid3]) P_av = sum(sum(sum(P,dim=5),dim=4),dim=3) * wgt @@ -870,14 +870,14 @@ end subroutine utilities_constitutiveResponse !-------------------------------------------------------------------------------------------------- -!> @brief calculates forward rate, either guessing or just add delta/timeinc +!> @brief calculates forward rate, either guessing or just add delta/Delta_t !-------------------------------------------------------------------------------------------------- pure function utilities_calculateRate(heterogeneous,field0,field,dt,avRate) real(pReal), intent(in), dimension(3,3) :: & avRate !< homogeneous addon real(pReal), intent(in) :: & - dt !< timeinc between field0 and field + dt !< Delta_t between field0 and field logical, intent(in) :: & heterogeneous !< calculate field of rates real(pReal), intent(in), dimension(3,3,grid(1),grid(2),grid3) :: & @@ -899,10 +899,10 @@ end function utilities_calculateRate !> @brief forwards a field with a pointwise given rate, if aim is given, !> ensures that the average matches the aim !-------------------------------------------------------------------------------------------------- -function utilities_forwardField(timeinc,field_lastInc,rate,aim) +function utilities_forwardField(Delta_t,field_lastInc,rate,aim) real(pReal), intent(in) :: & - timeinc !< timeinc of current step + Delta_t !< Delta_t of current step real(pReal), intent(in), dimension(3,3,grid(1),grid(2),grid3) :: & field_lastInc, & !< initial field rate !< rate by which to forward @@ -913,7 +913,7 @@ function utilities_forwardField(timeinc,field_lastInc,rate,aim) real(pReal), dimension(3,3) :: fieldDiff !< - aim PetscErrorCode :: ierr - utilities_forwardField = field_lastInc + rate*timeinc + utilities_forwardField = field_lastInc + rate*Delta_t if (present(aim)) then !< correct to match average fieldDiff = sum(sum(sum(utilities_forwardField,dim=5),dim=4),dim=3)*wgt call MPI_Allreduce(MPI_IN_PLACE,fieldDiff,9,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD,ierr) From 03b7532cc52357649cdd45474e33c44e1666d73d Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 19 Jul 2021 22:56:34 +0200 Subject: [PATCH 21/43] numpy.MaskedArray behavior --- src/grid/DAMASK_grid.f90 | 18 +++++++++--------- src/grid/grid_mech_FEM.f90 | 16 ++++++++-------- src/grid/grid_mech_spectral_basic.f90 | 16 ++++++++-------- src/grid/grid_mech_spectral_polarisation.f90 | 18 +++++++++--------- src/grid/spectral_utilities.f90 | 4 ++-- 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/grid/DAMASK_grid.f90 b/src/grid/DAMASK_grid.f90 index 9d8db1b82..2d049f702 100644 --- a/src/grid/DAMASK_grid.f90 +++ b/src/grid/DAMASK_grid.f90 @@ -231,14 +231,14 @@ program DAMASK_grid endif do i = 1, 3; do j = 1, 3 if (loadCases(l)%deformation%mask(i,j)) then - write(IO_STDOUT,'(2x,f12.7)',advance='no') loadCases(l)%deformation%values(i,j) - else write(IO_STDOUT,'(2x,12a)',advance='no') ' x ' + else + write(IO_STDOUT,'(2x,f12.7)',advance='no') loadCases(l)%deformation%values(i,j) endif enddo; write(IO_STDOUT,'(/)',advance='no') enddo if (any(loadCases(l)%stress%mask .eqv. loadCases(l)%deformation%mask)) errorID = 831 - if (any(loadCases(l)%stress%mask .and. transpose(loadCases(l)%stress%mask) .and. (math_I3<1))) & + if (any(.not.(loadCases(l)%stress%mask .or. transpose(loadCases(l)%stress%mask)) .and. (math_I3<1))) & errorID = 838 ! no rotation is allowed by stress BC if (loadCases(l)%stress%myType == 'P') print*, ' P / MPa:' @@ -247,9 +247,9 @@ program DAMASK_grid if (loadCases(l)%stress%myType /= '') then do i = 1, 3; do j = 1, 3 if (loadCases(l)%stress%mask(i,j)) then - write(IO_STDOUT,'(2x,f12.4)',advance='no') loadCases(l)%stress%values(i,j)*1e-6_pReal - else write(IO_STDOUT,'(2x,12a)',advance='no') ' x ' + else + write(IO_STDOUT,'(2x,f12.4)',advance='no') loadCases(l)%stress%values(i,j)*1e-6_pReal endif enddo; write(IO_STDOUT,'(/)',advance='no') enddo @@ -484,15 +484,15 @@ subroutine getMaskedTensor(values,mask,tensor) values = 0.0 if (tensor%length == 9) then ! temporary support for deprecated 1D tensor do i = 1,9 - mask((i-1)/3+1,mod(i-1,3)+1) = tensor%get_asString(i) /= 'x' - if (mask((i-1)/3+1,mod(i-1,3)+1)) values((i-1)/3+1,mod(i-1,3)+1) = tensor%get_asFloat(i) + mask((i-1)/3+1,mod(i-1,3)+1) = tensor%get_asString(i) == 'x' + if (.not. mask((i-1)/3+1,mod(i-1,3)+1)) values((i-1)/3+1,mod(i-1,3)+1) = tensor%get_asFloat(i) enddo else do i = 1,3 row => tensor%get(i) do j = 1,3 - mask(i,j) = row%get_asString(j) /= 'x' ! ToDo change to np.masked behavior - if (mask(i,j)) values(i,j) = row%get_asFloat(j) + mask(i,j) = row%get_asString(j) == 'x' + if (.not. mask(i,j)) values(i,j) = row%get_asFloat(j) enddo enddo endif diff --git a/src/grid/grid_mech_FEM.f90 b/src/grid/grid_mech_FEM.f90 index abb4a3705..16a5c440e 100644 --- a/src/grid/grid_mech_FEM.f90 +++ b/src/grid/grid_mech_FEM.f90 @@ -324,7 +324,7 @@ function grid_mechanical_FEM_solution(incInfoIn) result(solution) solution%iterationsNeeded = totalIter solution%termIll = terminallyIll terminallyIll = .false. - P_aim = merge(P_aim,P_av,params%stress_mask) + P_aim = merge(P_av,P_aim,params%stress_mask) end function grid_mechanical_FEM_solution @@ -363,20 +363,20 @@ subroutine grid_mechanical_FEM_forward(cutBack,guess,Delta_t,Delta_t_old,t_remai else C_volAvgLastInc = C_volAvg - F_aimDot = merge(merge((F_aim-F_aim_lastInc)/Delta_t_old,0.0_pReal,stress_BC%mask), 0.0_pReal, guess) ! estimate deformation rate for prescribed stress components + F_aimDot = merge(merge(.0_pReal,(F_aim-F_aim_lastInc)/Delta_t_old,stress_BC%mask),.0_pReal,guess) ! estimate deformation rate for prescribed stress components F_aim_lastInc = F_aim !----------------------------------------------------------------------------------------------- ! calculate rate for aim if (deformation_BC%myType=='L') then ! calculate F_aimDot from given L and current F F_aimDot = F_aimDot & - + merge(matmul(deformation_BC%values, F_aim_lastInc),.0_pReal,deformation_BC%mask) + + merge(.0_pReal,matmul(deformation_BC%values, F_aim_lastInc),deformation_BC%mask) elseif (deformation_BC%myType=='dot_F') then ! F_aimDot is prescribed F_aimDot = F_aimDot & - + merge(deformation_BC%values,.0_pReal,deformation_BC%mask) + + merge(.0_pReal,deformation_BC%values,deformation_BC%mask) elseif (deformation_BC%myType=='F') then ! aim at end of load case is prescribed F_aimDot = F_aimDot & - + merge((deformation_BC%values - F_aim_lastInc)/t_remaining,.0_pReal,deformation_BC%mask) + + merge(.0_pReal,(deformation_BC%values - F_aim_lastInc)/t_remaining,deformation_BC%mask) endif if (guess) then @@ -397,9 +397,9 @@ subroutine grid_mechanical_FEM_forward(cutBack,guess,Delta_t,Delta_t_old,t_remai ! update average and local deformation gradients F_aim = F_aim_lastInc + F_aimDot * Delta_t if (stress_BC%myType=='P') P_aim = P_aim & - + merge((stress_BC%values - P_aim)/t_remaining,0.0_pReal,stress_BC%mask)*Delta_t + + merge(.0_pReal,(stress_BC%values - P_aim)/t_remaining,stress_BC%mask)*Delta_t if (stress_BC%myType=='dot_P') P_aim = P_aim & - + merge(stress_BC%values,0.0_pReal,stress_BC%mask)*Delta_t + + merge(.0_pReal,stress_BC%values,stress_BC%mask)*Delta_t call VecAXPY(solution_current,Delta_t,solution_rate,ierr); CHKERRQ(ierr) @@ -574,7 +574,7 @@ subroutine formResidual(da_local,x_local, & !-------------------------------------------------------------------------------------------------- ! stress BC handling F_aim = F_aim - math_mul3333xx33(S, P_av - P_aim) ! S = 0.0 for no bc - err_BC = maxval(abs(merge(P_av - P_aim,.0_pReal,params%stress_mask))) + err_BC = maxval(abs(merge(.0_pReal,P_av - P_aim,params%stress_mask))) !-------------------------------------------------------------------------------------------------- ! constructing residual diff --git a/src/grid/grid_mech_spectral_basic.f90 b/src/grid/grid_mech_spectral_basic.f90 index 8b707c39d..ced767f5f 100644 --- a/src/grid/grid_mech_spectral_basic.f90 +++ b/src/grid/grid_mech_spectral_basic.f90 @@ -277,7 +277,7 @@ function grid_mechanical_spectral_basic_solution(incInfoIn) result(solution) solution%iterationsNeeded = totalIter solution%termIll = terminallyIll terminallyIll = .false. - P_aim = merge(P_aim,P_av,params%stress_mask) + P_aim = merge(P_av,P_aim,params%stress_mask) end function grid_mechanical_spectral_basic_solution @@ -314,20 +314,20 @@ subroutine grid_mechanical_spectral_basic_forward(cutBack,guess,Delta_t,Delta_t_ C_volAvgLastInc = C_volAvg C_minMaxAvgLastInc = C_minMaxAvg - F_aimDot = merge(merge((F_aim-F_aim_lastInc)/Delta_t_old,0.0_pReal,stress_BC%mask), 0.0_pReal, guess) ! estimate deformation rate for prescribed stress components + F_aimDot = merge(merge(.0_pReal,(F_aim-F_aim_lastInc)/Delta_t_old,stress_BC%mask),.0_pReal,guess) ! estimate deformation rate for prescribed stress components F_aim_lastInc = F_aim !----------------------------------------------------------------------------------------------- ! calculate rate for aim if (deformation_BC%myType=='L') then ! calculate F_aimDot from given L and current F F_aimDot = F_aimDot & - + merge(matmul(deformation_BC%values, F_aim_lastInc),.0_pReal,deformation_BC%mask) + + merge(.0_pReal,matmul(deformation_BC%values, F_aim_lastInc),deformation_BC%mask) elseif (deformation_BC%myType=='dot_F') then ! F_aimDot is prescribed F_aimDot = F_aimDot & - + merge(deformation_BC%values,.0_pReal,deformation_BC%mask) + + merge(.0_pReal,deformation_BC%values,deformation_BC%mask) elseif (deformation_BC%myType=='F') then ! aim at end of load case is prescribed F_aimDot = F_aimDot & - + merge((deformation_BC%values - F_aim_lastInc)/t_remaining,.0_pReal,deformation_BC%mask) + + merge(.0_pReal,(deformation_BC%values - F_aim_lastInc)/t_remaining,deformation_BC%mask) endif Fdot = utilities_calculateRate(guess, & @@ -342,9 +342,9 @@ subroutine grid_mechanical_spectral_basic_forward(cutBack,guess,Delta_t,Delta_t_ ! update average and local deformation gradients F_aim = F_aim_lastInc + F_aimDot * Delta_t if (stress_BC%myType=='P') P_aim = P_aim & - + merge((stress_BC%values - P_aim)/t_remaining,0.0_pReal,stress_BC%mask)*Delta_t + + merge(.0_pReal,(stress_BC%values - P_aim)/t_remaining,stress_BC%mask)*Delta_t if (stress_BC%myType=='dot_P') P_aim = P_aim & - + merge(stress_BC%values,0.0_pReal,stress_BC%mask)*Delta_t + + merge(.0_pReal,stress_BC%values,stress_BC%mask)*Delta_t F = reshape(utilities_forwardField(Delta_t,F_lastInc,Fdot, & ! estimate of F at end of time+Delta_t that matches rotated F_aim on average rotation_BC%rotate(F_aim,active=.true.)),[9,grid(1),grid(2),grid3]) @@ -499,7 +499,7 @@ subroutine formResidual(in, F, & ! stress BC handling deltaF_aim = math_mul3333xx33(S, P_av - P_aim) ! S = 0.0 for no bc F_aim = F_aim - deltaF_aim - err_BC = maxval(abs(merge(P_av - P_aim,.0_pReal,params%stress_mask))) + err_BC = maxval(abs(merge(.0_pReal,P_av - P_aim,params%stress_mask))) !-------------------------------------------------------------------------------------------------- ! updated deformation gradient using fix point algorithm of basic scheme diff --git a/src/grid/grid_mech_spectral_polarisation.f90 b/src/grid/grid_mech_spectral_polarisation.f90 index 59f4b6108..3821cac4d 100644 --- a/src/grid/grid_mech_spectral_polarisation.f90 +++ b/src/grid/grid_mech_spectral_polarisation.f90 @@ -309,7 +309,7 @@ function grid_mechanical_spectral_polarisation_solution(incInfoIn) result(soluti solution%iterationsNeeded = totalIter solution%termIll = terminallyIll terminallyIll = .false. - P_aim = merge(P_aim,P_av,params%stress_mask) + P_aim = merge(P_av,P_aim,params%stress_mask) end function grid_mechanical_spectral_polarisation_solution @@ -350,20 +350,20 @@ subroutine grid_mechanical_spectral_polarisation_forward(cutBack,guess,Delta_t,D C_volAvgLastInc = C_volAvg C_minMaxAvgLastInc = C_minMaxAvg - F_aimDot = merge(merge((F_aim-F_aim_lastInc)/Delta_t_old,0.0_pReal,stress_BC%mask), 0.0_pReal, guess) ! estimate deformation rate for prescribed stress components + F_aimDot = merge(merge(.0_pReal,(F_aim-F_aim_lastInc)/Delta_t_old,stress_BC%mask),.0_pReal,guess) ! estimate deformation rate for prescribed stress components F_aim_lastInc = F_aim !----------------------------------------------------------------------------------------------- ! calculate rate for aim if (deformation_BC%myType=='L') then ! calculate F_aimDot from given L and current F F_aimDot = F_aimDot & - + merge(matmul(deformation_BC%values, F_aim_lastInc),.0_pReal,deformation_BC%mask) + + merge(.0_pReal,matmul(deformation_BC%values, F_aim_lastInc),deformation_BC%mask) elseif (deformation_BC%myType=='dot_F') then ! F_aimDot is prescribed F_aimDot = F_aimDot & - + merge(deformation_BC%values,.0_pReal,deformation_BC%mask) + + merge(.0_pReal,deformation_BC%values,deformation_BC%mask) elseif (deformation_BC%myType=='F') then ! aim at end of load case is prescribed F_aimDot = F_aimDot & - + merge((deformation_BC%values - F_aim_lastInc)/t_remaining,.0_pReal,deformation_BC%mask) + + merge(.0_pReal,(deformation_BC%values - F_aim_lastInc)/t_remaining,deformation_BC%mask) endif Fdot = utilities_calculateRate(guess, & @@ -382,9 +382,9 @@ subroutine grid_mechanical_spectral_polarisation_forward(cutBack,guess,Delta_t,D ! update average and local deformation gradients F_aim = F_aim_lastInc + F_aimDot * Delta_t if(stress_BC%myType=='P') P_aim = P_aim & - + merge((stress_BC%values - P_aim)/t_remaining,0.0_pReal,stress_BC%mask)*Delta_t + + merge(.0_pReal,(stress_BC%values - P_aim)/t_remaining,stress_BC%mask)*Delta_t if(stress_BC%myType=='dot_P') P_aim = P_aim & - + merge(stress_BC%values,0.0_pReal,stress_BC%mask)*Delta_t + + merge(.0_pReal,stress_BC%values,stress_BC%mask)*Delta_t F = reshape(utilities_forwardField(Delta_t,F_lastInc,Fdot, & ! estimate of F at end of time+Delta_t that matches rotated F_aim on average rotation_BC%rotate(F_aim,active=.true.)),& @@ -598,8 +598,8 @@ subroutine formResidual(in, FandF_tau, & !-------------------------------------------------------------------------------------------------- ! stress BC handling F_aim = F_aim - math_mul3333xx33(S, P_av - P_aim) ! S = 0.0 for no bc - err_BC = maxval(abs(merge(P_av-P_aim, & - math_mul3333xx33(C_scale,F_aim-params%rotation_BC%rotate(F_av)),& + err_BC = maxval(abs(merge(math_mul3333xx33(C_scale,F_aim-params%rotation_BC%rotate(F_av)), & + P_av-P_aim, & params%stress_mask))) ! calculate divergence tensorField_real = 0.0_pReal diff --git a/src/grid/spectral_utilities.f90 b/src/grid/spectral_utilities.f90 index 7fae47d99..e33ee94d0 100644 --- a/src/grid/spectral_utilities.f90 +++ b/src/grid/spectral_utilities.f90 @@ -88,7 +88,7 @@ module spectral_utilities type, public :: tBoundaryCondition !< set of parameters defining a boundary condition real(pReal), dimension(3,3) :: values = 0.0_pReal - logical, dimension(3,3) :: mask = .false. + logical, dimension(3,3) :: mask = .true. character(len=:), allocatable :: myType end type tBoundaryCondition @@ -684,7 +684,7 @@ function utilities_maskedCompliance(rot_BC,mask_stress,C) logical :: errmatinv character(len=pStringLen):: formatString - mask_stressVector = reshape(transpose(mask_stress), [9]) + mask_stressVector = .not. reshape(transpose(mask_stress), [9]) size_reduced = count(mask_stressVector) if(size_reduced > 0) then temp99_real = math_3333to99(rot_BC%rotate(C)) From 85735605f837c5cf20073ca184ddeead38767924 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 20 Jul 2021 06:38:29 +0200 Subject: [PATCH 22/43] more flexibility for the L in the load case Note that mixed boundary conditions for L introduce an ambiguity. Consider: L = [[1.0, x, x], [ 0, 0, 0], [ 0, 0, 0]] P = [[x, 0, 0], [x, x, x], [x, x, x]] What we need is F^(n+1)=F_dot^(n+1) x Delta_t, where F_dot^(n+1) is F_dot^(n+1)_ij = L_ik F^n_kj. So component F_11 has contributions from L_12 and L_13. We first assume L_12=L_13=0 and then choose F^(n+1)_12 and F^(n+1)_13 to get P_12=P_13=0. This implicitly gives a solution for L_12 and L_13, which is however only one out of infinitely many. --- src/IO.f90 | 2 -- src/grid/DAMASK_grid.f90 | 6 ------ src/grid/grid_mech_FEM.f90 | 2 +- src/grid/grid_mech_spectral_basic.f90 | 2 +- src/grid/grid_mech_spectral_polarisation.f90 | 2 +- 5 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/IO.f90 b/src/IO.f90 index 3fbeb4300..d0fdb0255 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -504,8 +504,6 @@ subroutine IO_error(error_ID,el,ip,g,instance,ext_msg) ! errors related to the grid solver case (831) msg = 'mask consistency violated in grid load case' - case (832) - msg = 'ill-defined L (line partly defined) in grid load case' case (833) msg = 'non-positive ratio for geometric progression' case (834) diff --git a/src/grid/DAMASK_grid.f90 b/src/grid/DAMASK_grid.f90 index 2d049f702..5bb476792 100644 --- a/src/grid/DAMASK_grid.f90 +++ b/src/grid/DAMASK_grid.f90 @@ -218,12 +218,6 @@ program DAMASK_grid reportAndCheck: if (worldrank == 0) then print'(/,a,i0)', ' load case: ', l print*, ' estimate_rate:', loadCases(l)%estimate_rate - if (loadCases(l)%deformation%myType == 'L') then - do j = 1, 3 - if (any(loadCases(l)%deformation%mask(j,1:3) .eqv. .true.) .and. & - any(loadCases(l)%deformation%mask(j,1:3) .eqv. .false.)) errorID = 832 ! each row should be either fully or not at all defined - enddo - endif if (loadCases(l)%deformation%myType == 'F') then print*, ' F:' else diff --git a/src/grid/grid_mech_FEM.f90 b/src/grid/grid_mech_FEM.f90 index 16a5c440e..9cccd2fc0 100644 --- a/src/grid/grid_mech_FEM.f90 +++ b/src/grid/grid_mech_FEM.f90 @@ -370,7 +370,7 @@ subroutine grid_mechanical_FEM_forward(cutBack,guess,Delta_t,Delta_t_old,t_remai ! calculate rate for aim if (deformation_BC%myType=='L') then ! calculate F_aimDot from given L and current F F_aimDot = F_aimDot & - + merge(.0_pReal,matmul(deformation_BC%values, F_aim_lastInc),deformation_BC%mask) + + matmul(merge(.0_pReal,deformation_BC%values,deformation_BC%mask),F_aim_lastInc) elseif (deformation_BC%myType=='dot_F') then ! F_aimDot is prescribed F_aimDot = F_aimDot & + merge(.0_pReal,deformation_BC%values,deformation_BC%mask) diff --git a/src/grid/grid_mech_spectral_basic.f90 b/src/grid/grid_mech_spectral_basic.f90 index ced767f5f..6f382d639 100644 --- a/src/grid/grid_mech_spectral_basic.f90 +++ b/src/grid/grid_mech_spectral_basic.f90 @@ -321,7 +321,7 @@ subroutine grid_mechanical_spectral_basic_forward(cutBack,guess,Delta_t,Delta_t_ ! calculate rate for aim if (deformation_BC%myType=='L') then ! calculate F_aimDot from given L and current F F_aimDot = F_aimDot & - + merge(.0_pReal,matmul(deformation_BC%values, F_aim_lastInc),deformation_BC%mask) + + matmul(merge(.0_pReal,deformation_BC%values,deformation_BC%mask),F_aim_lastInc) elseif (deformation_BC%myType=='dot_F') then ! F_aimDot is prescribed F_aimDot = F_aimDot & + merge(.0_pReal,deformation_BC%values,deformation_BC%mask) diff --git a/src/grid/grid_mech_spectral_polarisation.f90 b/src/grid/grid_mech_spectral_polarisation.f90 index 3821cac4d..7a4ae7595 100644 --- a/src/grid/grid_mech_spectral_polarisation.f90 +++ b/src/grid/grid_mech_spectral_polarisation.f90 @@ -357,7 +357,7 @@ subroutine grid_mechanical_spectral_polarisation_forward(cutBack,guess,Delta_t,D ! calculate rate for aim if (deformation_BC%myType=='L') then ! calculate F_aimDot from given L and current F F_aimDot = F_aimDot & - + merge(.0_pReal,matmul(deformation_BC%values, F_aim_lastInc),deformation_BC%mask) + + matmul(merge(.0_pReal,deformation_BC%values,deformation_BC%mask),F_aim_lastInc) elseif (deformation_BC%myType=='dot_F') then ! F_aimDot is prescribed F_aimDot = F_aimDot & + merge(.0_pReal,deformation_BC%values,deformation_BC%mask) From 9d349f8a7c7abc53016bdb9a6d9bbd49ba832a24 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 21 Jul 2021 06:45:53 +0200 Subject: [PATCH 23/43] symbolic notation --- src/grid/DAMASK_grid.f90 | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/grid/DAMASK_grid.f90 b/src/grid/DAMASK_grid.f90 index 5bb476792..8a33e3923 100644 --- a/src/grid/DAMASK_grid.f90 +++ b/src/grid/DAMASK_grid.f90 @@ -53,12 +53,11 @@ program DAMASK_grid integer, parameter :: & subStepFactor = 2 !< for each substep, divide the last time increment by 2.0 real(pReal) :: & - T_0 = 300.0_pReal, & - time = 0.0_pReal, & !< elapsed time - time0 = 0.0_pReal, & !< begin of interval + t = 0.0_pReal, & !< elapsed time + t_0 = 0.0_pReal, & !< begin of interval Delta_t = 1.0_pReal, & !< current time interval Delta_t_prev = 0.0_pReal, & !< previous time interval - remainingLoadCaseTime = 0.0_pReal !< remaining time of current load case + t_remaining = 0.0_pReal !< remaining time of current load case logical :: & guess, & !< guess along former trajectory stagIterate, & @@ -285,7 +284,7 @@ program DAMASK_grid case(FIELD_THERMAL_ID) initial_conditions => config_load%get('initial_conditions',defaultVal=emptyDict) thermal => initial_conditions%get('thermal',defaultVal=emptyDict) - call grid_thermal_spectral_init(thermal%get_asFloat('T',defaultVal = T_0)) + call grid_thermal_spectral_init(thermal%get_asFloat('T')) case(FIELD_DAMAGE_ID) call grid_damage_spectral_init @@ -312,7 +311,7 @@ program DAMASK_grid endif writeUndeformed loadCaseLooping: do l = 1, size(loadCases) - time0 = time ! load case start time + t_0 = t ! load case start time guess = loadCases(l)%estimate_rate ! change of load case? homogeneous guess for the first inc incLooping: do inc = 1, loadCases(l)%N @@ -330,21 +329,21 @@ program DAMASK_grid Delta_t = Delta_t * real(subStepFactor,pReal)**real(-cutBackLevel,pReal) ! depending on cut back level, decrease time step skipping: if (totalIncsCounter <= interface_restartInc) then ! not yet at restart inc? - time = time + Delta_t ! just advance time, skip already performed calculation + t = t + Delta_t ! just advance time, skip already performed calculation guess = .true. ! QUESTION:why forced guessing instead of inheriting loadcase preference else skipping stepFraction = 0 ! fraction scaled by stepFactor**cutLevel subStepLooping: do while (stepFraction < subStepFactor**cutBackLevel) - remainingLoadCaseTime = loadCases(l)%t+time0 - time - time = time + Delta_t ! forward target time + t_remaining = loadCases(l)%t + t_0 - t + t = t + Delta_t ! forward target time stepFraction = stepFraction + 1 ! count step !-------------------------------------------------------------------------------------------------- ! report begin of new step print'(/,a)', ' ###########################################################################' print'(1x,a,es12.5,6(a,i0))', & - 'Time', time, & + 'Time', t, & 's: Increment ', inc,'/',loadCases(l)%N,& '-', stepFraction,'/',subStepFactor**cutBackLevel,& ' of load case ', l,'/',size(loadCases) @@ -359,10 +358,10 @@ program DAMASK_grid select case(ID(field)) case(FIELD_MECH_ID) call mechanical_forward (& - cutBack,guess,Delta_t,Delta_t_prev,remainingLoadCaseTime, & - deformation_BC = loadCases(l)%deformation, & - stress_BC = loadCases(l)%stress, & - rotation_BC = loadCases(l)%rot) + cutBack,guess,Delta_t,Delta_t_prev,t_remaining, & + deformation_BC = loadCases(l)%deformation, & + stress_BC = loadCases(l)%stress, & + rotation_BC = loadCases(l)%rot) case(FIELD_THERMAL_ID); call grid_thermal_spectral_forward(cutBack) case(FIELD_DAMAGE_ID); call grid_damage_spectral_forward(cutBack) @@ -404,7 +403,7 @@ program DAMASK_grid cutBack = .false. guess = .true. ! start guessing after first converged (sub)inc if (worldrank == 0) then - write(statUnit,*) totalIncsCounter, time, cutBackLevel, & + write(statUnit,*) totalIncsCounter, t, cutBackLevel, & solres(1)%converged, solres(1)%iterationsNeeded flush(statUnit) endif @@ -412,7 +411,7 @@ program DAMASK_grid cutBack = .true. stepFraction = (stepFraction - 1) * subStepFactor ! adjust to new denominator cutBackLevel = cutBackLevel + 1 - time = time - Delta_t ! rewind time + t = t - Delta_t Delta_t = Delta_t/real(subStepFactor,pReal) ! cut timestep print'(/,a)', ' cutting back ' else ! no more options to continue @@ -435,7 +434,7 @@ program DAMASK_grid if (mod(inc,loadCases(l)%f_out) == 0 .or. signal) then print'(1/,a)', ' ... writing results to file ......................................' flush(IO_STDOUT) - call CPFEM_results(totalIncsCounter,time) + call CPFEM_results(totalIncsCounter,t) endif if(signal) call interface_setSIGUSR1(.false.) call MPI_Allreduce(interface_SIGUSR2,signal,1,MPI_LOGICAL,MPI_LOR,MPI_COMM_WORLD,ierr) From bd9cae11a54a5f138a63fafe581371c90e11c07b Mon Sep 17 00:00:00 2001 From: Sharan Roongta Date: Tue, 20 Jul 2021 15:48:48 +0200 Subject: [PATCH 24/43] allow multi line scalar list items. If a scalar list item has ',', the parser would consider it as a separator and not a part of the string For the moment, this scalar item is enclosed with double quotes "" In the next step, the flow parser should be able to use this quotes as an indicator for strings --- src/YAML_parse.f90 | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/src/YAML_parse.f90 b/src/YAML_parse.f90 index d7cfa016f..e8e043bf1 100644 --- a/src/YAML_parse.f90 +++ b/src/YAML_parse.f90 @@ -333,6 +333,36 @@ subroutine remove_line_break(blck,s_blck,e_char,flow_line) end subroutine remove_line_break +!-------------------------------------------------------------------------------------------------- +!> @brief return the scalar list item without line break +!-------------------------------------------------------------------------------------------------- +subroutine list_item_inline(blck,s_blck,inline) !ToDo: SR: merge with remove_line_break eventually + + character(len=*), intent(in) :: blck !< YAML in mixed style + integer, intent(inout) :: s_blck + character(len=:), allocatable, intent(out) :: inline + + character(len=:), allocatable :: line + integer :: indent,indent_next + + line = IO_rmComment(blck(s_blck:s_blck + index(blck(s_blck:),IO_EOL) - 2)) + indent = indentDepth(blck(s_blck:)) + inline = line(indent+3:) + s_blck = s_blck + index(blck(s_blck:),IO_EOL) + + indent_next = indentDepth(blck(s_blck:)) + + do while(indent_next > indent) + inline = inline//IO_rmComment(blck(s_blck:s_blck + index(blck(s_blck:),IO_EOL) - 2)) + s_blck = s_blck + index(blck(s_blck:),IO_EOL) + indent_next = indentDepth(blck(s_blck:)) + enddo + + if(scan(inline,",") > 0) inline = '"'//inline//'"' ! ToDO: SR: Parse as string and not multiple list items. To be added later + +end subroutine list_item_inline + + !-------------------------------------------------------------------------------------------------- ! @brief reads a line of YAML block which is already in flow style ! @details Dicts should be enlcosed within '{}' for it to be consistent with DAMASK YAML parser @@ -463,7 +493,7 @@ recursive subroutine lst(blck,flow,s_blck,s_flow,offset) integer, intent(inout) :: s_blck, & !< start position in blck s_flow, & !< start position in flow offset !< stores leading '- ' in nested lists - character(len=:), allocatable :: line,flow_line + character(len=:), allocatable :: line,flow_line,inline integer :: e_blck,indent indent = indentDepth(blck(s_blck:),offset) @@ -509,8 +539,8 @@ recursive subroutine lst(blck,flow,s_blck,s_flow,offset) else ! list item in the same line line = line(indentDepth(line)+3:) if(isScalar(line)) then - call line_toFlow(flow,s_flow,line) - s_blck = e_blck +2 + call list_item_inline(blck,s_blck,inline) + call line_toFlow(flow,s_flow,inline) offset = 0 elseif(isFlow(line)) then s_blck = s_blck + index(blck(s_blck:),'-') @@ -848,14 +878,15 @@ subroutine selfTest " "//IO_EOL//& " "//IO_EOL//& " param_1: [a: b, c, {d: {e: [f: g, h]}}]"//IO_EOL//& - " - c: d"//IO_EOL//& + " - c:d"//IO_EOL//& + " e.f,"//IO_EOL//& " bb:"//IO_EOL//& " "//IO_EOL//& " - "//IO_EOL//& " {param_1: [{a: b}, c, {d: {e: [{f: g}, h]}}]}"//IO_EOL//& "..."//IO_EOL character(len=*), parameter :: mixed_flow = & - "{aa: [{param_1: [{a: b}, c, {d: {e: [{f: g}, h]}}]}, {c: d}], bb: [{param_1: [{a: b}, c, {d: {e: [{f: g}, h]}}]}]}" + '{aa: [{param_1: [{a: b}, c, {d: {e: [{f: g}, h]}}]}, "c:d e.f,"], bb: [{param_1: [{a: b}, c, {d: {e: [{f: g}, h]}}]}]}' if(.not. to_flow(block_flow) == mixed_flow) error stop 'to_flow' end block basic_mixed From ca8775e0d45ccc40da0b39a6f23c1655ce6321ee Mon Sep 17 00:00:00 2001 From: Sharan Roongta Date: Fri, 23 Jul 2021 16:21:56 +0200 Subject: [PATCH 25/43] allow " " in YAML, follow yamllint. --- examples/grid/material.yaml | 6 +++--- src/YAML_parse.f90 | 30 +++++++++++++++++++----------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/examples/grid/material.yaml b/examples/grid/material.yaml index a89068e1b..b59d235ff 100644 --- a/examples/grid/material.yaml +++ b/examples/grid/material.yaml @@ -6,10 +6,10 @@ homogenization: phase: Aluminum: - lattice: cF + lattice: "cF" mechanical: output: [F, P, F_e, F_p, L_p, O] - elastic: {type: Hooke, C_11: 106.75e+9, C_12: 60.41e+9, C_44: 28.34e+9} + elastic: {type: "Hooke", C_11: 106.75e+9, C_12: 60.41e+9, C_44: 28.34e+9} plastic: type: phenopowerlaw N_sl: [12] @@ -19,7 +19,7 @@ phase: h_0_sl-sl: 75.e+6 h_sl-sl: [1, 1, 1.4, 1.4, 1.4, 1.4, 1.4] n_sl: 20 - output: [xi_sl] + output: ["xi_sl"] xi_0_sl: [31.e+6] xi_inf_sl: [63.e+6] diff --git a/src/YAML_parse.f90 b/src/YAML_parse.f90 index e8e043bf1..eb485ef40 100644 --- a/src/YAML_parse.f90 +++ b/src/YAML_parse.f90 @@ -54,7 +54,7 @@ recursive function parse_flow(YAML_flow) result(node) myVal character(len=:), allocatable :: & flow_string, & - key + key, line integer :: & e, & ! end position of dictionary or list s, & ! start position of dictionary or list @@ -97,7 +97,12 @@ recursive function parse_flow(YAML_flow) result(node) allocate(tScalar::node) select type (node) class is (tScalar) - node = trim(adjustl(flow_string)) + line = trim(adjustl(flow_string)) + if(line(1:1) == '"') then + node = line(2:len(line)-1) + else + node = trim(adjustl(flow_string)) + endif end select endif @@ -119,12 +124,15 @@ integer function find_end(str,e_char) N_sq = 0 N_cu = 0 - do i = 1, len_trim(str) + i = 1 + do while(i<=len_trim(str)) + if (str(i:i) == '"') i = i + scan(str(i+1:),'"') if (N_sq==0 .and. N_cu==0 .and. scan(str(i:i),e_char//',') == 1) exit N_sq = N_sq + merge(1,0,str(i:i) == '[') N_cu = N_cu + merge(1,0,str(i:i) == '{') N_sq = N_sq - merge(1,0,str(i:i) == ']') N_cu = N_cu - merge(1,0,str(i:i) == '}') + i = i + 1 enddo find_end = i @@ -353,7 +361,7 @@ subroutine list_item_inline(blck,s_blck,inline) !ToDo: SR: merge with remove_ indent_next = indentDepth(blck(s_blck:)) do while(indent_next > indent) - inline = inline//IO_rmComment(blck(s_blck:s_blck + index(blck(s_blck:),IO_EOL) - 2)) + inline = inline//' '//trim(adjustl(IO_rmComment(blck(s_blck:s_blck + index(blck(s_blck:),IO_EOL) - 2)))) s_blck = s_blck + index(blck(s_blck:),IO_EOL) indent_next = indentDepth(blck(s_blck:)) enddo @@ -839,14 +847,14 @@ subroutine selfTest multi_line_flow1: block character(len=*), parameter :: flow_multi = & - "%YAML 1.1"//IO_EOL//& - "---"//IO_EOL//& - "a: [b,"//IO_EOL//& - "c: "//IO_EOL//& - "d, e]"//IO_EOL + '%YAML 1.1'//IO_EOL//& + '---'//IO_EOL//& + 'a: ["b",'//IO_EOL//& + 'c: '//IO_EOL//& + '"d", "e"]'//IO_EOL character(len=*), parameter :: flow = & - "{a: [b, {c: d}, e]}" + '{a: ["b", {c: "d"}, "e"]}' if( .not. to_flow(flow_multi) == flow) error stop 'to_flow' end block multi_line_flow1 @@ -886,7 +894,7 @@ subroutine selfTest " {param_1: [{a: b}, c, {d: {e: [{f: g}, h]}}]}"//IO_EOL//& "..."//IO_EOL character(len=*), parameter :: mixed_flow = & - '{aa: [{param_1: [{a: b}, c, {d: {e: [{f: g}, h]}}]}, "c:d e.f,"], bb: [{param_1: [{a: b}, c, {d: {e: [{f: g}, h]}}]}]}' + '{aa: [{param_1: [{a: b}, c, {d: {e: [{f: g}, h]}}]}, "c:d e.f,"], bb: [{param_1: [{a: b}, c, {d: {e: [{f: g}, h]}}]}]}' if(.not. to_flow(block_flow) == mixed_flow) error stop 'to_flow' end block basic_mixed From a5c4e7232cf9e0c29982ab4d7723942bb961833b Mon Sep 17 00:00:00 2001 From: Sharan Roongta Date: Fri, 23 Jul 2021 18:37:45 +0200 Subject: [PATCH 26/43] probably not needed We remove blank spaces while converting to flow YAML. --- src/YAML_parse.f90 | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/YAML_parse.f90 b/src/YAML_parse.f90 index eb485ef40..030cd2735 100644 --- a/src/YAML_parse.f90 +++ b/src/YAML_parse.f90 @@ -54,7 +54,7 @@ recursive function parse_flow(YAML_flow) result(node) myVal character(len=:), allocatable :: & flow_string, & - key, line + key integer :: & e, & ! end position of dictionary or list s, & ! start position of dictionary or list @@ -97,9 +97,8 @@ recursive function parse_flow(YAML_flow) result(node) allocate(tScalar::node) select type (node) class is (tScalar) - line = trim(adjustl(flow_string)) - if(line(1:1) == '"') then - node = line(2:len(line)-1) + if(flow_string(1:1) == '"') then + node = trim(adjustl(flow_string(2:len(flow_string)-1))) else node = trim(adjustl(flow_string)) endif @@ -362,7 +361,7 @@ subroutine list_item_inline(blck,s_blck,inline) !ToDo: SR: merge with remove_ do while(indent_next > indent) inline = inline//' '//trim(adjustl(IO_rmComment(blck(s_blck:s_blck + index(blck(s_blck:),IO_EOL) - 2)))) - s_blck = s_blck + index(blck(s_blck:),IO_EOL) + s_blck = s_blck + index(blck(s_blck:),IO_EOL) indent_next = indentDepth(blck(s_blck:)) enddo @@ -849,9 +848,9 @@ subroutine selfTest character(len=*), parameter :: flow_multi = & '%YAML 1.1'//IO_EOL//& '---'//IO_EOL//& - 'a: ["b",'//IO_EOL//& + 'a: ["b",'//IO_EOL//& 'c: '//IO_EOL//& - '"d", "e"]'//IO_EOL + '"d", "e"]'//IO_EOL character(len=*), parameter :: flow = & '{a: ["b", {c: "d"}, "e"]}' From ae49920256ed4702d99fc5d7249855d60b159f8e Mon Sep 17 00:00:00 2001 From: Sharan Roongta Date: Sat, 24 Jul 2021 17:40:27 +0200 Subject: [PATCH 27/43] support single and double quotes in key value pairs also escape characters not supported in double quotes --- examples/grid/material.yaml | 14 +++++++------- src/YAML_parse.f90 | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/grid/material.yaml b/examples/grid/material.yaml index b59d235ff..ef35f9281 100644 --- a/examples/grid/material.yaml +++ b/examples/grid/material.yaml @@ -6,29 +6,29 @@ homogenization: phase: Aluminum: - lattice: "cF" + "lattice": 'cF' mechanical: output: [F, P, F_e, F_p, L_p, O] elastic: {type: "Hooke", C_11: 106.75e+9, C_12: 60.41e+9, C_44: 28.34e+9} plastic: - type: phenopowerlaw - N_sl: [12] + 'type': "phenopowerlaw" + "N_sl": [12] a_sl: 2.25 atol_xi: 1.0 dot_gamma_0_sl: 0.001 h_0_sl-sl: 75.e+6 h_sl-sl: [1, 1, 1.4, 1.4, 1.4, 1.4, 1.4] n_sl: 20 - output: ["xi_sl"] + 'output': ['xi_sl'] xi_0_sl: [31.e+6] xi_inf_sl: [63.e+6] material: - homogenization: SX constituents: - - phase: Aluminum - v: 1.0 - O: [1.0, 0.0, 0.0, 0.0] + - 'phase': "Aluminum" + 'v': 1.0 + "O": [1.0, 0.0, 0.0, 0.0] - homogenization: SX constituents: - phase: Aluminum diff --git a/src/YAML_parse.f90 b/src/YAML_parse.f90 index 030cd2735..f876fd71f 100644 --- a/src/YAML_parse.f90 +++ b/src/YAML_parse.f90 @@ -71,8 +71,8 @@ recursive function parse_flow(YAML_flow) result(node) s = e d = s + scan(flow_string(s+1:),':') e = d + find_end(flow_string(d+1:),'}') - key = trim(adjustl(flow_string(s+1:d-1))) + if (key(1:1) == '"' .or. key(1:1) == "'" ) key = key(2:len(key)-1) myVal => parse_flow(flow_string(d+1:e-1)) ! parse items (recursively) select type (node) @@ -97,7 +97,7 @@ recursive function parse_flow(YAML_flow) result(node) allocate(tScalar::node) select type (node) class is (tScalar) - if(flow_string(1:1) == '"') then + if (flow_string(1:1) == '"' .or. flow_string(1:1) == "'") then node = trim(adjustl(flow_string(2:len(flow_string)-1))) else node = trim(adjustl(flow_string)) @@ -125,7 +125,7 @@ integer function find_end(str,e_char) N_cu = 0 i = 1 do while(i<=len_trim(str)) - if (str(i:i) == '"') i = i + scan(str(i+1:),'"') + if (str(i:i) == '"' .or. str(i:i) == "'") i = i + scan(str(i+1:),str(i:i)) if (N_sq==0 .and. N_cu==0 .and. scan(str(i:i),e_char//',') == 1) exit N_sq = N_sq + merge(1,0,str(i:i) == '[') N_cu = N_cu + merge(1,0,str(i:i) == '{') From d1c3d767cc22399d72bd708ea99dbd43ee04b223 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 25 Jul 2021 19:31:48 +0200 Subject: [PATCH 28/43] documenting --- python/damask/_crystal.py | 14 ++++++++++++++ python/damask/_orientation.py | 19 +++++++++---------- python/damask/util.py | 20 +++++++++++++++++++- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/python/damask/_crystal.py b/python/damask/_crystal.py index 09e480709..6caa51fec 100644 --- a/python/damask/_crystal.py +++ b/python/damask/_crystal.py @@ -324,6 +324,20 @@ class Crystal(): def kinematics(self,mode): + """ + Return kinematic sytems. + + Parameters + ---------- + mode : {'slip','twin'} + Deformation mode. + + Returns + ------- + direction_plane : dictionary + Direction and plane of deformation mode. + + """ _kinematics = { 'cF': { 'slip' : np.array([ diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 448ba3159..48f3b3e46 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -107,7 +107,8 @@ class Orientation(Rotation,Crystal): -------- An array of 3 x 5 random orientations reduced to the fundamental zone of tetragonal symmetry: - >>> damask.Orientation.from_random(shape=(3,5),lattice='tetragonal').reduced + >>> import damask + >>> o=damask.Orientation.from_random(shape=(3,5),lattice='tetragonal').reduced """ @@ -712,6 +713,7 @@ class Orientation(Rotation,Crystal): -------- Inverse pole figure color of the e_3 direction for a crystal in "Cube" orientation with cubic symmetry: + >>> import damask >>> o = damask.Orientation(lattice='cubic') >>> o.IPF_color([0,0,1]) array([1., 0., 0.]) @@ -859,7 +861,7 @@ class Orientation(Rotation,Crystal): Parameters ---------- mode : {'slip', 'twin'} - Type of kinematics. + Deformation mode. Returns ------- @@ -868,7 +870,7 @@ class Orientation(Rotation,Crystal): Examples -------- - Schmid matrix (in lab frame) of slip systems of a face-centered + Schmid matrix (in lab frame) of first slip system of a face-centered cubic crystal in "Goss" orientation. >>> import damask @@ -880,13 +882,10 @@ class Orientation(Rotation,Crystal): [ 0.000, 0.000, 0.000]]) """ - try: - d = self.to_frame(uvw=self.kinematics(mode)['direction']) - p = self.to_frame(hkl=self.kinematics(mode)['plane']) - except KeyError: - raise (f'"{mode}" not defined for lattice "{self.lattice}"') - P = np.einsum('...i,...j',d/np.linalg.norm(d,axis=-1,keepdims=True), - p/np.linalg.norm(p,axis=-1,keepdims=True)) + d = self.to_frame(uvw=self.kinematics(mode)['direction']) + p = self.to_frame(hkl=self.kinematics(mode)['plane']) + P = np.einsum('...i,...j',d/np.linalg.norm(d,axis=1,keepdims=True), + p/np.linalg.norm(p,axis=1,keepdims=True)) return ~self.broadcast_to( self.shape+P.shape[:-2],mode='right') \ @ np.broadcast_to(P,self.shape+P.shape) diff --git a/python/damask/util.py b/python/damask/util.py index 18eb376da..d59b41b16 100644 --- a/python/damask/util.py +++ b/python/damask/util.py @@ -287,6 +287,8 @@ def project_stereographic(vector,direction='z',normalize=True,keepdims=False): Examples -------- + >>> import damask + >>> import numpy as np >>> project_stereographic(np.ones(3)) [0.3660254, 0.3660254] >>> project_stereographic(np.ones(3),direction='x',normalize=False,keepdims=True) @@ -339,7 +341,7 @@ def hybrid_IA(dist,N,rng_seed=None): def shapeshifter(fro,to,mode='left',keep_ones=False): """ - Return a tuple that reshapes 'fro' to become broadcastable to 'to'. + Return dimensions that reshape 'fro' to become broadcastable to 'to'. Parameters ---------- @@ -356,6 +358,22 @@ def shapeshifter(fro,to,mode='left',keep_ones=False): Treat '1' in fro as literal value instead of dimensional placeholder. Defaults to False. + Returns + ------- + new_dims : tuple + Dimensions for reshape. + + Example + ------- + >>> import numpy as np + >>> from damask import util + >>> a = np.ones((3,4,2)) + >>> b = np.ones(4) + >>> b_extended = b.reshape(util.shapeshifter(b.shape,a.shape)) + >>> (a * np.broadcast_to(b_extended,a.shape)).shape + (3,4,2) + + """ beg = dict(left ='(^.*\\b)', right='(^.*?\\b)') From e95157f039528446acc5967138d2277b65feff54 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 25 Jul 2021 20:32:24 +0200 Subject: [PATCH 29/43] unified behavior extra dimensions (here: slip systems) are added to the left, inline with .equivalent and .related --- python/damask/_orientation.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 48f3b3e46..0c730ac06 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -865,7 +865,7 @@ class Orientation(Rotation,Crystal): Returns ------- - P : numpy.ndarray of shape (...,N,3,3) + P : numpy.ndarray of shape (N,...,3,3) Schmid matrix for each of the N deformation systems. Examples @@ -887,8 +887,9 @@ class Orientation(Rotation,Crystal): P = np.einsum('...i,...j',d/np.linalg.norm(d,axis=1,keepdims=True), p/np.linalg.norm(p,axis=1,keepdims=True)) - return ~self.broadcast_to( self.shape+P.shape[:-2],mode='right') \ - @ np.broadcast_to(P,self.shape+P.shape) + shape = P.shape[0:1]+self.shape+(3,3) + return ~self.broadcast_to(shape[:-2]) \ + @ np.broadcast_to(P.reshape(util.shapeshifter(P.shape,shape)),shape) def related(self,model): From dd2d569482bf3de14aad1660f4537859d62da8aa Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 25 Jul 2021 21:28:35 +0200 Subject: [PATCH 30/43] ensure that vectorization works --- python/tests/test_Orientation.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index 38559c662..aa9897312 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -438,13 +438,22 @@ class TestOrientation: assert o.to_pole(**{kw:vector,'with_symmetry':with_symmetry}).shape \ == o.shape + (o.symmetry_operations.shape if with_symmetry else ()) + vector.shape - @pytest.mark.parametrize('lattice',['hP','cI','cF']) + @pytest.mark.parametrize('lattice',['hP','cI','cF']) #tI not included yet def test_Schmid(self,update,ref_path,lattice): L = Orientation(lattice=lattice) - for mode in ['slip','twin']: # ToDo test tI + for mode in ['slip','twin']: reference = ref_path/f'{lattice}_{mode}.txt' P = L.Schmid(mode) if update: table = Table(P.reshape(-1,9),{'Schmid':(3,3,)}) table.save(reference) assert np.allclose(P,Table.load(reference).get('Schmid')) + + @pytest.mark.parametrize('lattice',['hP','cI','cF']) # tI not included yet + def test_Schmid_vectorize(self,lattice): + O = Orientation.from_random(shape=4,lattice=lattice) # noqa + for mode in ['slip','twin']: + P = O.Schmid(mode) + print(P.shape) + for i in range(4): + assert np.allclose(Orientation(rotation=O[i],lattice=lattice).Schmid(mode),P[:,i]) From 96767e75a3f01dbd9d15e4d6b77a6ebeb4f120a9 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 26 Jul 2021 00:19:03 +0200 Subject: [PATCH 31/43] can be done directly in the python library: P = damask.Orientation(lattice='cF').Schmid('slip') sigma = np.array([[1,0,0],[0,0,0],[0,0,0]]) sigma_extended = np.broadcast_to(sigma.reshape(damask.util.shapeshifter(sigma.shape,P.shape)), P.shape) tau = np.einsum('i...jk,i...jk->i...',sigma_extended,P) --- processing/legacy/addSchmidfactors.py | 117 -------------------------- 1 file changed, 117 deletions(-) delete mode 100755 processing/legacy/addSchmidfactors.py diff --git a/processing/legacy/addSchmidfactors.py b/processing/legacy/addSchmidfactors.py deleted file mode 100755 index 8e1e4ffb7..000000000 --- a/processing/legacy/addSchmidfactors.py +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/env python3 - -import os -import sys -from io import StringIO -from optparse import OptionParser - -import numpy as np - -import damask - - -scriptName = os.path.splitext(os.path.basename(__file__))[0] -scriptID = ' '.join([scriptName,damask.version]) - -slipSystems = { -'fcc': damask.lattice.kinematics['cF']['slip'][:12], -'bcc': damask.lattice.kinematics['cI']['slip'], -'hex': damask.lattice.kinematics['hP']['slip'], -} - -# -------------------------------------------------------------------- -# MAIN -# -------------------------------------------------------------------- - -parser = OptionParser(usage='%prog options [ASCIItable(s)]', description = """ -Add columns listing Schmid factors (and optional trace vector of selected system) for given Euler angles. - -""", version = scriptID) - -lattice_choices = list(slipSystems.keys()) -parser.add_option('-l', - '--lattice', - dest = 'lattice', type = 'choice', choices = lattice_choices, metavar='string', - help = 'type of lattice structure [%default] {}'.format(lattice_choices)) -parser.add_option('--covera', - dest = 'CoverA', type = 'float', metavar = 'float', - help = 'C over A ratio for hexagonal systems [%default]') -parser.add_option('-f', - '--force', - dest = 'force', - type = 'float', nargs = 3, metavar = 'float float float', - help = 'force direction in lab frame [%default]') -parser.add_option('-n', - '--normal', - dest = 'normal', - type = 'float', nargs = 3, metavar = 'float float float', - help = 'stress plane normal in lab frame, per default perpendicular to the force') -parser.add_option('-o', - '--orientation', - dest = 'quaternion', - metavar = 'string', - help = 'label of crystal orientation given as unit quaternion [%default]') - -parser.set_defaults(force = (0.0,0.0,1.0), - quaternion='orientation', - normal = None, - lattice = lattice_choices[0], - CoverA = np.sqrt(8./3.), - ) - -(options, filenames) = parser.parse_args() -if filenames == []: filenames = [None] - -force = np.array(options.force)/np.linalg.norm(options.force) - -if options.normal is not None: - normal = np.array(options.normal)/np.linalg.norm(options.ormal) - if abs(np.dot(force,normal)) > 1e-3: - parser.error('stress plane normal not orthogonal to force direction') -else: - normal = force - - -if options.lattice in ['bcc','fcc']: - slip_direction = slipSystems[options.lattice][:,:3] - slip_normal = slipSystems[options.lattice][:,3:] -elif options.lattice == 'hex': - slip_direction = np.zeros((len(slipSystems['hex']),3),'d') - slip_normal = np.zeros_like(slip_direction) - # convert 4 Miller index notation of hex to orthogonal 3 Miller index notation - for i in range(len(slip_direction)): - slip_direction[i] = np.array([slipSystems['hex'][i,0]*1.5, - (slipSystems['hex'][i,0] + 2.*slipSystems['hex'][i,1])*0.5*np.sqrt(3), - slipSystems['hex'][i,3]*options.CoverA, - ]) - slip_normal[i] = np.array([slipSystems['hex'][i,4], - (slipSystems['hex'][i,4] + 2.*slipSystems['hex'][i,5])/np.sqrt(3), - slipSystems['hex'][i,7]/options.CoverA, - ]) - -slip_direction /= np.linalg.norm(slip_direction,axis=1,keepdims=True) -slip_normal /= np.linalg.norm(slip_normal, axis=1,keepdims=True) - -labels = ['S[{direction[0]:.1g}_{direction[1]:.1g}_{direction[2]:.1g}]' - '({normal[0]:.1g}_{normal[1]:.1g}_{normal[2]:.1g})'\ - .format(normal = theNormal, direction = theDirection, - ) for theNormal,theDirection in zip(slip_normal,slip_direction)] - -for name in filenames: - damask.util.report(scriptName,name) - - table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) - - o = damask.Rotation.from_quaternion(table.get(options.quaternion)) - - force = np.broadcast_to(force, o.shape+(3,)) - normal = np.broadcast_to(normal,o.shape+(3,)) - slip_direction = np.broadcast_to(slip_direction,o.shape+slip_direction.shape) - slip_normal = np.broadcast_to(slip_normal, o.shape+slip_normal.shape) - S = np.abs(np.einsum('ijk,ik->ij',slip_direction,(o@force))* - np.einsum('ijk,ik->ij',slip_normal, (o@normal))) - - for i,label in enumerate(labels): - table = table.add(label,S[:,i],scriptID+' '+' '.join(sys.argv[1:])) - - table.save((sys.stdout if name is None else name)) From 3d7f15c940c2ece1292f141d0fff31d1a8d561da Mon Sep 17 00:00:00 2001 From: Sharan Roongta Date: Tue, 27 Jul 2021 12:17:22 +0200 Subject: [PATCH 32/43] meaningful error message incase of mismatch in quotes --- examples/grid/material.yaml | 16 ++++++++-------- src/IO.f90 | 2 ++ src/YAML_parse.f90 | 23 +++++++++++++++++++++-- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/examples/grid/material.yaml b/examples/grid/material.yaml index ef35f9281..a89068e1b 100644 --- a/examples/grid/material.yaml +++ b/examples/grid/material.yaml @@ -6,29 +6,29 @@ homogenization: phase: Aluminum: - "lattice": 'cF' + lattice: cF mechanical: output: [F, P, F_e, F_p, L_p, O] - elastic: {type: "Hooke", C_11: 106.75e+9, C_12: 60.41e+9, C_44: 28.34e+9} + elastic: {type: Hooke, C_11: 106.75e+9, C_12: 60.41e+9, C_44: 28.34e+9} plastic: - 'type': "phenopowerlaw" - "N_sl": [12] + type: phenopowerlaw + N_sl: [12] a_sl: 2.25 atol_xi: 1.0 dot_gamma_0_sl: 0.001 h_0_sl-sl: 75.e+6 h_sl-sl: [1, 1, 1.4, 1.4, 1.4, 1.4, 1.4] n_sl: 20 - 'output': ['xi_sl'] + output: [xi_sl] xi_0_sl: [31.e+6] xi_inf_sl: [63.e+6] material: - homogenization: SX constituents: - - 'phase': "Aluminum" - 'v': 1.0 - "O": [1.0, 0.0, 0.0, 0.0] + - phase: Aluminum + v: 1.0 + O: [1.0, 0.0, 0.0, 0.0] - homogenization: SX constituents: - phase: Aluminum diff --git a/src/IO.f90 b/src/IO.f90 index 399e2e6df..16d385106 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -494,6 +494,8 @@ subroutine IO_error(error_ID,el,ip,g,instance,ext_msg) msg = '--- expected after YAML file header' case (709) msg = 'Length mismatch' + case (710) + msg = 'Quotation missing in string' !------------------------------------------------------------------------------------------------- ! errors related to the grid solver diff --git a/src/YAML_parse.f90 b/src/YAML_parse.f90 index f876fd71f..4043fb6eb 100644 --- a/src/YAML_parse.f90 +++ b/src/YAML_parse.f90 @@ -72,7 +72,7 @@ recursive function parse_flow(YAML_flow) result(node) d = s + scan(flow_string(s+1:),':') e = d + find_end(flow_string(d+1:),'}') key = trim(adjustl(flow_string(s+1:d-1))) - if (key(1:1) == '"' .or. key(1:1) == "'" ) key = key(2:len(key)-1) + if(quotedString(key)) key = key(2:len(key)-1) myVal => parse_flow(flow_string(d+1:e-1)) ! parse items (recursively) select type (node) @@ -97,7 +97,7 @@ recursive function parse_flow(YAML_flow) result(node) allocate(tScalar::node) select type (node) class is (tScalar) - if (flow_string(1:1) == '"' .or. flow_string(1:1) == "'") then + if(quotedString(flow_string)) then node = trim(adjustl(flow_string(2:len(flow_string)-1))) else node = trim(adjustl(flow_string)) @@ -138,6 +138,23 @@ integer function find_end(str,e_char) end function find_end +!-------------------------------------------------------------------------------------------------- +! @brief check whether a string is enclosed with single or double quotes +!-------------------------------------------------------------------------------------------------- +logical function quotedString(line) + + character(len=*), intent(in) :: line + + quotedString = .false. + + if (line(1:1) == '"' .or. line(1:1) == "'") then + quotedString = .true. + if(line(len(line):len(line)) /= line(1:1)) call IO_error(710,ext_msg=line) + endif + +end function quotedString + + !-------------------------------------------------------------------------------------------------- ! @brief Returns Indentation. ! @details It determines the indentation level for a given block/line. @@ -760,6 +777,8 @@ subroutine selfTest if (indentDepth('a') /= 0) error stop 'indentDepth' if (indentDepth('x ') /= 0) error stop 'indentDepth' + if (.not. quotedString("'a'")) error stop 'quotedString' + if ( isFlow(' a')) error stop 'isFLow' if (.not. isFlow('{')) error stop 'isFlow' if (.not. isFlow(' [')) error stop 'isFlow' From e89a03d45683999d9f68922536564c26ee1ed657 Mon Sep 17 00:00:00 2001 From: Sharan Roongta Date: Wed, 28 Jul 2021 18:04:52 +0200 Subject: [PATCH 33/43] Suggestion by @Franz --- src/IO.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IO.f90 b/src/IO.f90 index 16d385106..ed075ac64 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -495,7 +495,7 @@ subroutine IO_error(error_ID,el,ip,g,instance,ext_msg) case (709) msg = 'Length mismatch' case (710) - msg = 'Quotation missing in string' + msg = 'Closing quotation mark missing in string' !------------------------------------------------------------------------------------------------- ! errors related to the grid solver From c5a80bc75d39d55fd30904aa83a74e42bc9a8658 Mon Sep 17 00:00:00 2001 From: Sharan Roongta Date: Mon, 2 Aug 2021 11:49:25 +0200 Subject: [PATCH 34/43] more readable --- PRIVATE | 2 +- src/IO.f90 | 3 ++- src/YAML_parse.f90 | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/PRIVATE b/PRIVATE index 72c581038..87d9413e3 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 72c58103860e127d37ccf3a06827331de29406ca +Subproject commit 87d9413e3e1d6a349eb2350e913ecfe6b423c537 diff --git a/src/IO.f90 b/src/IO.f90 index ed075ac64..6c0c3ce29 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -16,7 +16,8 @@ module IO private character(len=*), parameter, public :: & - IO_WHITESPACE = achar(44)//achar(32)//achar(9)//achar(10)//achar(13) !< whitespace characters + IO_WHITESPACE = achar(44)//achar(32)//achar(9)//achar(10)//achar(13), & !< whitespace characters + IO_QUOTES = "'"//'"' character, parameter, public :: & IO_EOL = new_line('DAMASK'), & !< end of line character IO_COMMENT = '#' diff --git a/src/YAML_parse.f90 b/src/YAML_parse.f90 index 4043fb6eb..ae19230a6 100644 --- a/src/YAML_parse.f90 +++ b/src/YAML_parse.f90 @@ -125,7 +125,7 @@ integer function find_end(str,e_char) N_cu = 0 i = 1 do while(i<=len_trim(str)) - if (str(i:i) == '"' .or. str(i:i) == "'") i = i + scan(str(i+1:),str(i:i)) + if (scan(str(i:i),IO_QUOTES) == 1) i = i + scan(str(i+1:),str(i:i)) if (N_sq==0 .and. N_cu==0 .and. scan(str(i:i),e_char//',') == 1) exit N_sq = N_sq + merge(1,0,str(i:i) == '[') N_cu = N_cu + merge(1,0,str(i:i) == '{') @@ -147,9 +147,9 @@ logical function quotedString(line) quotedString = .false. - if (line(1:1) == '"' .or. line(1:1) == "'") then + if (scan(line(:1),IO_QUOTES) == 1) then quotedString = .true. - if(line(len(line):len(line)) /= line(1:1)) call IO_error(710,ext_msg=line) + if(line(len(line):len(line)) /= line(:1)) call IO_error(710,ext_msg=line) endif end function quotedString @@ -382,7 +382,7 @@ subroutine list_item_inline(blck,s_blck,inline) !ToDo: SR: merge with remove_ indent_next = indentDepth(blck(s_blck:)) enddo - if(scan(inline,",") > 0) inline = '"'//inline//'"' ! ToDO: SR: Parse as string and not multiple list items. To be added later + if(scan(inline,",") > 0) inline = '"'//inline//'"' end subroutine list_item_inline From e0b06709ae31d357632d350685a9e359019a2eb9 Mon Sep 17 00:00:00 2001 From: Sharan Roongta Date: Mon, 2 Aug 2021 16:56:00 +0200 Subject: [PATCH 35/43] updated private --- PRIVATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVATE b/PRIVATE index 87d9413e3..e14c0ad89 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 87d9413e3e1d6a349eb2350e913ecfe6b423c537 +Subproject commit e14c0ad89c7e9bfea74b9e435b8e816f3ddc5d0a From bb5db3e79cdf1618fbb0cf9818bdf13ae71b7227 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 4 Aug 2021 16:06:28 +0200 Subject: [PATCH 36/43] structured according to family --- python/damask/_crystal.py | 331 +++++++++++++++++----------------- python/damask/_orientation.py | 4 +- 2 files changed, 170 insertions(+), 165 deletions(-) diff --git a/python/damask/_crystal.py b/python/damask/_crystal.py index 6caa51fec..7fc2e39b6 100644 --- a/python/damask/_crystal.py +++ b/python/damask/_crystal.py @@ -340,178 +340,183 @@ class Crystal(): """ _kinematics = { 'cF': { - 'slip' : np.array([ - [+0,+1,-1, +1,+1,+1], - [-1,+0,+1, +1,+1,+1], - [+1,-1,+0, +1,+1,+1], - [+0,-1,-1, -1,-1,+1], - [+1,+0,+1, -1,-1,+1], - [-1,+1,+0, -1,-1,+1], - [+0,-1,+1, +1,-1,-1], - [-1,+0,-1, +1,-1,-1], - [+1,+1,+0, +1,-1,-1], - [+0,+1,+1, -1,+1,-1], - [+1,+0,-1, -1,+1,-1], - [-1,-1,+0, -1,+1,-1], - [+1,+1,+0, +1,-1,+0], - [+1,-1,+0, +1,+1,+0], - [+1,+0,+1, +1,+0,-1], - [+1,+0,-1, +1,+0,+1], - [+0,+1,+1, +0,+1,-1], - [+0,+1,-1, +0,+1,+1], - ],dtype=float), - 'twin' : np.array([ - [-2, 1, 1, 1, 1, 1], - [ 1,-2, 1, 1, 1, 1], - [ 1, 1,-2, 1, 1, 1], - [ 2,-1, 1, -1,-1, 1], - [-1, 2, 1, -1,-1, 1], - [-1,-1,-2, -1,-1, 1], - [-2,-1,-1, 1,-1,-1], - [ 1, 2,-1, 1,-1,-1], - [ 1,-1, 2, 1,-1,-1], - [ 2, 1,-1, -1, 1,-1], - [-1,-2,-1, -1, 1,-1], - [-1, 1, 2, -1, 1,-1], - ],dtype=float), + 'slip' :[np.array([ + [+0,+1,-1, +1,+1,+1], + [-1,+0,+1, +1,+1,+1], + [+1,-1,+0, +1,+1,+1], + [+0,-1,-1, -1,-1,+1], + [+1,+0,+1, -1,-1,+1], + [-1,+1,+0, -1,-1,+1], + [+0,-1,+1, +1,-1,-1], + [-1,+0,-1, +1,-1,-1], + [+1,+1,+0, +1,-1,-1], + [+0,+1,+1, -1,+1,-1], + [+1,+0,-1, -1,+1,-1], + [-1,-1,+0, -1,+1,-1]]), + np.array([ + [+1,+1,+0, +1,-1,+0], + [+1,-1,+0, +1,+1,+0], + [+1,+0,+1, +1,+0,-1], + [+1,+0,-1, +1,+0,+1], + [+0,+1,+1, +0,+1,-1], + [+0,+1,-1, +0,+1,+1]])], + 'twin' :[np.array([ + [-2, 1, 1, 1, 1, 1], + [ 1,-2, 1, 1, 1, 1], + [ 1, 1,-2, 1, 1, 1], + [ 2,-1, 1, -1,-1, 1], + [-1, 2, 1, -1,-1, 1], + [-1,-1,-2, -1,-1, 1], + [-2,-1,-1, 1,-1,-1], + [ 1, 2,-1, 1,-1,-1], + [ 1,-1, 2, 1,-1,-1], + [ 2, 1,-1, -1, 1,-1], + [-1,-2,-1, -1, 1,-1], + [-1, 1, 2, -1, 1,-1]])] }, 'cI': { - 'slip' : np.array([ - [+1,-1,+1, +0,+1,+1], - [-1,-1,+1, +0,+1,+1], - [+1,+1,+1, +0,-1,+1], - [-1,+1,+1, +0,-1,+1], - [-1,+1,+1, +1,+0,+1], - [-1,-1,+1, +1,+0,+1], - [+1,+1,+1, -1,+0,+1], - [+1,-1,+1, -1,+0,+1], - [-1,+1,+1, +1,+1,+0], - [-1,+1,-1, +1,+1,+0], - [+1,+1,+1, -1,+1,+0], - [+1,+1,-1, -1,+1,+0], - [-1,+1,+1, +2,+1,+1], - [+1,+1,+1, -2,+1,+1], - [+1,+1,-1, +2,-1,+1], - [+1,-1,+1, +2,+1,-1], - [+1,-1,+1, +1,+2,+1], - [+1,+1,-1, -1,+2,+1], - [+1,+1,+1, +1,-2,+1], - [-1,+1,+1, +1,+2,-1], - [+1,+1,-1, +1,+1,+2], - [+1,-1,+1, -1,+1,+2], - [-1,+1,+1, +1,-1,+2], - [+1,+1,+1, +1,+1,-2], - [+1,+1,-1, +1,+2,+3], - [+1,-1,+1, -1,+2,+3], - [-1,+1,+1, +1,-2,+3], - [+1,+1,+1, +1,+2,-3], - [+1,-1,+1, +1,+3,+2], - [+1,+1,-1, -1,+3,+2], - [+1,+1,+1, +1,-3,+2], - [-1,+1,+1, +1,+3,-2], - [+1,+1,-1, +2,+1,+3], - [+1,-1,+1, -2,+1,+3], - [-1,+1,+1, +2,-1,+3], - [+1,+1,+1, +2,+1,-3], - [+1,-1,+1, +2,+3,+1], - [+1,+1,-1, -2,+3,+1], - [+1,+1,+1, +2,-3,+1], - [-1,+1,+1, +2,+3,-1], - [-1,+1,+1, +3,+1,+2], - [+1,+1,+1, -3,+1,+2], - [+1,+1,-1, +3,-1,+2], - [+1,-1,+1, +3,+1,-2], - [-1,+1,+1, +3,+2,+1], - [+1,+1,+1, -3,+2,+1], - [+1,+1,-1, +3,-2,+1], - [+1,-1,+1, +3,+2,-1], - ],dtype=float), - 'twin' : np.array([ - [-1, 1, 1, 2, 1, 1], - [ 1, 1, 1, -2, 1, 1], - [ 1, 1,-1, 2,-1, 1], - [ 1,-1, 1, 2, 1,-1], - [ 1,-1, 1, 1, 2, 1], - [ 1, 1,-1, -1, 2, 1], - [ 1, 1, 1, 1,-2, 1], - [-1, 1, 1, 1, 2,-1], - [ 1, 1,-1, 1, 1, 2], - [ 1,-1, 1, -1, 1, 2], - [-1, 1, 1, 1,-1, 2], - [ 1, 1, 1, 1, 1,-2], - ],dtype=float), + 'slip' :[np.array([ + [+1,-1,+1, +0,+1,+1], + [-1,-1,+1, +0,+1,+1], + [+1,+1,+1, +0,-1,+1], + [-1,+1,+1, +0,-1,+1], + [-1,+1,+1, +1,+0,+1], + [-1,-1,+1, +1,+0,+1], + [+1,+1,+1, -1,+0,+1], + [+1,-1,+1, -1,+0,+1], + [-1,+1,+1, +1,+1,+0], + [-1,+1,-1, +1,+1,+0], + [+1,+1,+1, -1,+1,+0], + [+1,+1,-1, -1,+1,+0]]), + np.array([ + [-1,+1,+1, +2,+1,+1], + [+1,+1,+1, -2,+1,+1], + [+1,+1,-1, +2,-1,+1], + [+1,-1,+1, +2,+1,-1], + [+1,-1,+1, +1,+2,+1], + [+1,+1,-1, -1,+2,+1], + [+1,+1,+1, +1,-2,+1], + [-1,+1,+1, +1,+2,-1], + [+1,+1,-1, +1,+1,+2], + [+1,-1,+1, -1,+1,+2], + [-1,+1,+1, +1,-1,+2], + [+1,+1,+1, +1,+1,-2]]), + np.array([ + [+1,+1,-1, +1,+2,+3], + [+1,-1,+1, -1,+2,+3], + [-1,+1,+1, +1,-2,+3], + [+1,+1,+1, +1,+2,-3], + [+1,-1,+1, +1,+3,+2], + [+1,+1,-1, -1,+3,+2], + [+1,+1,+1, +1,-3,+2], + [-1,+1,+1, +1,+3,-2], + [+1,+1,-1, +2,+1,+3], + [+1,-1,+1, -2,+1,+3], + [-1,+1,+1, +2,-1,+3], + [+1,+1,+1, +2,+1,-3], + [+1,-1,+1, +2,+3,+1], + [+1,+1,-1, -2,+3,+1], + [+1,+1,+1, +2,-3,+1], + [-1,+1,+1, +2,+3,-1], + [-1,+1,+1, +3,+1,+2], + [+1,+1,+1, -3,+1,+2], + [+1,+1,-1, +3,-1,+2], + [+1,-1,+1, +3,+1,-2], + [-1,+1,+1, +3,+2,+1], + [+1,+1,+1, -3,+2,+1], + [+1,+1,-1, +3,-2,+1], + [+1,-1,+1, +3,+2,-1]])], + 'twin' :[np.array([ + [-1, 1, 1, 2, 1, 1], + [ 1, 1, 1, -2, 1, 1], + [ 1, 1,-1, 2,-1, 1], + [ 1,-1, 1, 2, 1,-1], + [ 1,-1, 1, 1, 2, 1], + [ 1, 1,-1, -1, 2, 1], + [ 1, 1, 1, 1,-2, 1], + [-1, 1, 1, 1, 2,-1], + [ 1, 1,-1, 1, 1, 2], + [ 1,-1, 1, -1, 1, 2], + [-1, 1, 1, 1,-1, 2], + [ 1, 1, 1, 1, 1,-2]])] }, 'hP': { - 'slip' : np.array([ - [+2,-1,-1,+0, +0,+0,+0,+1], - [-1,+2,-1,+0, +0,+0,+0,+1], - [-1,-1,+2,+0, +0,+0,+0,+1], - [+2,-1,-1,+0, +0,+1,-1,+0], - [-1,+2,-1,+0, -1,+0,+1,+0], - [-1,-1,+2,+0, +1,-1,+0,+0], - [-1,+1,+0,+0, +1,+1,-2,+0], - [+0,-1,+1,+0, -2,+1,+1,+0], - [+1,+0,-1,+0, +1,-2,+1,+0], - [-1,+2,-1,+0, +1,+0,-1,+1], - [-2,+1,+1,+0, +0,+1,-1,+1], - [-1,-1,+2,+0, -1,+1,+0,+1], - [+1,-2,+1,+0, -1,+0,+1,+1], - [+2,-1,-1,+0, +0,-1,+1,+1], - [+1,+1,-2,+0, +1,-1,+0,+1], - [-2,+1,+1,+3, +1,+0,-1,+1], - [-1,-1,+2,+3, +1,+0,-1,+1], - [-1,-1,+2,+3, +0,+1,-1,+1], - [+1,-2,+1,+3, +0,+1,-1,+1], - [+1,-2,+1,+3, -1,+1,+0,+1], - [+2,-1,-1,+3, -1,+1,+0,+1], - [+2,-1,-1,+3, -1,+0,+1,+1], - [+1,+1,-2,+3, -1,+0,+1,+1], - [+1,+1,-2,+3, +0,-1,+1,+1], - [-1,+2,-1,+3, +0,-1,+1,+1], - [-1,+2,-1,+3, +1,-1,+0,+1], - [-2,+1,+1,+3, +1,-1,+0,+1], - [-1,-1,+2,+3, +1,+1,-2,+2], - [+1,-2,+1,+3, -1,+2,-1,+2], - [+2,-1,-1,+3, -2,+1,+1,+2], - [+1,+1,-2,+3, -1,-1,+2,+2], - [-1,+2,-1,+3, +1,-2,+1,+2], - [-2,+1,+1,+3, +2,-1,-1,+2], - ],dtype=float), - 'twin' : np.array([ - [-1, 0, 1, 1, 1, 0,-1, 2], # shear = (3-(c/a)^2)/(sqrt(3) c/a) <-10.1>{10.2} - [ 0,-1, 1, 1, 0, 1,-1, 2], - [ 1,-1, 0, 1, -1, 1, 0, 2], - [ 1, 0,-1, 1, -1, 0, 1, 2], - [ 0, 1,-1, 1, 0,-1, 1, 2], - [-1, 1, 0, 1, 1,-1, 0, 2], - [-1,-1, 2, 6, 1, 1,-2, 1], # shear = 1/(c/a) <11.6>{-1-1.1} - [ 1,-2, 1, 6, -1, 2,-1, 1], - [ 2,-1,-1, 6, -2, 1, 1, 1], - [ 1, 1,-2, 6, -1,-1, 2, 1], - [-1, 2,-1, 6, 1,-2, 1, 1], - [-2, 1, 1, 6, 2,-1,-1, 1], - [ 1, 0,-1,-2, 1, 0,-1, 1], # shear = (4(c/a)^2-9)/(4 sqrt(3) c/a) <10.-2>{10.1} - [ 0, 1,-1,-2, 0, 1,-1, 1], - [-1, 1, 0,-2, -1, 1, 0, 1], - [-1, 0, 1,-2, -1, 0, 1, 1], - [ 0,-1, 1,-2, 0,-1, 1, 1], - [ 1,-1, 0,-2, 1,-1, 0, 1], - [ 1, 1,-2,-3, 1, 1,-2, 2], # shear = 2((c/a)^2-2)/(3 c/a) <11.-3>{11.2} - [-1, 2,-1,-3, -1, 2,-1, 2], - [-2, 1, 1,-3, -2, 1, 1, 2], - [-1,-1, 2,-3, -1,-1, 2, 2], - [ 1,-2, 1,-3, 1,-2, 1, 2], - [ 2,-1,-1,-3, 2,-1,-1, 2], - ],dtype=float), + 'slip' :[np.array([ + [+2,-1,-1,+0, +0,+0,+0,+1], + [-1,+2,-1,+0, +0,+0,+0,+1], + [-1,-1,+2,+0, +0,+0,+0,+1]]), + np.array([ + [+2,-1,-1,+0, +0,+1,-1,+0], + [-1,+2,-1,+0, -1,+0,+1,+0], + [-1,-1,+2,+0, +1,-1,+0,+0]]), + np.array([ + [-1,+1,+0,+0, +1,+1,-2,+0], + [+0,-1,+1,+0, -2,+1,+1,+0], + [+1,+0,-1,+0, +1,-2,+1,+0]]), + np.array([ + [-1,+2,-1,+0, +1,+0,-1,+1], + [-2,+1,+1,+0, +0,+1,-1,+1], + [-1,-1,+2,+0, -1,+1,+0,+1], + [+1,-2,+1,+0, -1,+0,+1,+1], + [+2,-1,-1,+0, +0,-1,+1,+1], + [+1,+1,-2,+0, +1,-1,+0,+1]]), + np.array([ + [-2,+1,+1,+3, +1,+0,-1,+1], + [-1,-1,+2,+3, +1,+0,-1,+1], + [-1,-1,+2,+3, +0,+1,-1,+1], + [+1,-2,+1,+3, +0,+1,-1,+1], + [+1,-2,+1,+3, -1,+1,+0,+1], + [+2,-1,-1,+3, -1,+1,+0,+1], + [+2,-1,-1,+3, -1,+0,+1,+1], + [+1,+1,-2,+3, -1,+0,+1,+1], + [+1,+1,-2,+3, +0,-1,+1,+1], + [-1,+2,-1,+3, +0,-1,+1,+1], + [-1,+2,-1,+3, +1,-1,+0,+1], + [-2,+1,+1,+3, +1,-1,+0,+1]]), + np.array([ + [-1,-1,+2,+3, +1,+1,-2,+2], + [+1,-2,+1,+3, -1,+2,-1,+2], + [+2,-1,-1,+3, -2,+1,+1,+2], + [+1,+1,-2,+3, -1,-1,+2,+2], + [-1,+2,-1,+3, +1,-2,+1,+2], + [-2,+1,+1,+3, +2,-1,-1,+2]])], + 'twin' :[np.array([ + [-1, 0, 1, 1, 1, 0,-1, 2], # shear = (3-(c/a)^2)/(sqrt(3) c/a) <-10.1>{10.2} + [ 0,-1, 1, 1, 0, 1,-1, 2], + [ 1,-1, 0, 1, -1, 1, 0, 2], + [ 1, 0,-1, 1, -1, 0, 1, 2], + [ 0, 1,-1, 1, 0,-1, 1, 2], + [-1, 1, 0, 1, 1,-1, 0, 2]]), + np.array([ + [-1,-1, 2, 6, 1, 1,-2, 1], # shear = 1/(c/a) <11.6>{-1-1.1} + [ 1,-2, 1, 6, -1, 2,-1, 1], + [ 2,-1,-1, 6, -2, 1, 1, 1], + [ 1, 1,-2, 6, -1,-1, 2, 1], + [-1, 2,-1, 6, 1,-2, 1, 1], + [-2, 1, 1, 6, 2,-1,-1, 1]]), + np.array([ + [ 1, 0,-1,-2, 1, 0,-1, 1], # shear = (4(c/a)^2-9)/(4 sqrt(3) c/a) <10.-2>{10.1} + [ 0, 1,-1,-2, 0, 1,-1, 1], + [-1, 1, 0,-2, -1, 1, 0, 1], + [-1, 0, 1,-2, -1, 0, 1, 1], + [ 0,-1, 1,-2, 0,-1, 1, 1], + [ 1,-1, 0,-2, 1,-1, 0, 1]]), + np.array([ + [ 1, 1,-2,-3, 1, 1,-2, 2], # shear = 2((c/a)^2-2)/(3 c/a) <11.-3>{11.2} + [-1, 2,-1,-3, -1, 2,-1, 2], + [-2, 1, 1,-3, -2, 1, 1, 2], + [-1,-1, 2,-3, -1,-1, 2, 2], + [ 1,-2, 1,-3, 1,-2, 1, 2], + [ 2,-1,-1,-3, 2,-1,-1, 2]])] }, } master = _kinematics[self.lattice][mode] if self.lattice == 'hP': - return {'direction':util.Bravais_to_Miller(uvtw=master[:,0:4]), - 'plane': util.Bravais_to_Miller(hkil=master[:,4:8])} + return {'direction':[util.Bravais_to_Miller(uvtw=m[:,0:4]) for m in master], + 'plane': [util.Bravais_to_Miller(hkil=m[:,4:8]) for m in master]} else: - return {'direction':master[:,0:3], - 'plane': master[:,3:6]} + return {'direction':[m[:,0:3] for m in master], + 'plane': [m[:,3:6] for m in master]} def relation_operations(self,model): diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 0c730ac06..80e5d3929 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -882,8 +882,8 @@ class Orientation(Rotation,Crystal): [ 0.000, 0.000, 0.000]]) """ - d = self.to_frame(uvw=self.kinematics(mode)['direction']) - p = self.to_frame(hkl=self.kinematics(mode)['plane']) + d = self.to_frame(uvw=np.vstack(self.kinematics(mode)['direction'])) + p = self.to_frame(hkl=np.vstack(self.kinematics(mode)['plane'])) P = np.einsum('...i,...j',d/np.linalg.norm(d,axis=1,keepdims=True), p/np.linalg.norm(p,axis=1,keepdims=True)) From b5be4c3fa51ef1a2bd5b521d62bbd2c23946b532 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 4 Aug 2021 17:45:25 +0200 Subject: [PATCH 37/43] compatible with material.yaml --- python/damask/_orientation.py | 23 ++++++++++++++++------- python/tests/test_Orientation.py | 7 +++---- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 80e5d3929..749115566 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -854,14 +854,15 @@ class Orientation(Rotation,Crystal): @ np.broadcast_to(v,self.shape+v.shape) - def Schmid(self,mode): + def Schmid(self,*,N_slip=None,N_twin=None): u""" - Calculate Schmid matrix P = d ⨂ n in the lab frame for given lattice shear kinematics. + Calculate Schmid matrix P = d ⨂ n in the lab frame for selected deformation systems. Parameters ---------- - mode : {'slip', 'twin'} - Deformation mode. + N_slip|N_twin : iterable of int + Number of deformation systems per deformation system. + Use '*'. to select all. Returns ------- @@ -876,14 +877,22 @@ class Orientation(Rotation,Crystal): >>> import damask >>> import numpy as np >>> np.set_printoptions(3,suppress=True,floatmode='fixed') - >>> damask.Orientation.from_Eulers(phi=[0,45,0],degrees=True,lattice='cF').Schmid('slip')[0] + >>> O = damask.Orientation.from_Euler_angles(phi=[0,45,0],degrees=True,lattice='cF') + >>> O.Schmid(N_slip=[1]) array([[ 0.000, 0.000, 0.000], [ 0.577, -0.000, 0.816], [ 0.000, 0.000, 0.000]]) """ - d = self.to_frame(uvw=np.vstack(self.kinematics(mode)['direction'])) - p = self.to_frame(hkl=np.vstack(self.kinematics(mode)['plane'])) + if (N_slip is not None) ^ (N_twin is None): + raise KeyError('Specify either "N_slip" or "N_twin"') + + kinematics = self.kinematics('slip' if N_twin is None else 'twin') + active = N_slip if N_twin is None else N_twin + if active == '*': active = [len(a) for a in kinematics] + + d = self.to_frame(uvw=np.vstack([kinematics['direction'][i][:n] for i,n in enumerate(active)])) + p = self.to_frame(hkl=np.vstack([kinematics['plane'][i][:n] for i,n in enumerate(active)])) P = np.einsum('...i,...j',d/np.linalg.norm(d,axis=1,keepdims=True), p/np.linalg.norm(p,axis=1,keepdims=True)) diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index aa9897312..7d820b2bf 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -440,10 +440,10 @@ class TestOrientation: @pytest.mark.parametrize('lattice',['hP','cI','cF']) #tI not included yet def test_Schmid(self,update,ref_path,lattice): - L = Orientation(lattice=lattice) + O = Orientation(lattice=lattice) # noqa for mode in ['slip','twin']: reference = ref_path/f'{lattice}_{mode}.txt' - P = L.Schmid(mode) + P = O.Schmid(N_slip='*') if mode == 'slip' else O.Schmid(N_twin='*') if update: table = Table(P.reshape(-1,9),{'Schmid':(3,3,)}) table.save(reference) @@ -453,7 +453,6 @@ class TestOrientation: def test_Schmid_vectorize(self,lattice): O = Orientation.from_random(shape=4,lattice=lattice) # noqa for mode in ['slip','twin']: - P = O.Schmid(mode) - print(P.shape) + P = O.Schmid(N_slip='*') if mode == 'slip' else O.Schmid(N_twin='*') for i in range(4): assert np.allclose(Orientation(rotation=O[i],lattice=lattice).Schmid(mode),P[:,i]) From 07e59fcc1c8d6e80d915662ecfbcf96359ff9d35 Mon Sep 17 00:00:00 2001 From: Sharan Roongta Date: Thu, 5 Aug 2021 14:49:18 +0200 Subject: [PATCH 38/43] private updated --- PRIVATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVATE b/PRIVATE index e14c0ad89..4ce625b4a 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit e14c0ad89c7e9bfea74b9e435b8e816f3ddc5d0a +Subproject commit 4ce625b4ac0da9d490620f8cf1694d0a057cfa47 From be8e0dc86a1ca01eeb0484f5c1e7efbc1251816c Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 5 Aug 2021 23:39:34 +0200 Subject: [PATCH 39/43] [skip ci] updated version information after successful test of v3.0.0-alpha4-235-g2635bb012 --- python/damask/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/damask/VERSION b/python/damask/VERSION index b7d94c61e..c62e10b15 100644 --- a/python/damask/VERSION +++ b/python/damask/VERSION @@ -1 +1 @@ -v3.0.0-alpha4-221-g4a8c83611 +v3.0.0-alpha4-235-g2635bb012 From b1dab5f398aa133c0c3df6f4dd4dd20997b8e236 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Aug 2021 10:44:38 +0200 Subject: [PATCH 40/43] tests working now --- python/damask/_orientation.py | 6 +++--- python/tests/test_Orientation.py | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 749115566..1bc28bc13 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -861,7 +861,7 @@ class Orientation(Rotation,Crystal): Parameters ---------- N_slip|N_twin : iterable of int - Number of deformation systems per deformation system. + Number of deformation systems per family of the deformation system. Use '*'. to select all. Returns @@ -871,7 +871,7 @@ class Orientation(Rotation,Crystal): Examples -------- - Schmid matrix (in lab frame) of first slip system of a face-centered + Schmid matrix (in lab frame) of first octahedral slip system of a face-centered cubic crystal in "Goss" orientation. >>> import damask @@ -889,7 +889,7 @@ class Orientation(Rotation,Crystal): kinematics = self.kinematics('slip' if N_twin is None else 'twin') active = N_slip if N_twin is None else N_twin - if active == '*': active = [len(a) for a in kinematics] + if active == '*': active = [len(a) for a in kinematics['direction']] d = self.to_frame(uvw=np.vstack([kinematics['direction'][i][:n] for i,n in enumerate(active)])) p = self.to_frame(hkl=np.vstack([kinematics['plane'][i][:n] for i,n in enumerate(active)])) diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index 7d820b2bf..0c735c20f 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -453,6 +453,7 @@ class TestOrientation: def test_Schmid_vectorize(self,lattice): O = Orientation.from_random(shape=4,lattice=lattice) # noqa for mode in ['slip','twin']: - P = O.Schmid(N_slip='*') if mode == 'slip' else O.Schmid(N_twin='*') + Ps = O.Schmid(N_slip='*') if mode == 'slip' else O.Schmid(N_twin='*') for i in range(4): - assert np.allclose(Orientation(rotation=O[i],lattice=lattice).Schmid(mode),P[:,i]) + P = O[i].Schmid(N_slip='*') if mode == 'slip' else O[i].Schmid(N_twin='*') + assert np.allclose(P,Ps[:,i]) From 5292f9b414488f977970f90afe4f4fc90107a762 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Sun, 8 Aug 2021 17:56:54 -0400 Subject: [PATCH 41/43] added crystal repr(); fixed typos; extended help --- python/damask/_crystal.py | 23 ++++++++++++++++------- python/damask/_orientation.py | 13 ++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/python/damask/_crystal.py b/python/damask/_crystal.py index 7fc2e39b6..73d9f031c 100644 --- a/python/damask/_crystal.py +++ b/python/damask/_crystal.py @@ -26,7 +26,7 @@ lattice_symmetries = { class Crystal(): - """Lattice.""" + """Crystal lattice.""" def __init__(self,*, family = None, @@ -41,7 +41,7 @@ class Crystal(): ---------- family : {'triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic'}, optional. Name of the crystal family. - Will be infered if 'lattice' is given. + Will be inferred if 'lattice' is given. lattice : {'aP', 'mP', 'mS', 'oP', 'oS', 'oI', 'oF', 'tP', 'tI', 'hP', 'cP', 'cI', 'cF'}, optional. Name of the Bravais lattice in Pearson notation. a : float, optional @@ -110,14 +110,23 @@ class Crystal(): self.alpha = self.beta = self.gamma = None + def __repr__(self): + """Represent.""" + return '\n'.join([f'Crystal family {self.family}'] + + ([] if self.lattice is None else [f'Bravais lattice {self.lattice}']+ + list(map(lambda x:f'{x[0]}:{x[1]:.5g}', + zip(['a','b','c','alpha','beta','gamma',], + self.parameters)))) + ) + def __eq__(self,other): """ Equal to other. Parameters ---------- - other : Lattice - Lattice to check for equality. + other : Crystal + Crystal to check for equality. """ return self.lattice == other.lattice and \ @@ -325,7 +334,7 @@ class Crystal(): def kinematics(self,mode): """ - Return kinematic sytems. + Return crystal kinematics systems. Parameters ---------- @@ -335,7 +344,7 @@ class Crystal(): Returns ------- direction_plane : dictionary - Direction and plane of deformation mode. + Directions and planes of deformation mode families. """ _kinematics = { @@ -531,7 +540,7 @@ class Crystal(): Returns ------- operations : (string, damask.Rotation) - Rotations characterizing the orientation relationship. + Resulting lattice and rotations characterizing the orientation relationship. References ---------- diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 1bc28bc13..7007951da 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -108,7 +108,7 @@ class Orientation(Rotation,Crystal): An array of 3 x 5 random orientations reduced to the fundamental zone of tetragonal symmetry: >>> import damask - >>> o=damask.Orientation.from_random(shape=(3,5),lattice='tetragonal').reduced + >>> o=damask.Orientation.from_random(shape=(3,5),family='tetragonal').reduced """ @@ -138,9 +138,8 @@ class Orientation(Rotation,Crystal): def __repr__(self): """Represent.""" - return '\n'.join(([] if self.lattice is None else [f'Bravais lattice {self.lattice}']) - + ([f'Crystal family {self.family}']) - + [super().__repr__()]) + return '\n'.join([Crystal.__repr__(self), + Rotation.__repr__(self)]) def __copy__(self,rotation=None): @@ -862,7 +861,7 @@ class Orientation(Rotation,Crystal): ---------- N_slip|N_twin : iterable of int Number of deformation systems per family of the deformation system. - Use '*'. to select all. + Use '*' to select all. Returns ------- @@ -887,8 +886,8 @@ class Orientation(Rotation,Crystal): if (N_slip is not None) ^ (N_twin is None): raise KeyError('Specify either "N_slip" or "N_twin"') - kinematics = self.kinematics('slip' if N_twin is None else 'twin') - active = N_slip if N_twin is None else N_twin + kinematics,active = (self.kinematics('slip'),N_slip) if N_twin is None else \ + (self.kinematics('twin'),N_twin) if active == '*': active = [len(a) for a in kinematics['direction']] d = self.to_frame(uvw=np.vstack([kinematics['direction'][i][:n] for i,n in enumerate(active)])) From f1aa4e9d2c8a96197cdffe62cd84dc7a3ec743d2 Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 9 Aug 2021 16:27:11 +0200 Subject: [PATCH 42/43] [skip ci] updated version information after successful test of v3.0.0-alpha4-268-g37ffd91f7 --- python/damask/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/damask/VERSION b/python/damask/VERSION index c62e10b15..308a47dd1 100644 --- a/python/damask/VERSION +++ b/python/damask/VERSION @@ -1 +1 @@ -v3.0.0-alpha4-235-g2635bb012 +v3.0.0-alpha4-268-g37ffd91f7 From 7f67ca243cef2664b9f4897ebfcd8f3a9a538e80 Mon Sep 17 00:00:00 2001 From: Test User Date: Tue, 10 Aug 2021 00:59:25 +0200 Subject: [PATCH 43/43] [skip ci] updated version information after successful test of v3.0.0-alpha4-276-gf75235f6a --- python/damask/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/damask/VERSION b/python/damask/VERSION index 308a47dd1..0bc4ef80f 100644 --- a/python/damask/VERSION +++ b/python/damask/VERSION @@ -1 +1 @@ -v3.0.0-alpha4-268-g37ffd91f7 +v3.0.0-alpha4-276-gf75235f6a