diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index cf31f4089..ad381712d 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -1,3 +1,5 @@ +import inspect + import numpy as np from . import Rotation @@ -7,7 +9,7 @@ from . import tensor _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]. + 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: a : float, optional Length of lattice parameter "a". @@ -107,8 +109,7 @@ class Orientation(Rotation): lattice = None, a = None,b = None,c = None, alpha = None,beta = None,gamma = None, - degrees = False, - **kwargs): + degrees = False): """ Initialize orientation object. @@ -263,70 +264,112 @@ class Orientation(Rotation): raise TypeError('Use "O@b", i.e. matmul, to apply Orientation "O" to object "b"') + @staticmethod + def _split_kwargs(kwargs,target): + """ + Separate keyword arguments in 'kwargs' targeted at 'target' from general keyword arguments of Orientation objects. + + Parameters + ---------- + kwargs : dictionary + Contains all **kwargs. + target: method + Function to scan for kwarg signature. + + Returns + ------- + rot_kwargs: dictionary + Valid keyword arguments of 'target' function of Rotation class. + ori_kwargs: dictionary + Valid keyword arguments of Orientation object. + + """ + kws = () + for t in (target,Orientation.__init__): + kws += ({key: kwargs[key] for key in set(inspect.signature(t).parameters) & set(kwargs)},) + + invalid_keys = set(kwargs)-(set(kws[0])|set(kws[1])) + if invalid_keys: + raise TypeError(f"{inspect.stack()[1][3]}() got an unexpected keyword argument '{invalid_keys.pop()}'") + + return kws + + @classmethod @util.extended_docstring(Rotation.from_random,_parameter_doc) def from_random(cls,**kwargs): - return cls(rotation=Rotation.from_random(**kwargs),**kwargs) + kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_random) + return cls(rotation=Rotation.from_random(**kwargs_rot),**kwargs_ori) @classmethod @util.extended_docstring(Rotation.from_quaternion,_parameter_doc) def from_quaternion(cls,**kwargs): - return cls(rotation=Rotation.from_quaternion(**kwargs),**kwargs) + kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_quaternion) + return cls(rotation=Rotation.from_quaternion(**kwargs_rot),**kwargs_ori) @classmethod @util.extended_docstring(Rotation.from_Euler_angles,_parameter_doc) def from_Euler_angles(cls,**kwargs): - return cls(rotation=Rotation.from_Euler_angles(**kwargs),**kwargs) + kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_Euler_angles) + return cls(rotation=Rotation.from_Euler_angles(**kwargs_rot),**kwargs_ori) @classmethod @util.extended_docstring(Rotation.from_axis_angle,_parameter_doc) def from_axis_angle(cls,**kwargs): - return cls(rotation=Rotation.from_axis_angle(**kwargs),**kwargs) + kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_axis_angle) + return cls(rotation=Rotation.from_axis_angle(**kwargs_rot),**kwargs_ori) @classmethod @util.extended_docstring(Rotation.from_basis,_parameter_doc) def from_basis(cls,**kwargs): - return cls(rotation=Rotation.from_basis(**kwargs),**kwargs) + kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_basis) + return cls(rotation=Rotation.from_basis(**kwargs_rot),**kwargs_ori) @classmethod @util.extended_docstring(Rotation.from_matrix,_parameter_doc) def from_matrix(cls,**kwargs): - return cls(rotation=Rotation.from_matrix(**kwargs),**kwargs) + kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_matrix) + return cls(rotation=Rotation.from_matrix(**kwargs_rot),**kwargs_ori) @classmethod @util.extended_docstring(Rotation.from_Rodrigues_vector,_parameter_doc) def from_Rodrigues_vector(cls,**kwargs): - return cls(rotation=Rotation.from_Rodrigues_vector(**kwargs),**kwargs) + kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_Rodrigues_vector) + return cls(rotation=Rotation.from_Rodrigues_vector(**kwargs_rot),**kwargs_ori) @classmethod @util.extended_docstring(Rotation.from_homochoric,_parameter_doc) def from_homochoric(cls,**kwargs): - return cls(rotation=Rotation.from_homochoric(**kwargs),**kwargs) + kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_homochoric) + return cls(rotation=Rotation.from_homochoric(**kwargs_rot),**kwargs_ori) @classmethod @util.extended_docstring(Rotation.from_cubochoric,_parameter_doc) def from_cubochoric(cls,**kwargs): - return cls(rotation=Rotation.from_cubochoric(**kwargs),**kwargs) + kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_cubochoric) + return cls(rotation=Rotation.from_cubochoric(**kwargs_rot),**kwargs_ori) @classmethod @util.extended_docstring(Rotation.from_spherical_component,_parameter_doc) def from_spherical_component(cls,**kwargs): - return cls(rotation=Rotation.from_spherical_component(**kwargs),**kwargs) + kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_spherical_component) + return cls(rotation=Rotation.from_spherical_component(**kwargs_rot),**kwargs_ori) @classmethod @util.extended_docstring(Rotation.from_fiber_component,_parameter_doc) def from_fiber_component(cls,**kwargs): - return cls(rotation=Rotation.from_fiber_component(**kwargs),**kwargs) + kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_fiber_component) + return cls(rotation=Rotation.from_fiber_component(**kwargs_rot),**kwargs_ori) @classmethod diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 441fb5b01..4109c181e 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -502,8 +502,8 @@ class Rotation: Returns ------- - c : numpy.ndarray of shape (...,3) - Cubochoric vector: (c_1, c_2, c_3), max(c_i) < 1/2*π^(2/3). + x : numpy.ndarray of shape (...,3) + Cubochoric vector: (x_1, x_2, x_3), max(x_i) < 1/2*π^(2/3). """ return Rotation._qu2cu(self.quaternion) @@ -514,8 +514,7 @@ class Rotation: @staticmethod def from_quaternion(q, accept_homomorph = False, - P = -1, - **kwargs): + P = -1): """ Initialize from quaternion. @@ -550,8 +549,7 @@ class Rotation: @staticmethod def from_Euler_angles(phi, - degrees = False, - **kwargs): + degrees = False): """ Initialize from Bunge-Euler angles. @@ -578,8 +576,7 @@ class Rotation: def from_axis_angle(axis_angle, degrees = False, normalize = False, - P = -1, - **kwargs): + P = -1): """ Initialize from Axis angle pair. @@ -616,8 +613,7 @@ class Rotation: @staticmethod def from_basis(basis, orthonormal = True, - reciprocal = False, - **kwargs): + reciprocal = False): """ Initialize from lattice basis vectors. @@ -651,7 +647,7 @@ class Rotation: return Rotation(Rotation._om2qu(om)) @staticmethod - def from_matrix(R,**kwargs): + def from_matrix(R): """ Initialize from rotation matrix. @@ -695,8 +691,7 @@ class Rotation: @staticmethod def from_Rodrigues_vector(rho, normalize = False, - P = -1, - **kwargs): + P = -1): """ Initialize from Rodrigues-Frank vector (angle separated from axis). @@ -727,8 +722,7 @@ class Rotation: @staticmethod def from_homochoric(h, - P = -1, - **kwargs): + P = -1): """ Initialize from homochoric vector. @@ -754,21 +748,20 @@ class Rotation: return Rotation(Rotation._ho2qu(ho)) @staticmethod - def from_cubochoric(c, - P = -1, - **kwargs): + def from_cubochoric(x, + P = -1): """ Initialize from cubochoric vector. Parameters ---------- - c : numpy.ndarray of shape (...,3) - Cubochoric vector: (c_1, c_2, c_3), max(c_i) < 1/2*π^(2/3). + x : numpy.ndarray of shape (...,3) + Cubochoric vector: (x_1, x_2, x_3), max(x_i) < 1/2*π^(2/3). P : int ∈ {-1,1}, optional Convention used. Defaults to -1. """ - cu = np.array(c,dtype=float) + cu = np.array(x,dtype=float) if cu.shape[:-2:-1] != (3,): raise ValueError('Invalid shape.') if abs(P) != 1: @@ -784,8 +777,7 @@ class Rotation: @staticmethod def from_random(shape = None, - rng_seed = None, - **kwargs): + rng_seed = None): """ Draw random rotation. @@ -878,8 +870,7 @@ class Rotation: sigma, N = 500, degrees = True, - rng_seed = None, - **kwargs): + rng_seed = None): """ Calculate set of rotations with Gaussian distribution around center. @@ -915,8 +906,7 @@ class Rotation: sigma = 0.0, N = 500, degrees = True, - rng_seed = None, - **kwargs): + rng_seed = None): """ Calculate set of rotations with Gaussian distribution around direction. diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index 436b73c04..e4174f795 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -118,7 +118,7 @@ class TestOrientation: == np.eye(3)) def test_from_cubochoric(self): - assert np.all(Orientation.from_cubochoric(c=np.zeros(3),lattice='triclinic').as_matrix() + assert np.all(Orientation.from_cubochoric(x=np.zeros(3),lattice='triclinic').as_matrix() == np.eye(3)) def test_from_spherical_component(self): @@ -141,7 +141,7 @@ class TestOrientation: dict(lattice='hP',a=1.0 ), dict(lattice='cI',a=1.0, ), ]) - def test_from_direction(self,kwargs): + def test_from_directions(self,kwargs): for a,b in np.random.random((10,2,3)): c = np.cross(b,a) if np.all(np.isclose(c,0)): continue @@ -151,6 +151,21 @@ class TestOrientation: assert np.isclose(np.dot(x/np.linalg.norm(x),np.array([1,0,0])),1) \ and np.isclose(np.dot(z/np.linalg.norm(z),np.array([0,0,1])),1) + @pytest.mark.parametrize('function',[Orientation.from_random, + Orientation.from_quaternion, + Orientation.from_Euler_angles, + Orientation.from_axis_angle, + Orientation.from_basis, + Orientation.from_matrix, + Orientation.from_Rodrigues_vector, + Orientation.from_homochoric, + Orientation.from_cubochoric, + Orientation.from_spherical_component, + Orientation.from_fiber_component, + Orientation.from_directions]) + def test_invalid_from(self,function): + with pytest.raises(TypeError): + function(c=.1,degrees=True,invalid=66) def test_negative_angle(self): with pytest.raises(ValueError):