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'))