From 5c4d48115517b3dff0f353f1ba8d1cea5d12838b Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 28 Apr 2022 01:33:33 +0200 Subject: [PATCH 1/4] common order is theta,phi --- python/damask/_rotation.py | 24 ++++++++++++++++++------ python/tests/test_Rotation.py | 4 ++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index f138fbb8a..083bea72a 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -1108,9 +1108,11 @@ class Rotation: Parameters ---------- crystal : numpy.ndarray, shape (2) - Polar coordinates (phi from x, theta from z) of fiber direction in crystal frame. + Polar coordinates (polar angle θ from [0 0 1], azimuthal angle φ from [1 0 0]) + of fiber direction in crystal frame. sample : numpy.ndarray, shape (2) - Polar coordinates (phi from x, theta from z) of fiber direction in sample frame. + Polar coordinates (polar angle θ from z, azimuthal angle φ from x) + of fiber direction in sample frame. sigma : float, optional Standard deviation of (Gaussian) misorientation distribution. Defaults to 0. @@ -1122,13 +1124,23 @@ class Rotation: A seed to initialize the BitGenerator. Defaults to None, i.e. unpredictable entropy will be pulled from the OS. + Notes + ----- + Polar coordinates follow the conventions typically used in physics, + see https://en.wikipedia.org/wiki/Spherical_coordinate_system. + + The common ranges are 0≤θ≤π and 0≤φ≤2π for a unique set of coordinates. + + Examples + -------- + """ rng = np.random.default_rng(rng_seed) - sigma_,alpha_,beta_ = (np.radians(coordinate) for coordinate in (sigma,crystal,sample)) if degrees else \ - map(np.array, (sigma,crystal,sample)) + sigma_,alpha,beta = (np.radians(coordinate) for coordinate in (sigma,crystal,sample)) if degrees else \ + map(np.array, (sigma,crystal,sample)) - d_cr = np.array([np.sin(alpha_[0])*np.cos(alpha_[1]), np.sin(alpha_[0])*np.sin(alpha_[1]), np.cos(alpha_[0])]) - d_lab = np.array([np.sin( beta_[0])*np.cos( beta_[1]), np.sin( beta_[0])*np.sin( beta_[1]), np.cos( beta_[0])]) + d_cr = np.array([np.sin(alpha[1])*np.cos(alpha[0]), np.sin(alpha[1])*np.sin(alpha[0]), np.cos(alpha[1])]) + d_lab = np.array([np.sin( beta[1])*np.cos( beta[0]), np.sin( beta[1])*np.sin( beta[0]), np.cos( beta[1])]) ax_align = np.append(np.cross(d_lab,d_cr), np.arccos(np.dot(d_lab,d_cr))) if np.isclose(ax_align[3],0.0): ax_align[:3] = np.array([1,0,0]) R_align = Rotation.from_axis_angle(ax_align if ax_align[3] > 0.0 else -ax_align,normalize=True) # rotate fiber axis from sample to crystal frame diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index a9b8b36f0..cb58c4452 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -1080,8 +1080,8 @@ class TestRotation: alpha = np.random.random()*2*np.pi,np.arccos(np.random.random()) beta = np.random.random()*2*np.pi,np.arccos(np.random.random()) - f_in_C = np.array([np.sin(alpha[0])*np.cos(alpha[1]), np.sin(alpha[0])*np.sin(alpha[1]), np.cos(alpha[0])]) - f_in_S = np.array([np.sin(beta[0] )*np.cos(beta[1] ), np.sin(beta[0] )*np.sin(beta[1] ), np.cos(beta[0] )]) + f_in_C = np.array([np.sin(alpha[1])*np.cos(alpha[0]), np.sin(alpha[1])*np.sin(alpha[0]), np.cos(alpha[1])]) + f_in_S = np.array([np.sin(beta[1] )*np.cos(beta[0] ), np.sin(beta[1] )*np.sin(beta[0] ), np.cos(beta[1] )]) ax = np.append(np.cross(f_in_C,f_in_S), - np.arccos(np.dot(f_in_C,f_in_S))) n = Rotation.from_axis_angle(ax if ax[3] > 0.0 else ax*-1.0 ,normalize=True) # rotation to align fiber axis in crystal and sample system From a0455cadf32edad084f85c4f4cb3b57d1b5ff624 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 28 Apr 2022 15:35:50 +0200 Subject: [PATCH 2/4] consistently have input in radians --- python/damask/_rotation.py | 16 ++++++++++------ python/tests/test_Rotation.py | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 083bea72a..7c183ff98 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -1010,7 +1010,7 @@ class Rotation: def from_ODF(weights: np.ndarray, phi: np.ndarray, shape: Union[int, IntSequence] = None, - degrees: bool = True, + degrees: bool = False, fractions: bool = True, rng_seed: NumpyRngSeed = None) -> 'Rotation': """ @@ -1063,7 +1063,7 @@ class Rotation: def from_spherical_component(center: 'Rotation', sigma: float, shape: Union[int, IntSequence] = None, - degrees: bool = True, + degrees: bool = False, rng_seed: NumpyRngSeed = None) -> 'Rotation': """ Initialize with samples from a Gaussian distribution around a given center. @@ -1100,7 +1100,7 @@ class Rotation: sample: IntSequence, sigma: float = 0.0, shape: Union[int, IntSequence] = None, - degrees: bool = True, + degrees: bool = False, rng_seed: NumpyRngSeed = None): """ Initialize with samples from a Gaussian distribution around a given direction. @@ -1126,10 +1126,14 @@ class Rotation: Notes ----- - Polar coordinates follow the conventions typically used in physics, - see https://en.wikipedia.org/wiki/Spherical_coordinate_system. + The default crystal direction (θ=0,φ=0) direction is [0 0 1], + the default sample direction (θ=0,φ=0) is z. - The common ranges are 0≤θ≤π and 0≤φ≤2π for a unique set of coordinates. + Polar coordinates follow the ISO 80000-2:2019 convention + typically used in physics. + See https://en.wikipedia.org/wiki/Spherical_coordinate_system. + + Ranges 0≤θ≤π and 0≤φ≤2π give a unique set of coordinates. Examples -------- diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index cb58c4452..2db08bc69 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -1061,7 +1061,7 @@ class TestRotation: p = [] for run in range(5): c = Rotation.from_random() - o = Rotation.from_spherical_component(c,sigma,shape) + o = Rotation.from_spherical_component(c,sigma,shape,degrees=True) _, angles = c.misorientation(o).as_axis_angle(pair=True,degrees=True) angles[::2] *= -1 # flip angle for every second to symmetrize distribution From 06cef4292767d067a3b57c25de909f21ad65790e Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 28 Apr 2022 15:56:24 +0200 Subject: [PATCH 3/4] use physical/ISO convention --- python/damask/_rotation.py | 5 +++-- python/tests/test_Rotation.py | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 7c183ff98..a0baf1a0b 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -1137,14 +1137,15 @@ class Rotation: Examples -------- + tbd """ rng = np.random.default_rng(rng_seed) sigma_,alpha,beta = (np.radians(coordinate) for coordinate in (sigma,crystal,sample)) if degrees else \ map(np.array, (sigma,crystal,sample)) - d_cr = np.array([np.sin(alpha[1])*np.cos(alpha[0]), np.sin(alpha[1])*np.sin(alpha[0]), np.cos(alpha[1])]) - d_lab = np.array([np.sin( beta[1])*np.cos( beta[0]), np.sin( beta[1])*np.sin( beta[0]), np.cos( beta[1])]) + d_cr = np.array([np.sin(alpha[0])*np.cos(alpha[1]), np.sin(alpha[0])*np.sin(alpha[1]), np.cos(alpha[0])]) + d_lab = np.array([np.sin( beta[0])*np.cos( beta[1]), np.sin( beta[0])*np.sin( beta[1]), np.cos( beta[0])]) ax_align = np.append(np.cross(d_lab,d_cr), np.arccos(np.dot(d_lab,d_cr))) if np.isclose(ax_align[3],0.0): ax_align[:3] = np.array([1,0,0]) R_align = Rotation.from_axis_angle(ax_align if ax_align[3] > 0.0 else -ax_align,normalize=True) # rotate fiber axis from sample to crystal frame diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 2db08bc69..c9dfc68b8 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -1077,11 +1077,11 @@ class TestRotation: def test_from_fiber_component(self,sigma,shape): p = [] for run in range(5): - alpha = np.random.random()*2*np.pi,np.arccos(np.random.random()) - beta = np.random.random()*2*np.pi,np.arccos(np.random.random()) + alpha = np.arccos(np.random.random()),np.random.random()*2*np.pi + beta = np.arccos(np.random.random()),np.random.random()*2*np.pi - f_in_C = np.array([np.sin(alpha[1])*np.cos(alpha[0]), np.sin(alpha[1])*np.sin(alpha[0]), np.cos(alpha[1])]) - f_in_S = np.array([np.sin(beta[1] )*np.cos(beta[0] ), np.sin(beta[1] )*np.sin(beta[0] ), np.cos(beta[1] )]) + f_in_C = np.array([np.sin(alpha[0])*np.cos(alpha[1]), np.sin(alpha[0])*np.sin(alpha[1]), np.cos(alpha[0])]) + f_in_S = np.array([np.sin( beta[0])*np.cos( beta[1]), np.sin( beta[0])*np.sin( beta[1]), np.cos( beta[0])]) ax = np.append(np.cross(f_in_C,f_in_S), - np.arccos(np.dot(f_in_C,f_in_S))) n = Rotation.from_axis_angle(ax if ax[3] > 0.0 else ax*-1.0 ,normalize=True) # rotation to align fiber axis in crystal and sample system From f27969caf9ab3013705921c8c89b6963f8f2ecaf Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 May 2022 23:22:03 +0200 Subject: [PATCH 4/4] documenting and testing --- python/damask/_rotation.py | 17 ++++++++++++++--- python/tests/test_Orientation.py | 7 +++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index a0baf1a0b..b24c864b3 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -1126,8 +1126,8 @@ class Rotation: Notes ----- - The default crystal direction (θ=0,φ=0) direction is [0 0 1], - the default sample direction (θ=0,φ=0) is z. + The crystal direction for (θ=0,φ=0) is [0 0 1], + the sample direction for (θ=0,φ=0) is z. Polar coordinates follow the ISO 80000-2:2019 convention typically used in physics. @@ -1137,7 +1137,18 @@ class Rotation: Examples -------- - tbd + Create an ideal α-fiber texture (<1 1 0> ǀǀ RD=x) consisting of + 200 orientations: + + >>> import damask + >>> import numpy as np + >>> alpha = damask.Rotation.from_fiber_component([np.pi/4.,0.],[np.pi/2.,0.],shape=200) + + Create an ideal γ-fiber texture (<1 1 1> ǀǀ ND=z) consisting of + 100 orientations: + + >>> import damask + >>> gamma = damask.Rotation.from_fiber_component([54.7,45.0],[0.,0.],shape=100,degrees=True) """ rng = np.random.default_rng(rng_seed) diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index 729967539..17da524d8 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -158,6 +158,13 @@ class TestOrientation: sigma=0.0,shape=None,rng_seed=0,lattice='cI').quaternion == r.quaternion) + @pytest.mark.parametrize('crystal,sample,direction,color',[([np.pi/4,0],[np.pi/2,0],[1,0,0],[0,1,0]), + ([np.arccos(3**(-.5)),np.pi/4,0],[0,0],[0,0,1],[0,0,1])]) + def test_fiber_IPF(self,crystal,sample,direction,color): + fiber = Orientation.from_fiber_component(crystal=crystal,sample=sample,family='cubic',shape=200) + print(np.allclose(fiber.IPF_color(direction),color)) + + @pytest.mark.parametrize('kwargs',[ dict(lattice='aP',a=1.0,b=1.1,c=1.2,alpha=np.pi/4.5,beta=np.pi/3.5,gamma=np.pi/2.5), dict(lattice='mP',a=1.0,b=1.1,c=1.2, beta=np.pi/3.5),