From fbd61fda99b9eab87d41d738411d648cfcc5022a Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Apr 2020 11:54:34 +0200 Subject: [PATCH 01/53] ignore comments --- python/damask/_geom.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/damask/_geom.py b/python/damask/_geom.py index 6bc92e300..f39343a50 100644 --- a/python/damask/_geom.py +++ b/python/damask/_geom.py @@ -287,7 +287,7 @@ class Geom: comments = [] for i,line in enumerate(content[:header_length]): - items = line.lower().strip().split() + items = line.split('#')[0].lower().strip().split() key = items[0] if items else '' if key == 'grid': grid = np.array([ int(dict(zip(items[1::2],items[2::2]))[i]) for i in ['a','b','c']]) @@ -303,7 +303,7 @@ class Geom: microstructure = np.empty(grid.prod()) # initialize as flat array i = 0 for line in content[header_length:]: - items = line.split() + items = line.split('#')[0].split() if len(items) == 3: if items[1].lower() == 'of': items = np.ones(int(items[0]))*float(items[2]) From ccf62ede52108e998064dc9d18aca0c0ebc73d02 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 8 Apr 2020 11:32:18 +0200 Subject: [PATCH 02/53] bugfix for Cubochoric forward and backward mappings are different --- python/damask/_Lambert.py | 14 ++++++------- src/Lambert.f90 | 42 +++++++++++++++++++-------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/python/damask/_Lambert.py b/python/damask/_Lambert.py index b3f159ff6..7e46255dc 100644 --- a/python/damask/_Lambert.py +++ b/python/damask/_Lambert.py @@ -60,7 +60,7 @@ def cube_to_ball(cube): else: # get pyramide and scale by grid parameter ratio p = _get_order(cube) - XYZ = cube[p] * sc + XYZ = cube[p[0]] * sc # intercept all the points along the z-axis if np.allclose(XYZ[0:2],0.0,rtol=0.0,atol=1.0e-300): @@ -82,7 +82,7 @@ def cube_to_ball(cube): ball = np.array([ T[order[1]] * q, T[order[0]] * q, np.sqrt(6.0/np.pi) * XYZ[2] - c ]) # reverse the coordinates back to the regular order according to the original pyramid number - ball = ball[p] + ball = ball[p[1]] return ball @@ -110,7 +110,7 @@ def ball_to_cube(ball): cube = np.zeros(3) else: p = _get_order(ball) - xyz3 = ball[p] + xyz3 = ball[p[0]] # inverse M_3 xyz2 = xyz3[0:2] * np.sqrt( 2.0*rs/(rs+np.abs(xyz3[2])) ) @@ -132,7 +132,7 @@ def ball_to_cube(ball): # inverse M_1 cube = np.array([ Tinv[0], Tinv[1], (-1.0 if xyz3[2] < 0.0 else 1.0) * rs / np.sqrt(6.0/np.pi) ]) /sc # reverse the coordinates back to the regular order according to the original pyramid number - cube = cube[p] + cube = cube[p[1]] return cube @@ -157,10 +157,10 @@ def _get_order(xyz): """ if (abs(xyz[0])<= xyz[2]) and (abs(xyz[1])<= xyz[2]) or \ (abs(xyz[0])<=-xyz[2]) and (abs(xyz[1])<=-xyz[2]): - return [0,1,2] + return [[0,1,2],[0,1,2]] elif (abs(xyz[2])<= xyz[0]) and (abs(xyz[1])<= xyz[0]) or \ (abs(xyz[2])<=-xyz[0]) and (abs(xyz[1])<=-xyz[0]): - return [1,2,0] + return [[1,2,0],[2,0,1]] elif (abs(xyz[0])<= xyz[1]) and (abs(xyz[2])<= xyz[1]) or \ (abs(xyz[0])<=-xyz[1]) and (abs(xyz[2])<=-xyz[1]): - return [2,0,1] + return [[2,0,1],[1,2,0]] diff --git a/src/Lambert.f90 b/src/Lambert.f90 index d7d3e48df..932fe221b 100644 --- a/src/Lambert.f90 +++ b/src/Lambert.f90 @@ -70,13 +70,13 @@ contains !-------------------------------------------------------------------------- pure function Lambert_CubeToBall(cube) result(ball) - real(pReal), intent(in), dimension(3) :: cube - real(pReal), dimension(3) :: ball, LamXYZ, XYZ - real(pReal), dimension(2) :: T - real(pReal) :: c, s, q - real(pReal), parameter :: eps = 1.0e-8_pReal - integer, dimension(3) :: p - integer, dimension(2) :: order + real(pReal), intent(in), dimension(3) :: cube + real(pReal), dimension(3) :: ball, LamXYZ, XYZ + real(pReal), dimension(2) :: T + real(pReal) :: c, s, q + real(pReal), parameter :: eps = 1.0e-8_pReal + integer, dimension(3,2) :: p + integer, dimension(2) :: order if (maxval(abs(cube)) > AP/2.0+eps) then ball = IEEE_value(cube,IEEE_positive_inf) @@ -89,7 +89,7 @@ pure function Lambert_CubeToBall(cube) result(ball) else center ! get pyramide and scale by grid parameter ratio p = GetPyramidOrder(cube) - XYZ = cube(p) * sc + XYZ = cube(p(:,1)) * sc ! intercept all the points along the z-axis special: if (all(dEq0(XYZ(1:2)))) then @@ -112,7 +112,7 @@ pure function Lambert_CubeToBall(cube) result(ball) endif special ! reverse the coordinates back to order according to the original pyramid number - ball = LamXYZ(p) + ball = LamXYZ(p(:,2)) endif center @@ -126,11 +126,11 @@ end function Lambert_CubeToBall !-------------------------------------------------------------------------- pure function Lambert_BallToCube(xyz) result(cube) - real(pReal), intent(in), dimension(3) :: xyz - real(pReal), dimension(3) :: cube, xyz1, xyz3 - real(pReal), dimension(2) :: Tinv, xyz2 - real(pReal) :: rs, qxy, q2, sq2, q, tt - integer, dimension(3) :: p + real(pReal), intent(in), dimension(3) :: xyz + real(pReal), dimension(3) :: cube, xyz1, xyz3 + real(pReal), dimension(2) :: Tinv, xyz2 + real(pReal) :: rs, qxy, q2, sq2, q, tt + integer, dimension(3,2) :: p rs = norm2(xyz) if (rs > R1) then @@ -142,7 +142,7 @@ pure function Lambert_BallToCube(xyz) result(cube) cube = 0.0_pReal else center p = GetPyramidOrder(xyz) - xyz3 = xyz(p) + xyz3 = xyz(p(:,1)) ! inverse M_3 xyz2 = xyz3(1:2) * sqrt( 2.0*rs/(rs+abs(xyz3(3))) ) @@ -166,7 +166,7 @@ pure function Lambert_BallToCube(xyz) result(cube) xyz1 = [ Tinv(1), Tinv(2), sign(1.0_pReal,xyz3(3)) * rs / pref ] /sc ! reverse the coordinates back to order according to the original pyramid number - cube = xyz1(p) + cube = xyz1(p(:,2)) endif center @@ -180,18 +180,18 @@ end function Lambert_BallToCube !-------------------------------------------------------------------------- pure function GetPyramidOrder(xyz) - real(pReal),intent(in),dimension(3) :: xyz - integer, dimension(3) :: GetPyramidOrder + real(pReal),intent(in),dimension(3) :: xyz + integer, dimension(3,2) :: GetPyramidOrder if (((abs(xyz(1)) <= xyz(3)).and.(abs(xyz(2)) <= xyz(3))) .or. & ((abs(xyz(1)) <= -xyz(3)).and.(abs(xyz(2)) <= -xyz(3)))) then - GetPyramidOrder = [1,2,3] + GetPyramidOrder = reshape([[1,2,3],[1,2,3]],[3,2]) else if (((abs(xyz(3)) <= xyz(1)).and.(abs(xyz(2)) <= xyz(1))) .or. & ((abs(xyz(3)) <= -xyz(1)).and.(abs(xyz(2)) <= -xyz(1)))) then - GetPyramidOrder = [2,3,1] + GetPyramidOrder = reshape([[2,3,1],[3,1,2]],[3,2]) else if (((abs(xyz(1)) <= xyz(2)).and.(abs(xyz(3)) <= xyz(2))) .or. & ((abs(xyz(1)) <= -xyz(2)).and.(abs(xyz(3)) <= -xyz(2)))) then - GetPyramidOrder = [3,1,2] + GetPyramidOrder = reshape([[3,1,2],[2,3,1]],[3,2]) else GetPyramidOrder = -1 ! should be impossible, but might simplify debugging end if From 4e06e9a410a8c0fe224e127aba4a13b73523a68b Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 8 Apr 2020 11:52:26 +0200 Subject: [PATCH 03/53] improved numerical stability for corner cases --- python/damask/_Lambert.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/python/damask/_Lambert.py b/python/damask/_Lambert.py index 7e46255dc..26bc67b84 100644 --- a/python/damask/_Lambert.py +++ b/python/damask/_Lambert.py @@ -51,19 +51,20 @@ def cube_to_ball(cube): https://doi.org/10.1088/0965-0393/22/7/075013 """ - if np.abs(np.max(cube))>np.pi**(2./3.) * 0.5: + cube_ = np.clip(cube,None,np.pi**(2./3.) * 0.5) if np.isclose(np.abs(np.max(cube)),np.pi**(2./3.) * 0.5) else cube + if np.abs(np.max(cube_))>np.pi**(2./3.) * 0.5: raise ValueError # transform to the sphere grid via the curved square, and intercept the zero point - if np.allclose(cube,0.0,rtol=0.0,atol=1.0e-300): + if np.allclose(cube_,0.0,rtol=0.0,atol=1.0e-16): ball = np.zeros(3) else: # get pyramide and scale by grid parameter ratio - p = _get_order(cube) - XYZ = cube[p[0]] * sc + p = _get_order(cube_) + XYZ = cube_[p[0]] * sc # intercept all the points along the z-axis - if np.allclose(XYZ[0:2],0.0,rtol=0.0,atol=1.0e-300): + if np.allclose(XYZ[0:2],0.0,rtol=0.0,atol=1.0e-16): ball = np.array([0.0, 0.0, np.sqrt(6.0/np.pi) * XYZ[2]]) else: order = [1,0] if np.abs(XYZ[1]) <= np.abs(XYZ[0]) else [0,1] @@ -102,15 +103,16 @@ def ball_to_cube(ball): https://doi.org/10.1088/0965-0393/22/7/075013 """ - rs = np.linalg.norm(ball) + ball_ = ball/np.linalg.norm(ball) if np.isclose(np.linalg.norm(ball),R1) else ball + rs = np.linalg.norm(ball_) if rs > R1: raise ValueError - if np.allclose(ball,0.0,rtol=0.0,atol=1.0e-300): + if np.allclose(ball_,0.0,rtol=0.0,atol=1.0e-16): cube = np.zeros(3) else: - p = _get_order(ball) - xyz3 = ball[p[0]] + p = _get_order(ball_) + xyz3 = ball_[p[0]] # inverse M_3 xyz2 = xyz3[0:2] * np.sqrt( 2.0*rs/(rs+np.abs(xyz3[2])) ) @@ -118,7 +120,7 @@ def ball_to_cube(ball): # inverse M_2 qxy = np.sum(xyz2**2) - if np.isclose(qxy,0.0,rtol=0.0,atol=1.0e-300): + if np.isclose(qxy,0.0,rtol=0.0,atol=1.0e-16): Tinv = np.zeros(2) else: q2 = qxy + np.max(np.abs(xyz2))**2 From b6f5548d8ab27a9d7284bfbaff5be4ab0cfa319c Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 8 Apr 2020 12:23:28 +0200 Subject: [PATCH 04/53] correct normalization at the corners --- python/damask/_Lambert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/damask/_Lambert.py b/python/damask/_Lambert.py index 26bc67b84..d9ad55f6e 100644 --- a/python/damask/_Lambert.py +++ b/python/damask/_Lambert.py @@ -103,7 +103,7 @@ def ball_to_cube(ball): https://doi.org/10.1088/0965-0393/22/7/075013 """ - ball_ = ball/np.linalg.norm(ball) if np.isclose(np.linalg.norm(ball),R1) else ball + ball_ = ball/np.linalg.norm(ball)*R1 if np.isclose(np.linalg.norm(ball),R1) else ball rs = np.linalg.norm(ball_) if rs > R1: raise ValueError From 8f88480790eb5680950400cf13186903b298a485 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 8 Apr 2020 12:59:34 +0200 Subject: [PATCH 05/53] better readable error messages --- python/damask/_Lambert.py | 4 ++-- python/damask/_rotation.py | 25 ++++++++++++------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/python/damask/_Lambert.py b/python/damask/_Lambert.py index d9ad55f6e..133c33cca 100644 --- a/python/damask/_Lambert.py +++ b/python/damask/_Lambert.py @@ -53,7 +53,7 @@ def cube_to_ball(cube): """ cube_ = np.clip(cube,None,np.pi**(2./3.) * 0.5) if np.isclose(np.abs(np.max(cube)),np.pi**(2./3.) * 0.5) else cube if np.abs(np.max(cube_))>np.pi**(2./3.) * 0.5: - raise ValueError + raise ValueError('Coordinate outside of the cube: {} {} {}.'.format(*cube)) # transform to the sphere grid via the curved square, and intercept the zero point if np.allclose(cube_,0.0,rtol=0.0,atol=1.0e-16): @@ -106,7 +106,7 @@ def ball_to_cube(ball): ball_ = ball/np.linalg.norm(ball)*R1 if np.isclose(np.linalg.norm(ball),R1) else ball rs = np.linalg.norm(ball_) if rs > R1: - raise ValueError + raise ValueError('Coordinate outside of the sphere: {} {} {}.'.format(*ball)) if np.allclose(ball_,0.0,rtol=0.0,atol=1.0e-16): cube = np.zeros(3) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index bea7aa5e6..1a52df4ba 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -262,9 +262,9 @@ class Rotation: if acceptHomomorph: qu *= -1. else: - raise ValueError('Quaternion has negative first component.\n{}'.format(qu[0])) + raise ValueError('Quaternion has negative first component: {}.'.format(qu[0])) if not np.isclose(np.linalg.norm(qu), 1.0): - raise ValueError('Quaternion is not of unit length.\n{} {} {} {}'.format(*qu)) + raise ValueError('Quaternion is not of unit length: {} {} {} {}.'.format(*qu)) return Rotation(qu) @@ -276,7 +276,7 @@ class Rotation: else np.array(eulers,dtype=float) eu = np.radians(eu) if degrees else eu if np.any(eu < 0.0) or np.any(eu > 2.0*np.pi) or eu[1] > np.pi: - raise ValueError('Euler angles outside of [0..2π],[0..π],[0..2π].\n{} {} {}.'.format(*eu)) + raise ValueError('Euler angles outside of [0..2π],[0..π],[0..2π]: {} {} {}.'.format(*eu)) return Rotation(Rotation.eu2qu(eu)) @@ -292,9 +292,9 @@ class Rotation: if degrees: ax[ 3] = np.radians(ax[3]) if normalise: ax[0:3] /= np.linalg.norm(ax[0:3]) if ax[3] < 0.0 or ax[3] > np.pi: - raise ValueError('Axis angle rotation angle outside of [0..π].\n{}'.format(ax[3])) + raise ValueError('Axis angle rotation angle outside of [0..π]: {}.'.format(ax[3])) if not np.isclose(np.linalg.norm(ax[0:3]), 1.0): - raise ValueError('Axis angle rotation axis is not of unit length.\n{} {} {}'.format(*ax[0:3])) + raise ValueError('Axis angle rotation axis is not of unit length: {} {} {}.'.format(*ax[0:3])) return Rotation(Rotation.ax2qu(ax)) @@ -312,11 +312,11 @@ class Rotation: (U,S,Vh) = np.linalg.svd(om) # singular value decomposition om = np.dot(U,Vh) if not np.isclose(np.linalg.det(om),1.0): - raise ValueError('matrix is not a proper rotation.\n{}'.format(om)) + raise ValueError('matrix is not a proper rotation: {}.'.format(om)) if not np.isclose(np.dot(om[0],om[1]), 0.0) \ or not np.isclose(np.dot(om[1],om[2]), 0.0) \ or not np.isclose(np.dot(om[2],om[0]), 0.0): - raise ValueError('matrix is not orthogonal.\n{}'.format(om)) + raise ValueError('matrix is not orthogonal: {}.'.format(om)) return Rotation(Rotation.om2qu(om)) @@ -336,9 +336,9 @@ class Rotation: if P > 0: ro[0:3] *= -1 # convert from P=1 to P=-1 if normalise: ro[0:3] /= np.linalg.norm(ro[0:3]) if not np.isclose(np.linalg.norm(ro[0:3]), 1.0): - raise ValueError('Rodrigues rotation axis is not of unit length.\n{} {} {}'.format(*ro[0:3])) + raise ValueError('Rodrigues rotation axis is not of unit length: {} {} {}.'.format(*ro[0:3])) if ro[3] < 0.0: - raise ValueError('Rodrigues rotation angle not positive.\n{}'.format(ro[3])) + raise ValueError('Rodrigues rotation angle not positive: {}.'.format(ro[3])) return Rotation(Rotation.ro2qu(ro)) @@ -383,7 +383,7 @@ class Rotation: """ if not all(isinstance(item, Rotation) for item in rotations): - raise TypeError("Only instances of Rotation can be averaged.") + raise TypeError('Only instances of Rotation can be averaged.') N = len(rotations) if not weights: @@ -503,11 +503,10 @@ class Rotation: @staticmethod def qu2ho(qu): """Quaternion to homochoric vector.""" - omega = 2.0 * np.arccos(np.clip(qu[0],-1.0,1.0)) - - if iszero(omega): + if np.isclose(qu[0],1.0): ho = np.array([ 0.0, 0.0, 0.0 ]) else: + omega = 2.0 * np.arccos(np.clip(qu[0],-1.0,1.0)) ho = np.array([qu[1], qu[2], qu[3]]) f = 0.75 * ( omega - np.sin(omega) ) ho = ho/np.linalg.norm(ho) * f**(1./3.) From 10d5b2e791451931f91551f0ba686c7dce1d9952 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 8 Apr 2020 13:41:46 +0200 Subject: [PATCH 06/53] testing some special cases --- python/tests/test_Rotation.py | 64 ++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index c545a7172..a7fa47302 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -4,13 +4,39 @@ import pytest import numpy as np from damask import Rotation - + n = 1000 @pytest.fixture def default(): """A set of n random rotations.""" - return [Rotation.fromRandom() for r in range(n)] + specials =[np.array([ 1.0, 0.0, 0.0, 0.0]), + #----------------------------------------------- + np.array([ 1.0, 1.0, 0.0, 0.0])*np.sqrt(2.)*.5, + np.array([ 1.0, 0.0, 1.0, 0.0])*np.sqrt(2.)*.5, + np.array([ 1.0, 0.0, 0.0, 1.0])*np.sqrt(2.)*.5, + np.array([ 0.0, 1.0, 1.0, 0.0])*np.sqrt(2.)*.5, + np.array([ 0.0, 1.0, 0.0, 1.0])*np.sqrt(2.)*.5, + np.array([ 0.0, 0.0, 1.0, 1.0])*np.sqrt(2.)*.5, + #----------------------------------------------- + np.array([ 1.0,-1.0, 0.0, 0.0])*np.sqrt(2.)*.5, + np.array([ 1.0, 0.0,-1.0, 0.0])*np.sqrt(2.)*.5, + np.array([ 1.0, 0.0, 0.0,-1.0])*np.sqrt(2.)*.5, + np.array([ 0.0, 1.0,-1.0, 0.0])*np.sqrt(2.)*.5, + np.array([ 0.0, 1.0, 0.0,-1.0])*np.sqrt(2.)*.5, + np.array([ 0.0, 0.0, 1.0,-1.0])*np.sqrt(2.)*.5, + #----------------------------------------------- + np.array([ 0.0, 1.0,-1.0, 0.0])*np.sqrt(2.)*.5, + np.array([ 0.0, 1.0, 0.0,-1.0])*np.sqrt(2.)*.5, + np.array([ 0.0, 0.0, 1.0,-1.0])*np.sqrt(2.)*.5, + #----------------------------------------------- + np.array([ 0.0,-1.0,-1.0, 0.0])*np.sqrt(2.)*.5, + np.array([ 0.0,-1.0, 0.0,-1.0])*np.sqrt(2.)*.5, + np.array([ 0.0, 0.0,-1.0,-1.0])*np.sqrt(2.)*.5, + #----------------------------------------------- + ] + return [Rotation.fromQuaternion(s) for s in specials] + \ + [Rotation.fromRandom() for r in range(n-len(specials))] @pytest.fixture def reference_dir(reference_dir_base): @@ -22,35 +48,41 @@ class TestRotation: def test_Eulers(self,default): for rot in default: - assert np.allclose(rot.asQuaternion(), - Rotation.fromEulers(rot.asEulers()).asQuaternion()) + c = Rotation.fromEulers(rot.asEulers()) + ok = np.allclose(rot.asQuaternion(),c.asQuaternion()) + if np.isclose(rot.asQuaternion()[0],0.0,atol=1.e-13,rtol=0.0): + ok = ok or np.allclose(rot.asQuaternion(),c.asQuaternion()*-1.) + assert ok def test_AxisAngle(self,default): for rot in default: - assert np.allclose(rot.asEulers(), - Rotation.fromAxisAngle(rot.asAxisAngle()).asEulers()) + c = Rotation.fromAxisAngle(rot.asAxisAngle()) + assert np.allclose(rot.asEulers(),c.asEulers()) def test_Matrix(self,default): for rot in default: - assert np.allclose(rot.asAxisAngle(), - Rotation.fromMatrix(rot.asMatrix()).asAxisAngle()) + c = Rotation.fromMatrix(rot.asMatrix()) + ok = np.allclose(rot.asAxisAngle(),c.asAxisAngle()) + if np.isclose(rot.asAxisAngle()[3],np.pi): + ok = ok or np.allclose(rot.asAxisAngle(),c.asAxisAngle()*np.array([-1.,-1.,-1.,1.])) + assert ok def test_Rodriques(self,default): for rot in default: - assert np.allclose(rot.asMatrix(), - Rotation.fromRodrigues(rot.asRodrigues()).asMatrix()) + c = Rotation.fromRodrigues(rot.asRodrigues()) + assert np.allclose(rot.asMatrix(),c.asMatrix()) def test_Homochoric(self,default): for rot in default: - assert np.allclose(rot.asRodrigues(), - Rotation.fromHomochoric(rot.asHomochoric()).asRodrigues(),rtol=1.e-4) + c = Rotation.fromHomochoric(rot.asHomochoric()) + assert np.allclose(np.clip(rot.asRodrigues(),None,1.e9),np.clip(c.asRodrigues(),None,1.e9)) def test_Cubochoric(self,default): for rot in default: - assert np.allclose(rot.asHomochoric(), - Rotation.fromCubochoric(rot.asCubochoric()).asHomochoric()) + c = Rotation.fromCubochoric(rot.asCubochoric()) + assert np.allclose(rot.asHomochoric(),c.asHomochoric()) def test_Quaternion(self,default): for rot in default: - assert np.allclose(rot.asCubochoric(), - Rotation.fromQuaternion(rot.asQuaternion()).asCubochoric()) + c = Rotation.fromQuaternion(rot.asQuaternion()) + assert np.allclose(rot.asCubochoric(),c.asCubochoric()) From f365ae104d7a6648166f0a3d46905d2470df1d82 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 8 Apr 2020 17:55:05 +0200 Subject: [PATCH 07/53] adjusting tolerances --- python/damask/_Lambert.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/damask/_Lambert.py b/python/damask/_Lambert.py index 133c33cca..a62f1922a 100644 --- a/python/damask/_Lambert.py +++ b/python/damask/_Lambert.py @@ -51,7 +51,7 @@ def cube_to_ball(cube): https://doi.org/10.1088/0965-0393/22/7/075013 """ - cube_ = np.clip(cube,None,np.pi**(2./3.) * 0.5) if np.isclose(np.abs(np.max(cube)),np.pi**(2./3.) * 0.5) else cube + cube_ = np.clip(cube,None,np.pi**(2./3.) * 0.5) if np.isclose(np.abs(np.max(cube)),np.pi**(2./3.) * 0.5,atol=1e-6) else cube if np.abs(np.max(cube_))>np.pi**(2./3.) * 0.5: raise ValueError('Coordinate outside of the cube: {} {} {}.'.format(*cube)) @@ -103,9 +103,9 @@ def ball_to_cube(ball): https://doi.org/10.1088/0965-0393/22/7/075013 """ - ball_ = ball/np.linalg.norm(ball)*R1 if np.isclose(np.linalg.norm(ball),R1) else ball + ball_ = ball/np.linalg.norm(ball)*R1 if np.isclose(np.linalg.norm(ball),R1,atol=1e-6) else ball rs = np.linalg.norm(ball_) - if rs > R1: + if rs > R1 and not np.isclose(rs,R1): raise ValueError('Coordinate outside of the sphere: {} {} {}.'.format(*ball)) if np.allclose(ball_,0.0,rtol=0.0,atol=1.0e-16): From 1ba01ba0db73831cc5f6453456b073812e0d2e68 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 8 Apr 2020 18:15:50 +0200 Subject: [PATCH 08/53] adjusting tolerances --- python/damask/_rotation.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 1a52df4ba..1325c3b3e 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -459,16 +459,16 @@ class Rotation: q03 = qu[0]**2+qu[3]**2 q12 = qu[1]**2+qu[2]**2 chi = np.sqrt(q03*q12) - - if iszero(chi): - eu = np.array([np.arctan2(-P*2.0*qu[0]*qu[3],qu[0]**2-qu[3]**2), 0.0, 0.0]) if iszero(q12) else \ - np.array([np.arctan2(2.0*qu[1]*qu[2],qu[1]**2-qu[2]**2), np.pi, 0.0]) + if np.abs(chi)< 1.e-6: + eu = np.array([np.arctan2(-P*2.0*qu[0]*qu[3],qu[0]**2-qu[3]**2), 0.0, 0.0]) if np.abs(q12)< 1.e-6 else \ + np.array([np.arctan2( 2.0*qu[1]*qu[2],qu[1]**2-qu[2]**2), np.pi, 0.0]) else: eu = np.array([np.arctan2((-P*qu[0]*qu[2]+qu[1]*qu[3])*chi, (-P*qu[0]*qu[1]-qu[2]*qu[3])*chi ), np.arctan2( 2.0*chi, q03-q12 ), np.arctan2(( P*qu[0]*qu[2]+qu[1]*qu[3])*chi, (-P*qu[0]*qu[1]+qu[2]*qu[3])*chi )]) # reduce Euler angles to definition range, i.e a lower limit of 0.0 + eu[np.abs(eu)<1.e-6] = 0.0 eu = np.where(eu<0, (eu+2.0*np.pi)%np.array([2.0*np.pi,np.pi,2.0*np.pi]),eu) return eu @@ -481,7 +481,7 @@ class Rotation: """ if iszero(qu[1]**2+qu[2]**2+qu[3]**2): # set axis to [001] if the angle is 0/360 ax = [ 0.0, 0.0, 1.0, 0.0 ] - elif not iszero(qu[0]): + elif np.abs(qu[0]) > 1.e-6: s = np.sign(qu[0])/np.sqrt(qu[1]**2+qu[2]**2+qu[3]**2) omega = 2.0 * np.arccos(np.clip(qu[0],-1.0,1.0)) ax = [ qu[1]*s, qu[2]*s, qu[3]*s, omega ] @@ -498,6 +498,7 @@ class Rotation: s = np.linalg.norm([qu[1],qu[2],qu[3]]) ro = [0.0,0.0,P,0.0] if iszero(s) else \ [ qu[1]/s, qu[2]/s, qu[3]/s, np.tan(np.arccos(np.clip(qu[0],-1.0,1.0)))] + return np.array(ro) @staticmethod @@ -531,7 +532,7 @@ class Rotation: @staticmethod def om2eu(om): """Rotation matrix to Bunge-Euler angles.""" - if abs(om[2,2]) < 1.0: + if not np.isclose(np.abs(om[2,2]),1.0,1.e-4): zeta = 1.0/np.sqrt(1.0-om[2,2]**2) eu = np.array([np.arctan2(om[2,0]*zeta,-om[2,1]*zeta), np.arccos(om[2,2]), @@ -540,6 +541,7 @@ class Rotation: eu = np.array([np.arctan2( om[0,1],om[0,0]), np.pi*0.5*(1-om[2,2]),0.0]) # following the paper, not the reference implementation # reduce Euler angles to definition range, i.e a lower limit of 0.0 + eu[np.abs(eu)<1.e-6] = 0.0 eu = np.where(eu<0, (eu+2.0*np.pi)%np.array([2.0*np.pi,np.pi,2.0*np.pi]),eu) return eu @@ -552,7 +554,7 @@ class Rotation: t = 0.5*(om.trace() -1.0) ax[3] = np.arccos(np.clip(t,-1.0,1.0)) - if iszero(ax[3]): + if np.abs(ax[3])<1.e-6: ax = [ 0.0, 0.0, 1.0, 0.0] else: w,vr = np.linalg.eig(om) @@ -560,7 +562,7 @@ class Rotation: i = np.where(np.isclose(w,1.0+0.0j))[0][0] ax[0:3] = np.real(vr[0:3,i]) diagDelta = np.array([om[1,2]-om[2,1],om[2,0]-om[0,2],om[0,1]-om[1,0]]) - ax[0:3] = np.where(iszero(diagDelta), ax[0:3],np.abs(ax[0:3])*np.sign(-P*diagDelta)) + ax[0:3] = np.where(np.abs(diagDelta)<1.e-6, ax[0:3],np.abs(ax[0:3])*np.sign(-P*diagDelta)) return np.array(ax) @staticmethod @@ -603,7 +605,7 @@ class Rotation: [-c[0]*s[2]-s[0]*c[2]*c[1], -s[0]*s[2]+c[0]*c[2]*c[1], +c[2]*s[1]], [+s[0]*s[1], -c[0]*s[1], +c[1] ]]) - om[np.where(iszero(om))] = 0.0 + om[np.abs(om)<1.e-6] = 0.0 return om @staticmethod @@ -616,7 +618,7 @@ class Rotation: alpha = np.pi if iszero(np.cos(sigma)) else \ 2.0*np.arctan(tau/np.cos(sigma)) - if iszero(alpha): + if np.abs(alpha)<1.e-6: ax = np.array([ 0.0, 0.0, 1.0, 0.0 ]) else: ax = -P/tau * np.array([ t*np.cos(delta), t*np.sin(delta), np.sin(sigma) ]) # passive axis angle pair so a minus sign in front @@ -651,7 +653,7 @@ class Rotation: @staticmethod def ax2qu(ax): """Axis angle pair to quaternion.""" - if iszero(ax[3]): + if np.abs(ax[3])<1.e-6: qu = np.array([ 1.0, 0.0, 0.0, 0.0 ]) else: c = np.cos(ax[3]*0.5) @@ -681,7 +683,7 @@ class Rotation: @staticmethod def ax2ro(ax): """Axis angle pair to Rodrigues-Frank vector.""" - if iszero(ax[3]): + if np.abs(ax[3])<1.e-6: ro = [ 0.0, 0.0, P, 0.0 ] else: ro = [ax[0], ax[1], ax[2]] From 3cd8f3d9a0641e46333493e9c5550882553934b0 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 8 Apr 2020 18:38:57 +0200 Subject: [PATCH 09/53] testing special orientations with scatter --- python/tests/test_Rotation.py | 139 ++++++++++++++++++++++++---------- 1 file changed, 101 insertions(+), 38 deletions(-) diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index a7fa47302..1f69f2025 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -6,35 +6,77 @@ import numpy as np from damask import Rotation n = 1000 +atol=1.e-5 +scatter=1.e-9 @pytest.fixture def default(): """A set of n random rotations.""" - specials =[np.array([ 1.0, 0.0, 0.0, 0.0]), + specials = np.array( + [np.array([ 1.0, 0.0, 0.0, 0.0]), #----------------------------------------------- - np.array([ 1.0, 1.0, 0.0, 0.0])*np.sqrt(2.)*.5, - np.array([ 1.0, 0.0, 1.0, 0.0])*np.sqrt(2.)*.5, - np.array([ 1.0, 0.0, 0.0, 1.0])*np.sqrt(2.)*.5, - np.array([ 0.0, 1.0, 1.0, 0.0])*np.sqrt(2.)*.5, - np.array([ 0.0, 1.0, 0.0, 1.0])*np.sqrt(2.)*.5, - np.array([ 0.0, 0.0, 1.0, 1.0])*np.sqrt(2.)*.5, + np.array([0.0, 1.0, 0.0, 0.0]), + np.array([0.0, 0.0, 1.0, 0.0]), + np.array([0.0, 0.0, 0.0, 1.0]), + np.array([0.0,-1.0, 0.0, 0.0]), + np.array([0.0, 0.0,-1.0, 0.0]), + np.array([0.0, 0.0, 0.0,-1.0]), #----------------------------------------------- - np.array([ 1.0,-1.0, 0.0, 0.0])*np.sqrt(2.)*.5, - np.array([ 1.0, 0.0,-1.0, 0.0])*np.sqrt(2.)*.5, - np.array([ 1.0, 0.0, 0.0,-1.0])*np.sqrt(2.)*.5, - np.array([ 0.0, 1.0,-1.0, 0.0])*np.sqrt(2.)*.5, - np.array([ 0.0, 1.0, 0.0,-1.0])*np.sqrt(2.)*.5, - np.array([ 0.0, 0.0, 1.0,-1.0])*np.sqrt(2.)*.5, + np.array([1.0, 1.0, 0.0, 0.0])/np.sqrt(2.), + np.array([1.0, 0.0, 1.0, 0.0])/np.sqrt(2.), + np.array([1.0, 0.0, 0.0, 1.0])/np.sqrt(2.), + np.array([0.0, 1.0, 1.0, 0.0])/np.sqrt(2.), + np.array([0.0, 1.0, 0.0, 1.0])/np.sqrt(2.), + np.array([0.0, 0.0, 1.0, 1.0])/np.sqrt(2.), #----------------------------------------------- - np.array([ 0.0, 1.0,-1.0, 0.0])*np.sqrt(2.)*.5, - np.array([ 0.0, 1.0, 0.0,-1.0])*np.sqrt(2.)*.5, - np.array([ 0.0, 0.0, 1.0,-1.0])*np.sqrt(2.)*.5, + np.array([1.0,-1.0, 0.0, 0.0])/np.sqrt(2.), + np.array([1.0, 0.0,-1.0, 0.0])/np.sqrt(2.), + np.array([1.0, 0.0, 0.0,-1.0])/np.sqrt(2.), + np.array([0.0, 1.0,-1.0, 0.0])/np.sqrt(2.), + np.array([0.0, 1.0, 0.0,-1.0])/np.sqrt(2.), + np.array([0.0, 0.0, 1.0,-1.0])/np.sqrt(2.), #----------------------------------------------- - np.array([ 0.0,-1.0,-1.0, 0.0])*np.sqrt(2.)*.5, - np.array([ 0.0,-1.0, 0.0,-1.0])*np.sqrt(2.)*.5, - np.array([ 0.0, 0.0,-1.0,-1.0])*np.sqrt(2.)*.5, + np.array([0.0, 1.0,-1.0, 0.0])/np.sqrt(2.), + np.array([0.0, 1.0, 0.0,-1.0])/np.sqrt(2.), + np.array([0.0, 0.0, 1.0,-1.0])/np.sqrt(2.), #----------------------------------------------- - ] + np.array([0.0,-1.0,-1.0, 0.0])/np.sqrt(2.), + np.array([0.0,-1.0, 0.0,-1.0])/np.sqrt(2.), + np.array([0.0, 0.0,-1.0,-1.0])/np.sqrt(2.), + #----------------------------------------------- + np.array([1.0, 1.0, 1.0, 0.0])/np.sqrt(3.), + np.array([1.0, 1.0, 0.0, 1.0])/np.sqrt(3.), + np.array([1.0, 0.0, 1.0, 1.0])/np.sqrt(3.), + np.array([1.0,-1.0, 1.0, 0.0])/np.sqrt(3.), + np.array([1.0,-1.0, 0.0, 1.0])/np.sqrt(3.), + np.array([1.0, 0.0,-1.0, 1.0])/np.sqrt(3.), + np.array([1.0, 1.0,-1.0, 0.0])/np.sqrt(3.), + np.array([1.0, 1.0, 0.0,-1.0])/np.sqrt(3.), + np.array([1.0, 0.0, 1.0,-1.0])/np.sqrt(3.), + np.array([1.0,-1.0,-1.0, 0.0])/np.sqrt(3.), + np.array([1.0,-1.0, 0.0,-1.0])/np.sqrt(3.), + np.array([1.0, 0.0,-1.0,-1.0])/np.sqrt(3.), + #----------------------------------------------- + np.array([0.0, 1.0, 1.0, 1.0])/np.sqrt(3.), + np.array([0.0, 1.0,-1.0, 1.0])/np.sqrt(3.), + np.array([0.0, 1.0, 1.0,-1.0])/np.sqrt(3.), + np.array([0.0,-1.0, 1.0, 1.0])/np.sqrt(3.), + np.array([0.0,-1.0,-1.0, 1.0])/np.sqrt(3.), + np.array([0.0,-1.0, 1.0,-1.0])/np.sqrt(3.), + np.array([0.0,-1.0,-1.0,-1.0])/np.sqrt(3.), + #----------------------------------------------- + np.array([1.0, 1.0, 1.0, 1.0])/2., + np.array([1.0,-1.0, 1.0, 1.0])/2., + np.array([1.0, 1.0,-1.0, 1.0])/2., + np.array([1.0, 1.0, 1.0,-1.0])/2., + np.array([1.0,-1.0,-1.0, 1.0])/2., + np.array([1.0,-1.0, 1.0,-1.0])/2., + np.array([1.0, 1.0,-1.0,-1.0])/2., + np.array([1.0,-1.0,-1.0,-1.0])/2., + ]) + specials += np.broadcast_to(np.random.rand(4)*scatter,specials.shape) + specials /= np.linalg.norm(specials,axis=1).reshape(-1,1) + specials[specials[:,0]<0]*=-1 return [Rotation.fromQuaternion(s) for s in specials] + \ [Rotation.fromRandom() for r in range(n-len(specials))] @@ -48,41 +90,62 @@ class TestRotation: def test_Eulers(self,default): for rot in default: - c = Rotation.fromEulers(rot.asEulers()) - ok = np.allclose(rot.asQuaternion(),c.asQuaternion()) - if np.isclose(rot.asQuaternion()[0],0.0,atol=1.e-13,rtol=0.0): - ok = ok or np.allclose(rot.asQuaternion(),c.asQuaternion()*-1.) + m = rot.asQuaternion() + o = Rotation.fromEulers(rot.asEulers()).asQuaternion() + ok = np.allclose(m,o,atol=atol) + if np.isclose(rot.asQuaternion()[0],0.0,atol=atol): + ok = ok or np.allclose(m*-1.,o,atol=atol) + print(m,o,rot.asQuaternion()) assert ok def test_AxisAngle(self,default): for rot in default: - c = Rotation.fromAxisAngle(rot.asAxisAngle()) - assert np.allclose(rot.asEulers(),c.asEulers()) + m = rot.asEulers() + o = Rotation.fromAxisAngle(rot.asAxisAngle()).asEulers() + u = np.array([np.pi*2,np.pi,np.pi*2]) + ok = np.allclose(m,o,atol=atol) + ok = ok or np.allclose(np.where(np.isclose(m,u),m-u,m),np.where(np.isclose(o,u),o-u,o),atol=atol) + if np.isclose(m[1],0.0,atol=atol) or np.isclose(m[1],np.pi,atol=atol): + sum_phi = np.unwrap([m[0]+m[2],o[0]+o[2]]) + ok = ok or np.isclose(sum_phi[0],sum_phi[1],atol=atol) + print(m,o,rot.asQuaternion()) + assert ok def test_Matrix(self,default): for rot in default: - c = Rotation.fromMatrix(rot.asMatrix()) - ok = np.allclose(rot.asAxisAngle(),c.asAxisAngle()) - if np.isclose(rot.asAxisAngle()[3],np.pi): - ok = ok or np.allclose(rot.asAxisAngle(),c.asAxisAngle()*np.array([-1.,-1.,-1.,1.])) + m = rot.asAxisAngle() + o = Rotation.fromAxisAngle(rot.asAxisAngle()).asAxisAngle() + ok = np.allclose(m,o,atol=atol) + if np.isclose(m[3],np.pi,atol=atol): + ok = ok or np.allclose(m*np.array([-1.,-1.,-1.,1.]),o,atol=atol) + print(m,o,rot.asQuaternion()) assert ok def test_Rodriques(self,default): for rot in default: - c = Rotation.fromRodrigues(rot.asRodrigues()) - assert np.allclose(rot.asMatrix(),c.asMatrix()) + m = rot.asMatrix() + o = Rotation.fromRodrigues(rot.asRodrigues()).asMatrix() + print(m,o) + assert np.allclose(m,o,atol=atol) def test_Homochoric(self,default): for rot in default: - c = Rotation.fromHomochoric(rot.asHomochoric()) - assert np.allclose(np.clip(rot.asRodrigues(),None,1.e9),np.clip(c.asRodrigues(),None,1.e9)) + m = rot.asRodrigues() + o = Rotation.fromHomochoric(rot.asHomochoric()).asRodrigues() + ok = np.allclose(np.clip(m,None,1.e9),np.clip(o,None,1.e9),atol=atol) + print(m,o,rot.asQuaternion()) + ok = ok or np.isclose(m[3],0.0,atol=atol) def test_Cubochoric(self,default): for rot in default: - c = Rotation.fromCubochoric(rot.asCubochoric()) - assert np.allclose(rot.asHomochoric(),c.asHomochoric()) + m = rot.asHomochoric() + o = Rotation.fromCubochoric(rot.asCubochoric()).asHomochoric() + print(m,o,rot.asQuaternion()) + assert np.allclose(m,o,atol=atol*1e2) def test_Quaternion(self,default): for rot in default: - c = Rotation.fromQuaternion(rot.asQuaternion()) - assert np.allclose(rot.asCubochoric(),c.asCubochoric()) + m = rot.asCubochoric() + o = Rotation.fromQuaternion(rot.asQuaternion()).asCubochoric() + print(m,o,rot.asQuaternion()) + assert np.allclose(m,o,atol=atol) From 59e0041fd7a5f323f5656ca0ecff0b5e6aad4de5 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 8 Apr 2020 19:30:50 +0200 Subject: [PATCH 10/53] more scatter, slightly reduced tolerance --- python/tests/test_Rotation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 1f69f2025..fc88e4130 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -6,8 +6,8 @@ import numpy as np from damask import Rotation n = 1000 -atol=1.e-5 -scatter=1.e-9 +atol=1.e-4 +scatter=1.e-2 @pytest.fixture def default(): From 2a063b3bb5888c82d134406e9796723fe10734dc Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 8 Apr 2020 20:23:05 +0200 Subject: [PATCH 11/53] relaxed tolerance not needed --- python/tests/test_Rotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index fc88e4130..cec0ce22c 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -141,7 +141,7 @@ class TestRotation: m = rot.asHomochoric() o = Rotation.fromCubochoric(rot.asCubochoric()).asHomochoric() print(m,o,rot.asQuaternion()) - assert np.allclose(m,o,atol=atol*1e2) + assert np.allclose(m,o,atol=atol) def test_Quaternion(self,default): for rot in default: From 62ddfe098cebef167fa757e632f8238959e891a6 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 8 Apr 2020 20:30:36 +0200 Subject: [PATCH 12/53] fixed test --- PRIVATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVATE b/PRIVATE index 62bd5ede5..377e4f97a 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 62bd5ede5260cd4e0e3d1c3930c474c1e045aeef +Subproject commit 377e4f97a31ca1aa39a0645430c82bed40158001 From 464620b796d89d2318b06fdde7530207a6e3f783 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 8 Apr 2020 20:50:42 +0200 Subject: [PATCH 13/53] vectorized conversion from ax(is angle) --- python/damask/_rotation.py | 87 +++++++++++++++++++++++++---------- python/tests/test_Rotation.py | 14 +++++- 2 files changed, 75 insertions(+), 26 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 1325c3b3e..a2d26949d 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -653,27 +653,50 @@ class Rotation: @staticmethod def ax2qu(ax): """Axis angle pair to quaternion.""" - if np.abs(ax[3])<1.e-6: - qu = np.array([ 1.0, 0.0, 0.0, 0.0 ]) + if len(ax.shape) == 1: + if np.abs(ax[3])<1.e-6: + qu = np.array([ 1.0, 0.0, 0.0, 0.0 ]) + else: + c = np.cos(ax[3]*0.5) + s = np.sin(ax[3]*0.5) + qu = np.array([ c, ax[0]*s, ax[1]*s, ax[2]*s ]) + return qu else: - c = np.cos(ax[3]*0.5) - s = np.sin(ax[3]*0.5) - qu = np.array([ c, ax[0]*s, ax[1]*s, ax[2]*s ]) - return qu + c = np.cos(ax[...,3:4]*.5) + s = np.sin(ax[...,3:4]*.5) + qu = np.where(np.abs(ax[...,3:4])<1.e-12, + [1.0, 0.0, 0.0, 0.0], + np.block([c, ax[...,:3]*s])) + return qu @staticmethod def ax2om(ax): """Axis angle pair to rotation matrix.""" - c = np.cos(ax[3]) - s = np.sin(ax[3]) - omc = 1.0-c - om=np.diag(ax[0:3]**2*omc + c) + if len(ax.shape) == 1: + c = np.cos(ax[3]) + s = np.sin(ax[3]) + omc = 1.0-c + om=np.diag(ax[0:3]**2*omc + c) - for idx in [[0,1,2],[1,2,0],[2,0,1]]: - q = omc*ax[idx[0]] * ax[idx[1]] - om[idx[0],idx[1]] = q + s*ax[idx[2]] - om[idx[1],idx[0]] = q - s*ax[idx[2]] - return om if P < 0.0 else om.T + for idx in [[0,1,2],[1,2,0],[2,0,1]]: + q = omc*ax[idx[0]] * ax[idx[1]] + om[idx[0],idx[1]] = q + s*ax[idx[2]] + om[idx[1],idx[0]] = q - s*ax[idx[2]] + return om if P < 0.0 else om.T + else: + c = np.cos(ax[...,3:4]) + s = np.sin(ax[...,3:4]) + omc = 1. -c + ax = np.block([c+omc*ax[...,0:1]**2, + omc*ax[...,0:1]*ax[...,1:2] + s*ax[...,2:3], + omc*ax[...,0:1]*ax[...,2:3] - s*ax[...,1:2], + omc*ax[...,0:1]*ax[...,1:2] - s*ax[...,2:3], + c+omc*ax[...,1:2]**2, + omc*ax[...,1:2]*ax[...,2:3] + s*ax[...,0:1], + omc*ax[...,0:1]*ax[...,2:3] + s*ax[...,1:2], + omc*ax[...,1:2]*ax[...,2:3] - s*ax[...,0:1], + c+omc*ax[...,2:3]**2]).reshape(ax.shape[:-1]+(3,3)) + return ax @staticmethod def ax2eu(ax): @@ -683,21 +706,35 @@ class Rotation: @staticmethod def ax2ro(ax): """Axis angle pair to Rodrigues-Frank vector.""" - if np.abs(ax[3])<1.e-6: - ro = [ 0.0, 0.0, P, 0.0 ] + if len(ax.shape) == 1: + if np.abs(ax[3])<1.e-6: + ro = [ 0.0, 0.0, P, 0.0 ] + else: + ro = [ax[0], ax[1], ax[2]] + # 180 degree case + ro += [np.inf] if np.isclose(ax[3],np.pi,atol=1.0e-15,rtol=0.0) else \ + [np.tan(ax[3]*0.5)] + return np.array(ro) else: - ro = [ax[0], ax[1], ax[2]] - # 180 degree case - ro += [np.inf] if np.isclose(ax[3],np.pi,atol=1.0e-15,rtol=0.0) else \ - [np.tan(ax[3]*0.5)] - return np.array(ro) + ro = np.block([ax[...,:3], + np.where(np.isclose(ax[...,3:4],np.pi,atol=1.e-15,rtol=.0), + np.inf, + np.tan(ax[...,3:4]*0.5)) + ]) + ro[np.abs(ax[...,3])<1.e-6] = [.0,.0,P,.0] + return ro @staticmethod def ax2ho(ax): """Axis angle pair to homochoric vector.""" - f = (0.75 * ( ax[3] - np.sin(ax[3]) ))**(1.0/3.0) - ho = ax[0:3] * f - return ho + if len(ax.shape) == 1: + f = (0.75 * ( ax[3] - np.sin(ax[3]) ))**(1.0/3.0) + ho = ax[0:3] * f + return ho + else: + f = (0.75 * ( ax[...,3:4] - np.sin(ax[...,3:4]) ))**(1.0/3.0) + ho = ax[...,:3] * f + return ho @staticmethod def ax2cu(ax): diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index cec0ce22c..13c474ca7 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -78,7 +78,7 @@ def default(): specials /= np.linalg.norm(specials,axis=1).reshape(-1,1) specials[specials[:,0]<0]*=-1 return [Rotation.fromQuaternion(s) for s in specials] + \ - [Rotation.fromRandom() for r in range(n-len(specials))] + [Rotation.fromRandom() for _ in range(n-len(specials))] @pytest.fixture def reference_dir(reference_dir_base): @@ -149,3 +149,15 @@ class TestRotation: o = Rotation.fromQuaternion(rot.asQuaternion()).asCubochoric() print(m,o,rot.asQuaternion()) assert np.allclose(m,o,atol=atol) + + @pytest.mark.parametrize('conversion',[Rotation.ax2qu, + Rotation.ax2om, + Rotation.ax2ro, + Rotation.ax2ho, + ]) + def test_axisAngle_vectorization(self,default,conversion): + ax = np.array([rot.asAxisAngle() for rot in default]) + dev_null = conversion(ax.reshape(ax.shape[0]//2,-1,4)) + co = conversion(ax) + for a,c in zip(ax,co): + assert np.allclose(conversion(a),c) From da30fb8396e384d1012370df9023d722b1410514 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 8 Apr 2020 23:11:48 +0200 Subject: [PATCH 14/53] qu(aternion) and eu(ler) vectorized and tested --- python/damask/_rotation.py | 265 ++++++++++++++++++++++++---------- python/tests/test_Rotation.py | 22 +++ 2 files changed, 208 insertions(+), 79 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index a2d26949d..9296f57b4 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -441,32 +441,68 @@ class Rotation: #---------- Quaternion ---------- @staticmethod def qu2om(qu): - """Quaternion to rotation matrix.""" - qq = qu[0]**2-(qu[1]**2 + qu[2]**2 + qu[3]**2) - om = np.diag(qq + 2.0*np.array([qu[1],qu[2],qu[3]])**2) + if len(qu.shape) == 1: + """Quaternion to rotation matrix.""" + qq = qu[0]**2-(qu[1]**2 + qu[2]**2 + qu[3]**2) + om = np.diag(qq + 2.0*np.array([qu[1],qu[2],qu[3]])**2) - om[1,0] = 2.0*(qu[2]*qu[1]+qu[0]*qu[3]) - om[0,1] = 2.0*(qu[1]*qu[2]-qu[0]*qu[3]) - om[2,1] = 2.0*(qu[3]*qu[2]+qu[0]*qu[1]) - om[1,2] = 2.0*(qu[2]*qu[3]-qu[0]*qu[1]) - om[0,2] = 2.0*(qu[1]*qu[3]+qu[0]*qu[2]) - om[2,0] = 2.0*(qu[3]*qu[1]-qu[0]*qu[2]) - return om if P > 0.0 else om.T + om[1,0] = 2.0*(qu[2]*qu[1]+qu[0]*qu[3]) + om[0,1] = 2.0*(qu[1]*qu[2]-qu[0]*qu[3]) + om[2,1] = 2.0*(qu[3]*qu[2]+qu[0]*qu[1]) + om[1,2] = 2.0*(qu[2]*qu[3]-qu[0]*qu[1]) + om[0,2] = 2.0*(qu[1]*qu[3]+qu[0]*qu[2]) + om[2,0] = 2.0*(qu[3]*qu[1]-qu[0]*qu[2]) + return om if P > 0.0 else om.T + else: + qq = qu[...,0:1]**2-(qu[...,1:2]**2 + qu[...,2:3]**2 + qu[...,3:4]**2) + om = np.block([qq + 2.0*qu[...,1:2]**2, + 2.0*(qu[...,2:3]*qu[...,1:2]+qu[...,0:1]*qu[...,3:4]), + 2.0*(qu[...,3:4]*qu[...,1:2]-qu[...,0:1]*qu[...,2:3]), + 2.0*(qu[...,1:2]*qu[...,2:3]-qu[...,0:1]*qu[...,3:4]), + qq + 2.0*qu[...,2:3]**2, + 2.0*(qu[...,3:4]*qu[...,2:3]+qu[...,0:1]*qu[...,1:2]), + 2.0*(qu[...,1:2]*qu[...,3:4]+qu[...,0:1]*qu[...,2:3]), + 2.0*(qu[...,2:3]*qu[...,3:4]-qu[...,0:1]*qu[...,1:2]), + qq + 2.0*qu[...,3:4]**2, + ]).reshape(qu.shape[:-1]+(3,3)) + return om # TODO: TRANSPOSE FOR P = 1 @staticmethod def qu2eu(qu): """Quaternion to Bunge-Euler angles.""" - q03 = qu[0]**2+qu[3]**2 - q12 = qu[1]**2+qu[2]**2 - chi = np.sqrt(q03*q12) - if np.abs(chi)< 1.e-6: - eu = np.array([np.arctan2(-P*2.0*qu[0]*qu[3],qu[0]**2-qu[3]**2), 0.0, 0.0]) if np.abs(q12)< 1.e-6 else \ - np.array([np.arctan2( 2.0*qu[1]*qu[2],qu[1]**2-qu[2]**2), np.pi, 0.0]) + if len(qu.shape) == 1: + q03 = qu[0]**2+qu[3]**2 + q12 = qu[1]**2+qu[2]**2 + chi = np.sqrt(q03*q12) + if np.abs(q03)< 1.e-6: + eu = np.array([np.arctan2(-P*2.0*qu[0]*qu[3],qu[0]**2-qu[3]**2), 0.0, 0.0]) + elif np.abs(q12)< 1.e-6: + eu = np.array([np.arctan2( 2.0*qu[1]*qu[2],qu[1]**2-qu[2]**2), np.pi, 0.0]) + else: + eu = np.array([np.arctan2((-P*qu[0]*qu[2]+qu[1]*qu[3])*chi, (-P*qu[0]*qu[1]-qu[2]*qu[3])*chi ), + np.arctan2( 2.0*chi, q03-q12 ), + np.arctan2(( P*qu[0]*qu[2]+qu[1]*qu[3])*chi, (-P*qu[0]*qu[1]+qu[2]*qu[3])*chi )]) else: - eu = np.array([np.arctan2((-P*qu[0]*qu[2]+qu[1]*qu[3])*chi, (-P*qu[0]*qu[1]-qu[2]*qu[3])*chi ), - np.arctan2( 2.0*chi, q03-q12 ), - np.arctan2(( P*qu[0]*qu[2]+qu[1]*qu[3])*chi, (-P*qu[0]*qu[1]+qu[2]*qu[3])*chi )]) + q02 = qu[...,0:1]*qu[...,2:3] + q13 = qu[...,1:2]*qu[...,3:4] + q01 = qu[...,0:1]*qu[...,1:2] + q23 = qu[...,2:3]*qu[...,3:4] + q03_s = qu[...,0:1]**2+qu[...,3:4]**2 + q12_s = qu[...,1:2]**2+qu[...,2:3]**2 + chi = np.sqrt(q03_s*q12_s) + eu = np.where(np.abs(q12_s) < 1.0e-6, + np.block([np.arctan2(-P*2.0*qu[...,0:1]*qu[...,3:4],qu[...,0:1]**2-qu[...,3:4]**2), + np.zeros(qu.shape[:-1]+(2,))]), + np.block([np.arctan2((-P*q02+q13)*chi, (-P*q01-q23)*chi), + np.arctan2( 2.0*chi, q03_s-q12_s ), + np.arctan2(( P*q02+q13)*chi, (-P*q01+q23)*chi)]) + ) + eu = np.where(np.abs(q03_s) < 1.0e-6, + np.block([np.arctan2( 2.0*qu[...,1:2]*qu[...,2:3],qu[...,1:2]**2-qu[...,2:3]**2), + np.ones( qu.shape[:-1]+(1,))*np.pi, + np.zeros(qu.shape[:-1]+(1,))]), + eu) # TODO: Where not needed # reduce Euler angles to definition range, i.e a lower limit of 0.0 eu[np.abs(eu)<1.e-6] = 0.0 eu = np.where(eu<0, (eu+2.0*np.pi)%np.array([2.0*np.pi,np.pi,2.0*np.pi]),eu) @@ -479,38 +515,66 @@ class Rotation: Modified version of the original formulation, should be numerically more stable """ - if iszero(qu[1]**2+qu[2]**2+qu[3]**2): # set axis to [001] if the angle is 0/360 - ax = [ 0.0, 0.0, 1.0, 0.0 ] - elif np.abs(qu[0]) > 1.e-6: - s = np.sign(qu[0])/np.sqrt(qu[1]**2+qu[2]**2+qu[3]**2) - omega = 2.0 * np.arccos(np.clip(qu[0],-1.0,1.0)) - ax = [ qu[1]*s, qu[2]*s, qu[3]*s, omega ] + if len(qu.shape) == 1: + if iszero(np.sum(qu[1:4]**2)): # set axis to [001] if the angle is 0/360 + ax = np.array([ 0.0, 0.0, 1.0, 0.0 ]) + elif np.abs(qu[0]) > 1.e-6: + s = np.sign(qu[0])/np.sqrt(qu[1]**2+qu[2]**2+qu[3]**2) + omega = 2.0 * np.arccos(np.clip(qu[0],-1.0,1.0)) + ax = ax = np.array([ qu[1]*s, qu[2]*s, qu[3]*s, omega ]) + else: + ax = ax = np.array([ qu[1], qu[2], qu[3], np.pi]) else: - ax = [ qu[1], qu[2], qu[3], np.pi] - return np.array(ax) + with np.errstate(divide='ignore'): + s = np.sign(qu[...,0:1])/np.sqrt(qu[...,1:2]**2+qu[...,2:3]**2+qu[...,3:4]**2) + omega = 2.0 * np.arccos(np.clip(qu[...,0:1],-1.0,1.0)) + + ax = np.where(qu[...,0:1] < 1.0e-6, + np.block([qu[...,1:4],np.ones(qu.shape[:-1]+(1,))*np.pi]), + np.block([qu[...,1:4]*s,omega])) + ax = np.where(np.expand_dims(np.sum(np.abs(qu[:,1:4])**2,axis=-1) < 1.0e-6,-1), + [0.0, 0.0, 1.0, 0.0], ax) # TODO: Where not needed + return ax + @staticmethod def qu2ro(qu): """Quaternion to Rodrigues-Frank vector.""" - if iszero(qu[0]): - ro = [qu[1], qu[2], qu[3], np.inf] + if len(qu.shape) == 1: + if iszero(qu[0]): + ro = np.array([qu[1], qu[2], qu[3], np.inf]) + else: + s = np.linalg.norm([qu[1],qu[2],qu[3]]) + ro = np.array([0.0,0.0,P,0.0] if iszero(s) else \ + [ qu[1]/s, qu[2]/s, qu[3]/s, np.tan(np.arccos(np.clip(qu[0],-1.0,1.0)))]) else: - s = np.linalg.norm([qu[1],qu[2],qu[3]]) - ro = [0.0,0.0,P,0.0] if iszero(s) else \ - [ qu[1]/s, qu[2]/s, qu[3]/s, np.tan(np.arccos(np.clip(qu[0],-1.0,1.0)))] - - return np.array(ro) + s = np.expand_dims(np.linalg.norm(qu[...,1:4],axis=1),-1) + ro = np.where(np.abs(s) < 1.0e-12, + [0.0,0.0,P,0.0], + np.block([qu[...,1:2]/s,qu[...,2:3]/s,qu[...,3:4]/s, + np.tan(np.arccos(np.clip(qu[:,0:1],-1.0,1.0))) + ]) + ) + ro = np.where(np.abs(qu[...,0:1]) < 1.0e-12, + np.block([qu[...,1:2], qu[...,2:3], qu[...,3:4], np.ones(qu.shape[:-1]+(1,))*np.inf]),ro) # TODO: Where not needed + return ro @staticmethod def qu2ho(qu): """Quaternion to homochoric vector.""" - if np.isclose(qu[0],1.0): - ho = np.array([ 0.0, 0.0, 0.0 ]) + if len(qu.shape) == 1: + if np.isclose(qu[0],1.0): + ho = np.zeros(3) + else: + omega = 2.0 * np.arccos(np.clip(qu[0],-1.0,1.0)) + ho = np.array([qu[1], qu[2], qu[3]]) + f = 0.75 * ( omega - np.sin(omega) ) + ho = ho/np.linalg.norm(ho) * f**(1./3.) else: - omega = 2.0 * np.arccos(np.clip(qu[0],-1.0,1.0)) - ho = np.array([qu[1], qu[2], qu[3]]) - f = 0.75 * ( omega - np.sin(omega) ) - ho = ho/np.linalg.norm(ho) * f**(1./3.) + omega = 2.0 * np.arccos(np.clip(qu[...,0:1],-1.0,1.0)) + ho = np.where(np.abs(omega) < 1.0e-12, + np.zeros(3), + qu[...,1:4]/np.linalg.norm(qu[...,1:4],axis=1).reshape(qu.shape[:-1]+(1,)) * (0.75*(omega - np.sin(omega)))**(1./3.)) return ho @staticmethod @@ -555,7 +619,7 @@ class Rotation: ax[3] = np.arccos(np.clip(t,-1.0,1.0)) if np.abs(ax[3])<1.e-6: - ax = [ 0.0, 0.0, 1.0, 0.0] + ax = np.array([ 0.0, 0.0, 1.0, 0.0]) else: w,vr = np.linalg.eig(om) # next, find the eigenvalue (1,0j) @@ -563,7 +627,7 @@ class Rotation: ax[0:3] = np.real(vr[0:3,i]) diagDelta = np.array([om[1,2]-om[2,1],om[2,0]-om[0,2],om[0,1]-om[1,0]]) ax[0:3] = np.where(np.abs(diagDelta)<1.e-6, ax[0:3],np.abs(ax[0:3])*np.sign(-P*diagDelta)) - return np.array(ax) + return ax @staticmethod def om2ro(om): @@ -585,57 +649,102 @@ class Rotation: @staticmethod def eu2qu(eu): """Bunge-Euler angles to quaternion.""" - ee = 0.5*eu - cPhi = np.cos(ee[1]) - sPhi = np.sin(ee[1]) - qu = np.array([ cPhi*np.cos(ee[0]+ee[2]), - -P*sPhi*np.cos(ee[0]-ee[2]), - -P*sPhi*np.sin(ee[0]-ee[2]), - -P*cPhi*np.sin(ee[0]+ee[2]) ]) - if qu[0] < 0.0: qu*=-1 + if len(eu.shape) == 1: + ee = 0.5*eu + cPhi = np.cos(ee[1]) + sPhi = np.sin(ee[1]) + qu = np.array([ cPhi*np.cos(ee[0]+ee[2]), + -P*sPhi*np.cos(ee[0]-ee[2]), + -P*sPhi*np.sin(ee[0]-ee[2]), + -P*cPhi*np.sin(ee[0]+ee[2]) ]) + if qu[0] < 0.0: qu*=-1 + else: + ee = 0.5*eu + cPhi = np.cos(ee[...,1:2]) + sPhi = np.sin(ee[...,1:2]) + qu = np.block([ cPhi*np.cos(ee[...,0:1]+ee[...,2:3]), + -P*sPhi*np.cos(ee[...,0:1]-ee[...,2:3]), + -P*sPhi*np.sin(ee[...,0:1]-ee[...,2:3]), + -P*cPhi*np.sin(ee[...,0:1]+ee[...,2:3])]) + qu[qu[...,0]<0.0]*=-1 return qu + @staticmethod def eu2om(eu): """Bunge-Euler angles to rotation matrix.""" - c = np.cos(eu) - s = np.sin(eu) + if len(eu.shape) == 1: + c = np.cos(eu) + s = np.sin(eu) - om = np.array([[+c[0]*c[2]-s[0]*s[2]*c[1], +s[0]*c[2]+c[0]*s[2]*c[1], +s[2]*s[1]], - [-c[0]*s[2]-s[0]*c[2]*c[1], -s[0]*s[2]+c[0]*c[2]*c[1], +c[2]*s[1]], - [+s[0]*s[1], -c[0]*s[1], +c[1] ]]) - - om[np.abs(om)<1.e-6] = 0.0 + om = np.array([[+c[0]*c[2]-s[0]*s[2]*c[1], +s[0]*c[2]+c[0]*s[2]*c[1], +s[2]*s[1]], + [-c[0]*s[2]-s[0]*c[2]*c[1], -s[0]*s[2]+c[0]*c[2]*c[1], +c[2]*s[1]], + [+s[0]*s[1], -c[0]*s[1], +c[1] ]]) + else: + c = np.cos(eu) + s = np.sin(eu) + om = np.block([+c[...,0:1]*c[...,2:3]-s[...,0:1]*s[...,2:3]*c[...,1:2], + +s[...,0:1]*c[...,2:3]+c[...,0:1]*s[...,2:3]*c[...,1:2], + +s[...,2:3]*s[...,1:2], + -c[...,0:1]*s[...,2:3]-s[...,0:1]*c[...,2:3]*c[...,1:2], + -s[...,0:1]*s[...,2:3]+c[...,0:1]*c[...,2:3]*c[...,1:2], + +c[...,2:3]*s[...,1:2], + +s[...,0:1]*s[...,1:2], + -c[...,0:1]*s[...,1:2], + +c[...,1:2] + ]).reshape(eu.shape[:-1]+(3,3)) + om[np.abs(om)<1.e-12] = 0.0 return om @staticmethod def eu2ax(eu): """Bunge-Euler angles to axis angle pair.""" - t = np.tan(eu[1]*0.5) - sigma = 0.5*(eu[0]+eu[2]) - delta = 0.5*(eu[0]-eu[2]) - tau = np.linalg.norm([t,np.sin(sigma)]) - alpha = np.pi if iszero(np.cos(sigma)) else \ - 2.0*np.arctan(tau/np.cos(sigma)) + if len(eu.shape) == 1: + t = np.tan(eu[1]*0.5) + sigma = 0.5*(eu[0]+eu[2]) + delta = 0.5*(eu[0]-eu[2]) + tau = np.linalg.norm([t,np.sin(sigma)]) + alpha = np.pi if iszero(np.cos(sigma)) else \ + 2.0*np.arctan(tau/np.cos(sigma)) - if np.abs(alpha)<1.e-6: - ax = np.array([ 0.0, 0.0, 1.0, 0.0 ]) + if np.abs(alpha)<1.e-6: + ax = np.array([ 0.0, 0.0, 1.0, 0.0 ]) + else: + ax = -P/tau * np.array([ t*np.cos(delta), t*np.sin(delta), np.sin(sigma) ]) # passive axis angle pair so a minus sign in front + ax = np.append(ax,alpha) + if alpha < 0.0: ax *= -1.0 # ensure alpha is positive else: - ax = -P/tau * np.array([ t*np.cos(delta), t*np.sin(delta), np.sin(sigma) ]) # passive axis angle pair so a minus sign in front - ax = np.append(ax,alpha) - if alpha < 0.0: ax *= -1.0 # ensure alpha is positive + t = np.tan(eu[...,1:2]*0.5) + sigma = 0.5*(eu[...,0:1]+eu[...,2:3]) + delta = 0.5*(eu[...,0:1]-eu[...,2:3]) + tau = np.linalg.norm(np.block([t,np.sin(sigma)]),axis=-1).reshape(-1,1) + alpha = np.where(np.abs(np.cos(sigma))<1.e-12,np.pi,2.0*np.arctan(tau/np.cos(sigma))) + ax = np.where(np.broadcast_to(np.abs(alpha)<1.0e-12,eu.shape[:-1]+(4,)), + [0.0,0.0,1.0,0.0], + np.block([-P/tau*t*np.cos(delta), + -P/tau*t*np.sin(delta), + -P/tau* np.sin(sigma), + alpha + ])) + ax[(alpha<0.0).squeeze()] *=-1 return ax @staticmethod def eu2ro(eu): """Bunge-Euler angles to Rodrigues-Frank vector.""" - ro = Rotation.eu2ax(eu) # convert to axis angle pair representation - if ro[3] >= np.pi: # Differs from original implementation. check convention 5 - ro[3] = np.inf - elif iszero(ro[3]): - ro = np.array([ 0.0, 0.0, P, 0.0 ]) + if len(eu.shape) == 1: + ro = Rotation.eu2ax(eu) # convert to axis angle pair representation + if ro[3] >= np.pi: # Differs from original implementation. check convention 5 + ro[3] = np.inf + elif iszero(ro[3]): + ro = np.array([ 0.0, 0.0, P, 0.0 ]) + else: + ro[3] = np.tan(ro[3]*0.5) else: - ro[3] = np.tan(ro[3]*0.5) + ax = Rotation.eu2ax(eu) + ro = np.block([ax[:,:3],np.tan(ax[:,3:4]*.5)]) + ro[ax[:,3]>=np.pi,3] = np.inf + ro[np.abs(ax[:,3])<1.e-16] = [ 0.0, 0.0, P, 0.0 ] return ro @staticmethod @@ -664,9 +773,7 @@ class Rotation: else: c = np.cos(ax[...,3:4]*.5) s = np.sin(ax[...,3:4]*.5) - qu = np.where(np.abs(ax[...,3:4])<1.e-12, - [1.0, 0.0, 0.0, 0.0], - np.block([c, ax[...,:3]*s])) + qu = np.where(np.abs(ax[...,3:4])<1.e-12,[1.0, 0.0, 0.0, 0.0],np.block([c, ax[...,:3]*s])) return qu @staticmethod @@ -687,7 +794,7 @@ class Rotation: c = np.cos(ax[...,3:4]) s = np.sin(ax[...,3:4]) omc = 1. -c - ax = np.block([c+omc*ax[...,0:1]**2, + om = np.block([c+omc*ax[...,0:1]**2, omc*ax[...,0:1]*ax[...,1:2] + s*ax[...,2:3], omc*ax[...,0:1]*ax[...,2:3] - s*ax[...,1:2], omc*ax[...,0:1]*ax[...,1:2] - s*ax[...,2:3], @@ -696,7 +803,7 @@ class Rotation: omc*ax[...,0:1]*ax[...,2:3] + s*ax[...,1:2], omc*ax[...,1:2]*ax[...,2:3] - s*ax[...,0:1], c+omc*ax[...,2:3]**2]).reshape(ax.shape[:-1]+(3,3)) - return ax + return om # TODO: TRANSPOSE FOR P = 1 @staticmethod def ax2eu(ax): diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 13c474ca7..eaf614b4e 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -150,6 +150,28 @@ class TestRotation: print(m,o,rot.asQuaternion()) assert np.allclose(m,o,atol=atol) + @pytest.mark.parametrize('conversion',[Rotation.qu2om, + Rotation.qu2eu, + Rotation.qu2ax, + Rotation.qu2ro, + Rotation.qu2ho]) + def test_quaternion_vectorization(self,default,conversion): + qu = np.array([rot.asQuaternion() for rot in default]) + co = conversion(qu) + for q,c in zip(qu,co): + assert np.allclose(conversion(q),c) + + @pytest.mark.parametrize('conversion',[Rotation.eu2qu, + Rotation.eu2om, + Rotation.eu2ax, + Rotation.eu2ro, + ]) + def test_Euler_vectorization(self,default,conversion): + qu = np.array([rot.asEulers() for rot in default]) + co = conversion(qu) + for q,c in zip(qu,co): + assert np.allclose(conversion(q),c) + @pytest.mark.parametrize('conversion',[Rotation.ax2qu, Rotation.ax2om, Rotation.ax2ro, From 43e7639f778156f1c97c87dfd4001e4dd507e66f Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 9 Apr 2020 00:47:43 +0200 Subject: [PATCH 15/53] WIP: implementing orientation matrix conversions --- python/damask/_rotation.py | 24 ++++++++++++++++-------- python/tests/test_Rotation.py | 8 ++++++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 9296f57b4..06a400fad 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -596,19 +596,27 @@ class Rotation: @staticmethod def om2eu(om): """Rotation matrix to Bunge-Euler angles.""" - if not np.isclose(np.abs(om[2,2]),1.0,1.e-4): - zeta = 1.0/np.sqrt(1.0-om[2,2]**2) - eu = np.array([np.arctan2(om[2,0]*zeta,-om[2,1]*zeta), - np.arccos(om[2,2]), - np.arctan2(om[0,2]*zeta, om[1,2]*zeta)]) + if len(om.shape) == 2: + if not np.isclose(np.abs(om[2,2]),1.0,1.e-4): + zeta = 1.0/np.sqrt(1.0-om[2,2]**2) + eu = np.array([np.arctan2(om[2,0]*zeta,-om[2,1]*zeta), + np.arccos(om[2,2]), + np.arctan2(om[0,2]*zeta, om[1,2]*zeta)]) + else: + eu = np.array([np.arctan2( om[0,1],om[0,0]), np.pi*0.5*(1-om[2,2]),0.0]) # following the paper, not the reference implementation else: - eu = np.array([np.arctan2( om[0,1],om[0,0]), np.pi*0.5*(1-om[2,2]),0.0]) # following the paper, not the reference implementation - - # reduce Euler angles to definition range, i.e a lower limit of 0.0 + with np.errstate(divide='ignore'): + zeta = 1.0/np.sqrt(1.0-om[...,2,2:3]**2) + eu = np.block([np.arctan2(om[...,2,0:1]*zeta,-om[...,2,1:2]*zeta), + np.arccos(om[...,2,2:3]), + np.arctan2(om[...,0,2:3]*zeta,+om[...,1,2:3]*zeta) + ]) + # TODO Special case not implemented! eu[np.abs(eu)<1.e-6] = 0.0 eu = np.where(eu<0, (eu+2.0*np.pi)%np.array([2.0*np.pi,np.pi,2.0*np.pi]),eu) return eu + @staticmethod def om2ax(om): """Rotation matrix to axis angle pair.""" diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index eaf614b4e..448145c45 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -161,6 +161,14 @@ class TestRotation: for q,c in zip(qu,co): assert np.allclose(conversion(q),c) + @pytest.mark.parametrize('conversion',[Rotation.om2eu, + ]) + def test_matrix_vectorization(self,default,conversion): + qu = np.array([rot.asMatrix() for rot in default]) + co = conversion(qu) + for q,c in zip(qu,co): + assert np.allclose(conversion(q),c) + @pytest.mark.parametrize('conversion',[Rotation.eu2qu, Rotation.eu2om, Rotation.eu2ax, From cbfde73a29dbfe71d65860314a60fba119ad9687 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 9 Apr 2020 07:40:20 +0200 Subject: [PATCH 16/53] more testing and related fixes --- python/damask/_rotation.py | 108 +++++++++++++++++++--------------- python/tests/test_Rotation.py | 10 ++-- 2 files changed, 67 insertions(+), 51 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 06a400fad..b6c5ff0c7 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -474,7 +474,7 @@ class Rotation: q03 = qu[0]**2+qu[3]**2 q12 = qu[1]**2+qu[2]**2 chi = np.sqrt(q03*q12) - if np.abs(q03)< 1.e-6: + if np.abs(chi)< 1.e-6: eu = np.array([np.arctan2(-P*2.0*qu[0]*qu[3],qu[0]**2-qu[3]**2), 0.0, 0.0]) elif np.abs(q12)< 1.e-6: eu = np.array([np.arctan2( 2.0*qu[1]*qu[2],qu[1]**2-qu[2]**2), np.pi, 0.0]) @@ -491,14 +491,14 @@ class Rotation: q12_s = qu[...,1:2]**2+qu[...,2:3]**2 chi = np.sqrt(q03_s*q12_s) - eu = np.where(np.abs(q12_s) < 1.0e-6, + eu = np.where(np.abs(chi) < 1.0e-6, np.block([np.arctan2(-P*2.0*qu[...,0:1]*qu[...,3:4],qu[...,0:1]**2-qu[...,3:4]**2), np.zeros(qu.shape[:-1]+(2,))]), np.block([np.arctan2((-P*q02+q13)*chi, (-P*q01-q23)*chi), np.arctan2( 2.0*chi, q03_s-q12_s ), np.arctan2(( P*q02+q13)*chi, (-P*q01+q23)*chi)]) ) - eu = np.where(np.abs(q03_s) < 1.0e-6, + eu = np.where(np.logical_and(np.abs(q03_s) < 1.0e-6, np.abs(chi) > 1.0e-6), np.block([np.arctan2( 2.0*qu[...,1:2]*qu[...,2:3],qu[...,1:2]**2-qu[...,2:3]**2), np.ones( qu.shape[:-1]+(1,))*np.pi, np.zeros(qu.shape[:-1]+(1,))]), @@ -525,18 +525,15 @@ class Rotation: else: ax = ax = np.array([ qu[1], qu[2], qu[3], np.pi]) else: - with np.errstate(divide='ignore'): + with np.errstate(invalid='ignore',divide='ignore'): s = np.sign(qu[...,0:1])/np.sqrt(qu[...,1:2]**2+qu[...,2:3]**2+qu[...,3:4]**2) - omega = 2.0 * np.arccos(np.clip(qu[...,0:1],-1.0,1.0)) - + omega = 2.0 * np.arccos(np.clip(qu[...,0:1],-1.0,1.0)) + ax = np.where(np.expand_dims(np.sum(np.abs(qu[:,1:4])**2,axis=-1) < 1.0e-6,-1), + [0.0, 0.0, 1.0, 0.0], np.block([qu[...,1:4]*s,omega])) ax = np.where(qu[...,0:1] < 1.0e-6, - np.block([qu[...,1:4],np.ones(qu.shape[:-1]+(1,))*np.pi]), - np.block([qu[...,1:4]*s,omega])) - ax = np.where(np.expand_dims(np.sum(np.abs(qu[:,1:4])**2,axis=-1) < 1.0e-6,-1), - [0.0, 0.0, 1.0, 0.0], ax) # TODO: Where not needed + np.block([qu[...,1:4],np.ones(qu.shape[:-1]+(1,))*np.pi]),ax) # TODO: Where not needed return ax - @staticmethod def qu2ro(qu): """Quaternion to Rodrigues-Frank vector.""" @@ -548,12 +545,13 @@ class Rotation: ro = np.array([0.0,0.0,P,0.0] if iszero(s) else \ [ qu[1]/s, qu[2]/s, qu[3]/s, np.tan(np.arccos(np.clip(qu[0],-1.0,1.0)))]) else: - s = np.expand_dims(np.linalg.norm(qu[...,1:4],axis=1),-1) - ro = np.where(np.abs(s) < 1.0e-12, - [0.0,0.0,P,0.0], - np.block([qu[...,1:2]/s,qu[...,2:3]/s,qu[...,3:4]/s, - np.tan(np.arccos(np.clip(qu[:,0:1],-1.0,1.0))) - ]) + with np.errstate(invalid='ignore',divide='ignore'): + s = np.expand_dims(np.linalg.norm(qu[...,1:4],axis=1),-1) + ro = np.where(np.abs(s) < 1.0e-12, + [0.0,0.0,P,0.0], + np.block([qu[...,1:2]/s,qu[...,2:3]/s,qu[...,3:4]/s, + np.tan(np.arccos(np.clip(qu[:,0:1],-1.0,1.0))) + ]) ) ro = np.where(np.abs(qu[...,0:1]) < 1.0e-12, np.block([qu[...,1:2], qu[...,2:3], qu[...,3:4], np.ones(qu.shape[:-1]+(1,))*np.inf]),ro) # TODO: Where not needed @@ -571,10 +569,12 @@ class Rotation: f = 0.75 * ( omega - np.sin(omega) ) ho = ho/np.linalg.norm(ho) * f**(1./3.) else: - omega = 2.0 * np.arccos(np.clip(qu[...,0:1],-1.0,1.0)) - ho = np.where(np.abs(omega) < 1.0e-12, - np.zeros(3), - qu[...,1:4]/np.linalg.norm(qu[...,1:4],axis=1).reshape(qu.shape[:-1]+(1,)) * (0.75*(omega - np.sin(omega)))**(1./3.)) + with np.errstate(invalid='ignore'): + omega = 2.0 * np.arccos(np.clip(qu[...,0:1],-1.0,1.0)) + ho = np.where(np.abs(omega) < 1.0e-12, + np.zeros(3), + qu[...,1:4]/np.linalg.norm(qu[...,1:4],axis=1).reshape(qu.shape[:-1]+(1,)) \ + * (0.75*(omega - np.sin(omega)))**(1./3.)) return ho @staticmethod @@ -605,13 +605,18 @@ class Rotation: else: eu = np.array([np.arctan2( om[0,1],om[0,0]), np.pi*0.5*(1-om[2,2]),0.0]) # following the paper, not the reference implementation else: - with np.errstate(divide='ignore'): - zeta = 1.0/np.sqrt(1.0-om[...,2,2:3]**2) - eu = np.block([np.arctan2(om[...,2,0:1]*zeta,-om[...,2,1:2]*zeta), - np.arccos(om[...,2,2:3]), - np.arctan2(om[...,0,2:3]*zeta,+om[...,1,2:3]*zeta) - ]) - # TODO Special case not implemented! + with np.errstate(invalid='ignore',divide='ignore'): + zeta = 1.0/np.sqrt(1.0-om[...,2,2:3]**2) + eu = np.where(np.isclose(np.abs(om[...,2,2:3]),1.0,1e-4), + np.block([np.arctan2(om[...,0,1:2],om[...,0,0:1]), + np.pi*0.5*(1-om[...,2,2:3]), + np.zeros(om.shape[:-2]+(1,)), + ]), + np.block([np.arctan2(om[...,2,0:1]*zeta,-om[...,2,1:2]*zeta), + np.arccos(om[...,2,2:3]), + np.arctan2(om[...,0,2:3]*zeta,+om[...,1,2:3]*zeta) + ]) + ) eu[np.abs(eu)<1.e-6] = 0.0 eu = np.where(eu<0, (eu+2.0*np.pi)%np.array([2.0*np.pi,np.pi,2.0*np.pi]),eu) return eu @@ -620,22 +625,30 @@ class Rotation: @staticmethod def om2ax(om): """Rotation matrix to axis angle pair.""" - ax=np.empty(4) + if len(om.shape) == 2: + ax=np.empty(4) - # first get the rotation angle - t = 0.5*(om.trace() -1.0) - ax[3] = np.arccos(np.clip(t,-1.0,1.0)) + # first get the rotation angle + t = 0.5*(om.trace() -1.0) + ax[3] = np.arccos(np.clip(t,-1.0,1.0)) - if np.abs(ax[3])<1.e-6: - ax = np.array([ 0.0, 0.0, 1.0, 0.0]) + if np.abs(ax[3])<1.e-6: + ax = np.array([ 0.0, 0.0, 1.0, 0.0]) + else: + w,vr = np.linalg.eig(om) + # next, find the eigenvalue (1,0j) + i = np.where(np.isclose(w,1.0+0.0j))[0][0] + ax[0:3] = np.real(vr[0:3,i]) + diagDelta = np.array([om[1,2]-om[2,1],om[2,0]-om[0,2],om[0,1]-om[1,0]]) + ax[0:3] = np.where(np.abs(diagDelta)<1.e-6, ax[0:3],np.abs(ax[0:3])*np.sign(-P*diagDelta)) + return ax else: + diag_delta = np.block([om[...,1,2:3]-om[...,2,1:2], + om[...,2,0:1]-om[...,0,2:3], + om[...,0,1:2]-om[...,1,0:1] + ]) w,vr = np.linalg.eig(om) - # next, find the eigenvalue (1,0j) - i = np.where(np.isclose(w,1.0+0.0j))[0][0] - ax[0:3] = np.real(vr[0:3,i]) - diagDelta = np.array([om[1,2]-om[2,1],om[2,0]-om[0,2],om[0,1]-om[1,0]]) - ax[0:3] = np.where(np.abs(diagDelta)<1.e-6, ax[0:3],np.abs(ax[0:3])*np.sign(-P*diagDelta)) - return ax + # TODO ------------------ @staticmethod def om2ro(om): @@ -727,13 +740,14 @@ class Rotation: delta = 0.5*(eu[...,0:1]-eu[...,2:3]) tau = np.linalg.norm(np.block([t,np.sin(sigma)]),axis=-1).reshape(-1,1) alpha = np.where(np.abs(np.cos(sigma))<1.e-12,np.pi,2.0*np.arctan(tau/np.cos(sigma))) - ax = np.where(np.broadcast_to(np.abs(alpha)<1.0e-12,eu.shape[:-1]+(4,)), - [0.0,0.0,1.0,0.0], - np.block([-P/tau*t*np.cos(delta), - -P/tau*t*np.sin(delta), - -P/tau* np.sin(sigma), - alpha - ])) + with np.errstate(invalid='ignore',divide='ignore'): + ax = np.where(np.broadcast_to(np.abs(alpha)<1.0e-12,eu.shape[:-1]+(4,)), + [0.0,0.0,1.0,0.0], + np.block([-P/tau*t*np.cos(delta), + -P/tau*t*np.sin(delta), + -P/tau* np.sin(sigma), + alpha + ])) ax[(alpha<0.0).squeeze()] *=-1 return ax diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 448145c45..875a05d99 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -5,7 +5,7 @@ import numpy as np from damask import Rotation -n = 1000 +n = 1100 atol=1.e-4 scatter=1.e-2 @@ -74,10 +74,12 @@ def default(): np.array([1.0, 1.0,-1.0,-1.0])/2., np.array([1.0,-1.0,-1.0,-1.0])/2., ]) - specials += np.broadcast_to(np.random.rand(4)*scatter,specials.shape) - specials /= np.linalg.norm(specials,axis=1).reshape(-1,1) - specials[specials[:,0]<0]*=-1 + specials_scatter = specials + np.broadcast_to(np.random.rand(4)*scatter,specials.shape) + specials_scatter /= np.linalg.norm(specials_scatter,axis=1).reshape(-1,1) + specials_scatter[specials_scatter[:,0]<0]*=-1 + return [Rotation.fromQuaternion(s) for s in specials] + \ + [Rotation.fromQuaternion(s) for s in specials_scatter] + \ [Rotation.fromRandom() for _ in range(n-len(specials))] @pytest.fixture From b025c1838eebad6ac53d4bda0cfaf8a8bc73a8fa Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 9 Apr 2020 12:52:12 +0200 Subject: [PATCH 17/53] simplified --- python/damask/_rotation.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index b6c5ff0c7..97f494947 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -528,7 +528,7 @@ class Rotation: with np.errstate(invalid='ignore',divide='ignore'): s = np.sign(qu[...,0:1])/np.sqrt(qu[...,1:2]**2+qu[...,2:3]**2+qu[...,3:4]**2) omega = 2.0 * np.arccos(np.clip(qu[...,0:1],-1.0,1.0)) - ax = np.where(np.expand_dims(np.sum(np.abs(qu[:,1:4])**2,axis=-1) < 1.0e-6,-1), + ax = np.where(np.sum(np.abs(qu[:,1:4])**2,axis=-1,keepdims=True) < 1.0e-6, [0.0, 0.0, 1.0, 0.0], np.block([qu[...,1:4]*s,omega])) ax = np.where(qu[...,0:1] < 1.0e-6, np.block([qu[...,1:4],np.ones(qu.shape[:-1]+(1,))*np.pi]),ax) # TODO: Where not needed @@ -541,12 +541,12 @@ class Rotation: if iszero(qu[0]): ro = np.array([qu[1], qu[2], qu[3], np.inf]) else: - s = np.linalg.norm([qu[1],qu[2],qu[3]]) + s = np.linalg.norm(qu[1:4]) ro = np.array([0.0,0.0,P,0.0] if iszero(s) else \ [ qu[1]/s, qu[2]/s, qu[3]/s, np.tan(np.arccos(np.clip(qu[0],-1.0,1.0)))]) else: with np.errstate(invalid='ignore',divide='ignore'): - s = np.expand_dims(np.linalg.norm(qu[...,1:4],axis=1),-1) + s = np.linalg.norm(qu[...,1:4],axis=-1,keepdims=True) ro = np.where(np.abs(s) < 1.0e-12, [0.0,0.0,P,0.0], np.block([qu[...,1:2]/s,qu[...,2:3]/s,qu[...,3:4]/s, @@ -573,7 +573,7 @@ class Rotation: omega = 2.0 * np.arccos(np.clip(qu[...,0:1],-1.0,1.0)) ho = np.where(np.abs(omega) < 1.0e-12, np.zeros(3), - qu[...,1:4]/np.linalg.norm(qu[...,1:4],axis=1).reshape(qu.shape[:-1]+(1,)) \ + qu[...,1:4]/np.linalg.norm(qu[...,1:4],axis=-1,keepdims=True) \ * (0.75*(omega - np.sin(omega)))**(1./3.)) return ho @@ -738,7 +738,7 @@ class Rotation: t = np.tan(eu[...,1:2]*0.5) sigma = 0.5*(eu[...,0:1]+eu[...,2:3]) delta = 0.5*(eu[...,0:1]-eu[...,2:3]) - tau = np.linalg.norm(np.block([t,np.sin(sigma)]),axis=-1).reshape(-1,1) + tau = np.linalg.norm(np.block([t,np.sin(sigma)]),axis=-1,keepdims=True) alpha = np.where(np.abs(np.cos(sigma))<1.e-12,np.pi,2.0*np.arctan(tau/np.cos(sigma))) with np.errstate(invalid='ignore',divide='ignore'): ax = np.where(np.broadcast_to(np.abs(alpha)<1.0e-12,eu.shape[:-1]+(4,)), From e502573e05a6071ce1dd74e9249a66a31a3d6910 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 9 Apr 2020 14:20:43 +0200 Subject: [PATCH 18/53] polishing --- python/damask/_rotation.py | 16 +++++++++++++--- python/tests/test_Rotation.py | 30 +++++++++++++++++++----------- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 97f494947..4756e94c2 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -640,15 +640,25 @@ class Rotation: i = np.where(np.isclose(w,1.0+0.0j))[0][0] ax[0:3] = np.real(vr[0:3,i]) diagDelta = np.array([om[1,2]-om[2,1],om[2,0]-om[0,2],om[0,1]-om[1,0]]) - ax[0:3] = np.where(np.abs(diagDelta)<1.e-6, ax[0:3],np.abs(ax[0:3])*np.sign(-P*diagDelta)) - return ax + ax[0:3] = np.where(np.abs(diagDelta)<0, ax[0:3],np.abs(ax[0:3])*np.sign(-P*diagDelta)) else: diag_delta = np.block([om[...,1,2:3]-om[...,2,1:2], om[...,2,0:1]-om[...,0,2:3], om[...,0,1:2]-om[...,1,0:1] ]) + t = 0.5*(om.trace(axis2=-2,axis1=-1) -1.0).reshape(om.shape[:-2]+(1,)) w,vr = np.linalg.eig(om) - # TODO ------------------ + # mask duplicated real eigenvalues + w[np.isclose(w[...,0],1.0+0.0j),1:] = 0. + w[np.isclose(w[...,1],1.0+0.0j),2:] = 0. + ax = np.where(np.abs(diag_delta)<0, + np.real(vr[np.isclose(w,1.0+0.0j)]).reshape(om.shape[:-2]+(3,)), + np.real(vr[np.isclose(w,1.0+0.0j)]).reshape(om.shape[:-2]+(3,)) \ + * np.abs(diag_delta)*np.sign(-P*diag_delta)) + ax = np.block([ax,np.arccos(np.clip(t,-1.0,1.0))]) + ax[np.abs(ax[...,3])<1.e-6] = [ 0.0, 0.0, 1.0, 0.0] + return ax + @staticmethod def om2ro(om): diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 875a05d99..6bb0f5160 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -80,7 +80,7 @@ def default(): return [Rotation.fromQuaternion(s) for s in specials] + \ [Rotation.fromQuaternion(s) for s in specials_scatter] + \ - [Rotation.fromRandom() for _ in range(n-len(specials))] + [Rotation.fromRandom() for _ in range(n-len(specials)-len(specials_scatter))] @pytest.fixture def reference_dir(reference_dir_base): @@ -159,17 +159,22 @@ class TestRotation: Rotation.qu2ho]) def test_quaternion_vectorization(self,default,conversion): qu = np.array([rot.asQuaternion() for rot in default]) + #dev_null = conversion(qu.reshape(qu.shape[0]//2,-1,4)) co = conversion(qu) for q,c in zip(qu,co): - assert np.allclose(conversion(q),c) + print(q,c) + assert np.allclose(conversion(q),c) @pytest.mark.parametrize('conversion',[Rotation.om2eu, + #Rotation.om2ax, ]) def test_matrix_vectorization(self,default,conversion): - qu = np.array([rot.asMatrix() for rot in default]) - co = conversion(qu) - for q,c in zip(qu,co): - assert np.allclose(conversion(q),c) + om = np.array([rot.asMatrix() for rot in default]) + #dev_null = conversion(om.reshape(om.shape[0]//2,-1,3,3)) + co = conversion(om) + for o,c in zip(om,co): + print(o,c) + assert np.allclose(conversion(o),c) @pytest.mark.parametrize('conversion',[Rotation.eu2qu, Rotation.eu2om, @@ -177,10 +182,12 @@ class TestRotation: Rotation.eu2ro, ]) def test_Euler_vectorization(self,default,conversion): - qu = np.array([rot.asEulers() for rot in default]) - co = conversion(qu) - for q,c in zip(qu,co): - assert np.allclose(conversion(q),c) + eu = np.array([rot.asEulers() for rot in default]) + #dev_null = conversion(eu.reshape(eu.shape[0]//2,-1,3)) + co = conversion(eu) + for e,c in zip(eu,co): + print(e,c) + assert np.allclose(conversion(e),c) @pytest.mark.parametrize('conversion',[Rotation.ax2qu, Rotation.ax2om, @@ -192,4 +199,5 @@ class TestRotation: dev_null = conversion(ax.reshape(ax.shape[0]//2,-1,4)) co = conversion(ax) for a,c in zip(ax,co): - assert np.allclose(conversion(a),c) + print(a,c) + assert np.allclose(conversion(a),c) From bab3581b1194c74e5ca3c217db8d2ad1e78b0531 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 9 Apr 2020 15:01:01 +0200 Subject: [PATCH 19/53] need to transpose eigenvectors to find the correct one --- python/damask/_rotation.py | 22 ++++++++++++---------- python/tests/test_Rotation.py | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 4756e94c2..3336eb6c0 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -631,7 +631,6 @@ class Rotation: # first get the rotation angle t = 0.5*(om.trace() -1.0) ax[3] = np.arccos(np.clip(t,-1.0,1.0)) - if np.abs(ax[3])<1.e-6: ax = np.array([ 0.0, 0.0, 1.0, 0.0]) else: @@ -639,22 +638,25 @@ class Rotation: # next, find the eigenvalue (1,0j) i = np.where(np.isclose(w,1.0+0.0j))[0][0] ax[0:3] = np.real(vr[0:3,i]) - diagDelta = np.array([om[1,2]-om[2,1],om[2,0]-om[0,2],om[0,1]-om[1,0]]) - ax[0:3] = np.where(np.abs(diagDelta)<0, ax[0:3],np.abs(ax[0:3])*np.sign(-P*diagDelta)) + diagDelta = -P*np.array([om[1,2]-om[2,1],om[2,0]-om[0,2],om[0,1]-om[1,0]]) + diagDelta[np.abs(diagDelta)<1.e-6] = 1.0 + ax[0:3] = np.where(np.abs(diagDelta)<0, ax[0:3],np.abs(ax[0:3])*np.sign(diagDelta)) else: - diag_delta = np.block([om[...,1,2:3]-om[...,2,1:2], - om[...,2,0:1]-om[...,0,2:3], - om[...,0,1:2]-om[...,1,0:1] - ]) + diag_delta = -P*np.block([om[...,1,2:3]-om[...,2,1:2], + om[...,2,0:1]-om[...,0,2:3], + om[...,0,1:2]-om[...,1,0:1] + ]) + diag_delta[np.abs(diag_delta)<1.e-6] = 1.0 t = 0.5*(om.trace(axis2=-2,axis1=-1) -1.0).reshape(om.shape[:-2]+(1,)) w,vr = np.linalg.eig(om) # mask duplicated real eigenvalues w[np.isclose(w[...,0],1.0+0.0j),1:] = 0. w[np.isclose(w[...,1],1.0+0.0j),2:] = 0. + vr = np.swapaxes(vr,-1,-2) ax = np.where(np.abs(diag_delta)<0, - np.real(vr[np.isclose(w,1.0+0.0j)]).reshape(om.shape[:-2]+(3,)), - np.real(vr[np.isclose(w,1.0+0.0j)]).reshape(om.shape[:-2]+(3,)) \ - * np.abs(diag_delta)*np.sign(-P*diag_delta)) + np.real(vr[np.isclose(w,1.0+0.0j)]).reshape(om.shape[:-2]+(3,)), + np.abs(np.real(vr[np.isclose(w,1.0+0.0j)]).reshape(om.shape[:-2]+(3,))) \ + *np.sign(diag_delta)) ax = np.block([ax,np.arccos(np.clip(t,-1.0,1.0))]) ax[np.abs(ax[...,3])<1.e-6] = [ 0.0, 0.0, 1.0, 0.0] return ax diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 6bb0f5160..02d561b66 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -166,7 +166,7 @@ class TestRotation: assert np.allclose(conversion(q),c) @pytest.mark.parametrize('conversion',[Rotation.om2eu, - #Rotation.om2ax, + Rotation.om2ax, ]) def test_matrix_vectorization(self,default,conversion): om = np.array([rot.asMatrix() for rot in default]) From 98373904068d19b6a083210d6f4f7f531f2d54f1 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 10 Apr 2020 12:30:39 +0200 Subject: [PATCH 20/53] do not clutter namespace we do not need damask.util.np etc --- python/damask/grid_filters.py | 142 +++++++++++++++++----------------- python/damask/mechanics.py | 76 +++++++++--------- 2 files changed, 109 insertions(+), 109 deletions(-) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index 35876fc99..8106ed905 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -1,5 +1,5 @@ -from scipy import spatial -import numpy as np +from scipy import spatial as _spatial +import numpy as _np def _ks(size,grid,first_order=False): """ @@ -11,16 +11,16 @@ def _ks(size,grid,first_order=False): physical size of the periodic field. """ - k_sk = np.where(np.arange(grid[0])>grid[0]//2,np.arange(grid[0])-grid[0],np.arange(grid[0]))/size[0] + k_sk = _np.where(_np.arange(grid[0])>grid[0]//2,_np.arange(grid[0])-grid[0],_np.arange(grid[0]))/size[0] if grid[0]%2 == 0 and first_order: k_sk[grid[0]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) - k_sj = np.where(np.arange(grid[1])>grid[1]//2,np.arange(grid[1])-grid[1],np.arange(grid[1]))/size[1] + k_sj = _np.where(_np.arange(grid[1])>grid[1]//2,_np.arange(grid[1])-grid[1],_np.arange(grid[1]))/size[1] if grid[1]%2 == 0 and first_order: k_sj[grid[1]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) - k_si = np.arange(grid[2]//2+1)/size[2] + k_si = _np.arange(grid[2]//2+1)/size[2] - kk, kj, ki = np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij') - return np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3) + kk, kj, ki = _np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij') + return _np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3) def curl(size,field): @@ -33,18 +33,18 @@ def curl(size,field): physical size of the periodic field. """ - n = np.prod(field.shape[3:]) + n = _np.prod(field.shape[3:]) k_s = _ks(size,field.shape[:3],True) - e = np.zeros((3, 3, 3)) + e = _np.zeros((3, 3, 3)) e[0, 1, 2] = e[1, 2, 0] = e[2, 0, 1] = +1.0 # Levi-Civita symbol e[0, 2, 1] = e[2, 1, 0] = e[1, 0, 2] = -1.0 - field_fourier = np.fft.rfftn(field,axes=(0,1,2)) - curl_ = (np.einsum('slm,ijkl,ijkm ->ijks', e,k_s,field_fourier)*2.0j*np.pi if n == 3 else # vector, 3 -> 3 - np.einsum('slm,ijkl,ijknm->ijksn',e,k_s,field_fourier)*2.0j*np.pi) # tensor, 3x3 -> 3x3 + field_fourier = _np.fft.rfftn(field,axes=(0,1,2)) + curl_ = (_np.einsum('slm,ijkl,ijkm ->ijks', e,k_s,field_fourier)*2.0j*_np.pi if n == 3 else # vector, 3 -> 3 + _np.einsum('slm,ijkl,ijknm->ijksn',e,k_s,field_fourier)*2.0j*_np.pi) # tensor, 3x3 -> 3x3 - return np.fft.irfftn(curl_,axes=(0,1,2),s=field.shape[:3]) + return _np.fft.irfftn(curl_,axes=(0,1,2),s=field.shape[:3]) def divergence(size,field): @@ -57,14 +57,14 @@ def divergence(size,field): physical size of the periodic field. """ - n = np.prod(field.shape[3:]) + n = _np.prod(field.shape[3:]) k_s = _ks(size,field.shape[:3],True) - field_fourier = np.fft.rfftn(field,axes=(0,1,2)) - div_ = (np.einsum('ijkl,ijkl ->ijk', k_s,field_fourier)*2.0j*np.pi if n == 3 else # vector, 3 -> 1 - np.einsum('ijkm,ijklm->ijkl',k_s,field_fourier)*2.0j*np.pi) # tensor, 3x3 -> 3 + field_fourier = _np.fft.rfftn(field,axes=(0,1,2)) + div_ = (_np.einsum('ijkl,ijkl ->ijk', k_s,field_fourier)*2.0j*_np.pi if n == 3 else # vector, 3 -> 1 + _np.einsum('ijkm,ijklm->ijkl',k_s,field_fourier)*2.0j*_np.pi) # tensor, 3x3 -> 3 - return np.fft.irfftn(div_,axes=(0,1,2),s=field.shape[:3]) + return _np.fft.irfftn(div_,axes=(0,1,2),s=field.shape[:3]) def gradient(size,field): @@ -77,17 +77,17 @@ def gradient(size,field): physical size of the periodic field. """ - n = np.prod(field.shape[3:]) + n = _np.prod(field.shape[3:]) k_s = _ks(size,field.shape[:3],True) - field_fourier = np.fft.rfftn(field,axes=(0,1,2)) - grad_ = (np.einsum('ijkl,ijkm->ijkm', field_fourier,k_s)*2.0j*np.pi if n == 1 else # scalar, 1 -> 3 - np.einsum('ijkl,ijkm->ijklm',field_fourier,k_s)*2.0j*np.pi) # vector, 3 -> 3x3 + field_fourier = _np.fft.rfftn(field,axes=(0,1,2)) + grad_ = (_np.einsum('ijkl,ijkm->ijkm', field_fourier,k_s)*2.0j*_np.pi if n == 1 else # scalar, 1 -> 3 + _np.einsum('ijkl,ijkm->ijklm',field_fourier,k_s)*2.0j*_np.pi) # vector, 3 -> 3x3 - return np.fft.irfftn(grad_,axes=(0,1,2),s=field.shape[:3]) + return _np.fft.irfftn(grad_,axes=(0,1,2),s=field.shape[:3]) -def cell_coord0(grid,size,origin=np.zeros(3)): +def cell_coord0(grid,size,origin=_np.zeros(3)): """ Cell center positions (undeformed). @@ -103,7 +103,7 @@ def cell_coord0(grid,size,origin=np.zeros(3)): """ start = origin + size/grid*.5 end = origin + size - size/grid*.5 - return np.mgrid[start[0]:end[0]:grid[0]*1j,start[1]:end[1]:grid[1]*1j,start[2]:end[2]:grid[2]*1j].T + return _np.mgrid[start[0]:end[0]:grid[0]*1j,start[1]:end[1]:grid[1]*1j,start[2]:end[2]:grid[2]*1j].T def cell_displacement_fluct(size,F): @@ -118,19 +118,19 @@ def cell_displacement_fluct(size,F): deformation gradient field. """ - integrator = 0.5j*size/np.pi + integrator = 0.5j*size/_np.pi k_s = _ks(size,F.shape[:3],False) - k_s_squared = np.einsum('...l,...l',k_s,k_s) + k_s_squared = _np.einsum('...l,...l',k_s,k_s) k_s_squared[0,0,0] = 1.0 - displacement = -np.einsum('ijkml,ijkl,l->ijkm', - np.fft.rfftn(F,axes=(0,1,2)), + displacement = -_np.einsum('ijkml,ijkl,l->ijkm', + _np.fft.rfftn(F,axes=(0,1,2)), k_s, integrator, - ) / k_s_squared[...,np.newaxis] + ) / k_s_squared[...,_np.newaxis] - return np.fft.irfftn(displacement,axes=(0,1,2),s=F.shape[:3]) + return _np.fft.irfftn(displacement,axes=(0,1,2),s=F.shape[:3]) def cell_displacement_avg(size,F): @@ -145,8 +145,8 @@ def cell_displacement_avg(size,F): deformation gradient field. """ - F_avg = np.average(F,axis=(0,1,2)) - return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),cell_coord0(F.shape[:3][::-1],size)) + F_avg = _np.average(F,axis=(0,1,2)) + return _np.einsum('ml,ijkl->ijkm',F_avg-_np.eye(3),cell_coord0(F.shape[:3][::-1],size)) def cell_displacement(size,F): @@ -164,7 +164,7 @@ def cell_displacement(size,F): return cell_displacement_avg(size,F) + cell_displacement_fluct(size,F) -def cell_coord(size,F,origin=np.zeros(3)): +def cell_coord(size,F,origin=_np.zeros(3)): """ Cell center positions. @@ -193,17 +193,17 @@ def cell_coord0_gridSizeOrigin(coord0,ordered=True): expect coord0 data to be ordered (x fast, z slow). """ - coords = [np.unique(coord0[:,i]) for i in range(3)] - mincorner = np.array(list(map(min,coords))) - maxcorner = np.array(list(map(max,coords))) - grid = np.array(list(map(len,coords)),'i') - size = grid/np.maximum(grid-1,1) * (maxcorner-mincorner) + coords = [_np.unique(coord0[:,i]) for i in range(3)] + mincorner = _np.array(list(map(min,coords))) + maxcorner = _np.array(list(map(max,coords))) + grid = _np.array(list(map(len,coords)),'i') + size = grid/_np.maximum(grid-1,1) * (maxcorner-mincorner) delta = size/grid origin = mincorner - delta*.5 # 1D/2D: size/origin combination undefined, set origin to 0.0 - size [np.where(grid==1)] = origin[np.where(grid==1)]*2. - origin[np.where(grid==1)] = 0.0 + size [_np.where(grid==1)] = origin[_np.where(grid==1)]*2. + origin[_np.where(grid==1)] = 0.0 if grid.prod() != len(coord0): raise ValueError('Data count {} does not match grid {}.'.format(len(coord0),grid)) @@ -211,13 +211,13 @@ def cell_coord0_gridSizeOrigin(coord0,ordered=True): start = origin + delta*.5 end = origin - delta*.5 + size - if not np.allclose(coords[0],np.linspace(start[0],end[0],grid[0])) and \ - np.allclose(coords[1],np.linspace(start[1],end[1],grid[1])) and \ - np.allclose(coords[2],np.linspace(start[2],end[2],grid[2])): + if not _np.allclose(coords[0],_np.linspace(start[0],end[0],grid[0])) and \ + _np.allclose(coords[1],_np.linspace(start[1],end[1],grid[1])) and \ + _np.allclose(coords[2],_np.linspace(start[2],end[2],grid[2])): raise ValueError('Regular grid spacing violated.') - if ordered and not np.allclose(coord0.reshape(tuple(grid[::-1])+(3,)),cell_coord0(grid,size,origin)): - raise ValueError('Input data is not a regular grid.') + if ordered and not _np.allclose(coord0.reshape(tuple(grid[::-1])+(3,)),cell_coord0(grid,size,origin)): + raise ValueError('I_nput data is not a regular grid.') return (grid,size,origin) @@ -235,7 +235,7 @@ def coord0_check(coord0): cell_coord0_gridSizeOrigin(coord0,ordered=True) -def node_coord0(grid,size,origin=np.zeros(3)): +def node_coord0(grid,size,origin=_np.zeros(3)): """ Nodal positions (undeformed). @@ -249,7 +249,7 @@ def node_coord0(grid,size,origin=np.zeros(3)): physical origin of the periodic field. Defaults to [0.0,0.0,0.0]. """ - return np.mgrid[origin[0]:size[0]+origin[0]:(grid[0]+1)*1j, + return _np.mgrid[origin[0]:size[0]+origin[0]:(grid[0]+1)*1j, origin[1]:size[1]+origin[1]:(grid[1]+1)*1j, origin[2]:size[2]+origin[2]:(grid[2]+1)*1j].T @@ -281,8 +281,8 @@ def node_displacement_avg(size,F): deformation gradient field. """ - F_avg = np.average(F,axis=(0,1,2)) - return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),node_coord0(F.shape[:3][::-1],size)) + F_avg = _np.average(F,axis=(0,1,2)) + return _np.einsum('ml,ijkl->ijkm',F_avg-_np.eye(3),node_coord0(F.shape[:3][::-1],size)) def node_displacement(size,F): @@ -300,7 +300,7 @@ def node_displacement(size,F): return node_displacement_avg(size,F) + node_displacement_fluct(size,F) -def node_coord(size,F,origin=np.zeros(3)): +def node_coord(size,F,origin=_np.zeros(3)): """ Nodal positions. @@ -319,18 +319,18 @@ def node_coord(size,F,origin=np.zeros(3)): def cell_2_node(cell_data): """Interpolate periodic cell data to nodal data.""" - n = ( cell_data + np.roll(cell_data,1,(0,1,2)) - + np.roll(cell_data,1,(0,)) + np.roll(cell_data,1,(1,)) + np.roll(cell_data,1,(2,)) - + np.roll(cell_data,1,(0,1)) + np.roll(cell_data,1,(1,2)) + np.roll(cell_data,1,(2,0)))*0.125 + n = ( cell_data + _np.roll(cell_data,1,(0,1,2)) + + _np.roll(cell_data,1,(0,)) + _np.roll(cell_data,1,(1,)) + _np.roll(cell_data,1,(2,)) + + _np.roll(cell_data,1,(0,1)) + _np.roll(cell_data,1,(1,2)) + _np.roll(cell_data,1,(2,0)))*0.125 - return np.pad(n,((0,1),(0,1),(0,1))+((0,0),)*len(cell_data.shape[3:]),mode='wrap') + return _np.pad(n,((0,1),(0,1),(0,1))+((0,0),)*len(cell_data.shape[3:]),mode='wrap') def node_2_cell(node_data): """Interpolate periodic nodal data to cell data.""" - c = ( node_data + np.roll(node_data,1,(0,1,2)) - + np.roll(node_data,1,(0,)) + np.roll(node_data,1,(1,)) + np.roll(node_data,1,(2,)) - + np.roll(node_data,1,(0,1)) + np.roll(node_data,1,(1,2)) + np.roll(node_data,1,(2,0)))*0.125 + c = ( node_data + _np.roll(node_data,1,(0,1,2)) + + _np.roll(node_data,1,(0,)) + _np.roll(node_data,1,(1,)) + _np.roll(node_data,1,(2,)) + + _np.roll(node_data,1,(0,1)) + _np.roll(node_data,1,(1,2)) + _np.roll(node_data,1,(2,0)))*0.125 return c[:-1,:-1,:-1] @@ -347,23 +347,23 @@ def node_coord0_gridSizeOrigin(coord0,ordered=False): expect coord0 data to be ordered (x fast, z slow). """ - coords = [np.unique(coord0[:,i]) for i in range(3)] - mincorner = np.array(list(map(min,coords))) - maxcorner = np.array(list(map(max,coords))) - grid = np.array(list(map(len,coords)),'i') - 1 + coords = [_np.unique(coord0[:,i]) for i in range(3)] + mincorner = _np.array(list(map(min,coords))) + maxcorner = _np.array(list(map(max,coords))) + grid = _np.array(list(map(len,coords)),'i') - 1 size = maxcorner-mincorner origin = mincorner if (grid+1).prod() != len(coord0): raise ValueError('Data count {} does not match grid {}.'.format(len(coord0),grid)) - if not np.allclose(coords[0],np.linspace(mincorner[0],maxcorner[0],grid[0]+1)) and \ - np.allclose(coords[1],np.linspace(mincorner[1],maxcorner[1],grid[1]+1)) and \ - np.allclose(coords[2],np.linspace(mincorner[2],maxcorner[2],grid[2]+1)): + if not _np.allclose(coords[0],_np.linspace(mincorner[0],maxcorner[0],grid[0]+1)) and \ + _np.allclose(coords[1],_np.linspace(mincorner[1],maxcorner[1],grid[1]+1)) and \ + _np.allclose(coords[2],_np.linspace(mincorner[2],maxcorner[2],grid[2]+1)): raise ValueError('Regular grid spacing violated.') - if ordered and not np.allclose(coord0.reshape(tuple((grid+1)[::-1])+(3,)),node_coord0(grid,size,origin)): - raise ValueError('Input data is not a regular grid.') + if ordered and not _np.allclose(coord0.reshape(tuple((grid+1)[::-1])+(3,)),node_coord0(grid,size,origin)): + raise ValueError('I_nput data is not a regular grid.') return (grid,size,origin) @@ -386,10 +386,10 @@ def regrid(size,F,new_grid): + cell_displacement_avg(size,F) \ + cell_displacement_fluct(size,F) - outer = np.dot(np.average(F,axis=(0,1,2)),size) + outer = _np.dot(_np.average(F,axis=(0,1,2)),size) for d in range(3): - c[np.where(c[:,:,:,d]<0)] += outer[d] - c[np.where(c[:,:,:,d]>outer[d])] -= outer[d] + c[_np.where(c[:,:,:,d]<0)] += outer[d] + c[_np.where(c[:,:,:,d]>outer[d])] -= outer[d] - tree = spatial.cKDTree(c.reshape(-1,3),boxsize=outer) + tree = _spatial.cKDTree(c.reshape(-1,3),boxsize=outer) return tree.query(cell_coord0(new_grid,outer))[1].flatten() diff --git a/python/damask/mechanics.py b/python/damask/mechanics.py index 674ff9c5a..e19f140fb 100644 --- a/python/damask/mechanics.py +++ b/python/damask/mechanics.py @@ -1,4 +1,4 @@ -import numpy as np +import numpy as _np def Cauchy(P,F): """ @@ -14,10 +14,10 @@ def Cauchy(P,F): First Piola-Kirchhoff stress. """ - if np.shape(F) == np.shape(P) == (3,3): - sigma = 1.0/np.linalg.det(F) * np.dot(P,F.T) + if _np.shape(F) == _np.shape(P) == (3,3): + sigma = 1.0/_np.linalg.det(F) * _np.dot(P,F.T) else: - sigma = np.einsum('i,ijk,ilk->ijl',1.0/np.linalg.det(F),P,F) + sigma = _np.einsum('i,ijk,ilk->ijl',1.0/_np.linalg.det(F),P,F) return symmetric(sigma) @@ -31,8 +31,8 @@ def deviatoric_part(T): Tensor of which the deviatoric part is computed. """ - return T - np.eye(3)*spherical_part(T) if np.shape(T) == (3,3) else \ - T - np.einsum('ijk,i->ijk',np.broadcast_to(np.eye(3),[T.shape[0],3,3]),spherical_part(T)) + return T - _np.eye(3)*spherical_part(T) if _np.shape(T) == (3,3) else \ + T - _np.einsum('ijk,i->ijk',_np.broadcast_to(_np.eye(3),[T.shape[0],3,3]),spherical_part(T)) def eigenvalues(T_sym): @@ -48,7 +48,7 @@ def eigenvalues(T_sym): Symmetric tensor of which the eigenvalues are computed. """ - return np.linalg.eigvalsh(symmetric(T_sym)) + return _np.linalg.eigvalsh(symmetric(T_sym)) def eigenvectors(T_sym,RHS=False): @@ -65,13 +65,13 @@ def eigenvectors(T_sym,RHS=False): Enforce right-handed coordinate system. Default is False. """ - (u,v) = np.linalg.eigh(symmetric(T_sym)) + (u,v) = _np.linalg.eigh(symmetric(T_sym)) if RHS: - if np.shape(T_sym) == (3,3): - if np.linalg.det(v) < 0.0: v[:,2] *= -1.0 + if _np.shape(T_sym) == (3,3): + if _np.linalg.det(v) < 0.0: v[:,2] *= -1.0 else: - v[np.linalg.det(v) < 0.0,:,2] *= -1.0 + v[_np.linalg.det(v) < 0.0,:,2] *= -1.0 return v @@ -99,7 +99,7 @@ def maximum_shear(T_sym): """ w = eigenvalues(T_sym) - return (w[0] - w[2])*0.5 if np.shape(T_sym) == (3,3) else \ + return (w[0] - w[2])*0.5 if _np.shape(T_sym) == (3,3) else \ (w[:,0] - w[:,2])*0.5 @@ -141,10 +141,10 @@ def PK2(P,F): Deformation gradient. """ - if np.shape(F) == np.shape(P) == (3,3): - S = np.dot(np.linalg.inv(F),P) + if _np.shape(F) == _np.shape(P) == (3,3): + S = _np.dot(_np.linalg.inv(F),P) else: - S = np.einsum('ijk,ikl->ijl',np.linalg.inv(F),P) + S = _np.einsum('ijk,ikl->ijl',_np.linalg.inv(F),P) return symmetric(S) @@ -187,14 +187,14 @@ def spherical_part(T,tensor=False): """ if T.shape == (3,3): - sph = np.trace(T)/3.0 - return sph if not tensor else np.eye(3)*sph + sph = _np.trace(T)/3.0 + return sph if not tensor else _np.eye(3)*sph else: - sph = np.trace(T,axis1=1,axis2=2)/3.0 + sph = _np.trace(T,axis1=1,axis2=2)/3.0 if not tensor: return sph else: - return np.einsum('ijk,i->ijk',np.broadcast_to(np.eye(3),(T.shape[0],3,3)),sph) + return _np.einsum('ijk,i->ijk',_np.broadcast_to(_np.eye(3),(T.shape[0],3,3)),sph) def strain_tensor(F,t,m): @@ -216,22 +216,22 @@ def strain_tensor(F,t,m): """ F_ = F.reshape(1,3,3) if F.shape == (3,3) else F if t == 'V': - B = np.matmul(F_,transpose(F_)) - w,n = np.linalg.eigh(B) + B = _np.matmul(F_,transpose(F_)) + w,n = _np.linalg.eigh(B) elif t == 'U': - C = np.matmul(transpose(F_),F_) - w,n = np.linalg.eigh(C) + C = _np.matmul(transpose(F_),F_) + w,n = _np.linalg.eigh(C) if m > 0.0: - eps = 1.0/(2.0*abs(m)) * (+ np.matmul(n,np.einsum('ij,ikj->ijk',w**m,n)) - - np.broadcast_to(np.eye(3),[F_.shape[0],3,3])) + eps = 1.0/(2.0*abs(m)) * (+ _np.matmul(n,_np.einsum('ij,ikj->ijk',w**m,n)) + - _np.broadcast_to(_np.eye(3),[F_.shape[0],3,3])) elif m < 0.0: - eps = 1.0/(2.0*abs(m)) * (- np.matmul(n,np.einsum('ij,ikj->ijk',w**m,n)) - + np.broadcast_to(np.eye(3),[F_.shape[0],3,3])) + eps = 1.0/(2.0*abs(m)) * (- _np.matmul(n,_np.einsum('ij,ikj->ijk',w**m,n)) + + _np.broadcast_to(_np.eye(3),[F_.shape[0],3,3])) else: - eps = np.matmul(n,np.einsum('ij,ikj->ijk',0.5*np.log(w),n)) + eps = _np.matmul(n,_np.einsum('ij,ikj->ijk',0.5*_np.log(w),n)) - return eps.reshape(3,3) if np.shape(F) == (3,3) else \ + return eps.reshape(3,3) if _np.shape(F) == (3,3) else \ eps @@ -258,8 +258,8 @@ def transpose(T): Tensor of which the transpose is computed. """ - return T.T if np.shape(T) == (3,3) else \ - np.transpose(T,(0,2,1)) + return T.T if _np.shape(T) == (3,3) else \ + _np.transpose(T,(0,2,1)) def _polar_decomposition(T,requested): @@ -275,17 +275,17 @@ def _polar_decomposition(T,requested): ‘V’ for left stretch tensor and ‘U’ for right stretch tensor. """ - u, s, vh = np.linalg.svd(T) - R = np.dot(u,vh) if np.shape(T) == (3,3) else \ - np.einsum('ijk,ikl->ijl',u,vh) + u, s, vh = _np.linalg.svd(T) + R = _np.dot(u,vh) if _np.shape(T) == (3,3) else \ + _np.einsum('ijk,ikl->ijl',u,vh) output = [] if 'R' in requested: output.append(R) if 'V' in requested: - output.append(np.dot(T,R.T) if np.shape(T) == (3,3) else np.einsum('ijk,ilk->ijl',T,R)) + output.append(_np.dot(T,R.T) if _np.shape(T) == (3,3) else _np.einsum('ijk,ilk->ijl',T,R)) if 'U' in requested: - output.append(np.dot(R.T,T) if np.shape(T) == (3,3) else np.einsum('ikj,ikl->ijl',R,T)) + output.append(_np.dot(R.T,T) if _np.shape(T) == (3,3) else _np.einsum('ikj,ikl->ijl',R,T)) return tuple(output) @@ -303,5 +303,5 @@ def _Mises(T_sym,s): """ d = deviatoric_part(T_sym) - return np.sqrt(s*(np.sum(d**2.0))) if np.shape(T_sym) == (3,3) else \ - np.sqrt(s*np.einsum('ijk->i',d**2.0)) + return _np.sqrt(s*(_np.sum(d**2.0))) if _np.shape(T_sym) == (3,3) else \ + _np.sqrt(s*_np.einsum('ijk->i',d**2.0)) From 656c0199cf0ca8dd604b6d3177f53af933c5b23b Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 10 Apr 2020 12:32:33 +0200 Subject: [PATCH 21/53] sorted and make explicitly available what we need --- python/damask/__init__.py | 2 +- python/damask/util.py | 153 +++++++++++++++++++++----------------- 2 files changed, 87 insertions(+), 68 deletions(-) diff --git a/python/damask/__init__.py b/python/damask/__init__.py index 87c557721..9a01e8e62 100644 --- a/python/damask/__init__.py +++ b/python/damask/__init__.py @@ -6,7 +6,7 @@ name = 'damask' with open(_os.path.join(_os.path.dirname(__file__),'VERSION')) as _f: version = _re.sub(r'^v','',_f.readline().strip()) -# classes +# make classes directly accessible as damask.Class from ._environment import Environment # noqa from ._table import Table # noqa from ._vtk import VTK # noqa diff --git a/python/damask/util.py b/python/damask/util.py index 273da8c1e..d45ea366e 100644 --- a/python/damask/util.py +++ b/python/damask/util.py @@ -9,37 +9,22 @@ from optparse import Option import numpy as np -class bcolors: - """ - ASCII Colors. - - https://svn.blender.org/svnroot/bf-blender/trunk/blender/build_files/scons/tools/bcolors.py - https://stackoverflow.com/questions/287871 - """ - - HEADER = '\033[95m' - OKBLUE = '\033[94m' - OKGREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - BOLD = '\033[1m' - DIM = '\033[2m' - UNDERLINE = '\033[4m' - CROSSOUT = '\033[9m' - - def disable(self): - self.HEADER = '' - self.OKBLUE = '' - self.OKGREEN = '' - self.WARNING = '' - self.FAIL = '' - self.ENDC = '' - self.BOLD = '' - self.UNDERLINE = '' - self.CROSSOUT = '' - +# limit visibility +__all__=[ + 'srepr', + 'croak', + 'report', + 'emph','deemph','delete','strikeout', + 'execute', + 'show_progress', + 'scale_to_coprime', + 'return_message', + 'extendableOption', + ] +#################################################################################################### +# Functions +#################################################################################################### def srepr(arg,glue = '\n'): r""" Join arguments as individual lines. @@ -144,6 +129,52 @@ def execute(cmd, return out,error +def show_progress(iterable,N_iter=None,prefix='',bar_length=50): + """ + Decorate a loop with a status bar. + + Use similar like enumerate. + + Parameters + ---------- + iterable : iterable/function with yield statement + Iterable (or function with yield statement) to be decorated. + N_iter : int + Total # of iterations. Needed if number of iterations can not be obtained as len(iterable). + prefix : str, optional. + Prefix string. + bar_length : int, optional + Character length of bar. Defaults to 50. + + """ + status = _ProgressBar(N_iter if N_iter else len(iterable),prefix,bar_length) + + for i,item in enumerate(iterable): + yield item + status.update(i) + + +def scale_to_coprime(v): + """Scale vector to co-prime (relatively prime) integers.""" + MAX_DENOMINATOR = 1000 + + def get_square_denominator(x): + """Denominator of the square of a number.""" + return fractions.Fraction(x ** 2).limit_denominator(MAX_DENOMINATOR).denominator + + def lcm(a, b): + """Least common multiple.""" + return a * b // np.gcd(a, b) + + denominators = [int(get_square_denominator(i)) for i in v] + s = reduce(lcm, denominators) ** 0.5 + m = (np.array(v)*s).astype(np.int) + return m//reduce(np.gcd,m) + + +#################################################################################################### +# Classes +#################################################################################################### class extendableOption(Option): """ Used for definition of new option parser action 'extend', which enables to take multiple option arguments. @@ -215,47 +246,36 @@ class _ProgressBar: sys.stderr.write('\n') sys.stderr.flush() -def show_progress(iterable,N_iter=None,prefix='',bar_length=50): + +class bcolors: """ - Decorate a loop with a status bar. - - Use similar like enumerate. - - Parameters - ---------- - iterable : iterable/function with yield statement - Iterable (or function with yield statement) to be decorated. - N_iter : int - Total # of iterations. Needed if number of iterations can not be obtained as len(iterable). - prefix : str, optional. - Prefix string. - bar_length : int, optional - Character length of bar. Defaults to 50. + ASCII Colors. + https://svn.blender.org/svnroot/bf-blender/trunk/blender/build_files/scons/tools/bcolors.py + https://stackoverflow.com/questions/287871 """ - status = _ProgressBar(N_iter if N_iter else len(iterable),prefix,bar_length) - for i,item in enumerate(iterable): - yield item - status.update(i) + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + DIM = '\033[2m' + UNDERLINE = '\033[4m' + CROSSOUT = '\033[9m' - -def scale_to_coprime(v): - """Scale vector to co-prime (relatively prime) integers.""" - MAX_DENOMINATOR = 1000 - - def get_square_denominator(x): - """Denominator of the square of a number.""" - return fractions.Fraction(x ** 2).limit_denominator(MAX_DENOMINATOR).denominator - - def lcm(a, b): - """Least common multiple.""" - return a * b // np.gcd(a, b) - - denominators = [int(get_square_denominator(i)) for i in v] - s = reduce(lcm, denominators) ** 0.5 - m = (np.array(v)*s).astype(np.int) - return m//reduce(np.gcd,m) + def disable(self): + self.HEADER = '' + self.OKBLUE = '' + self.OKGREEN = '' + self.WARNING = '' + self.FAIL = '' + self.ENDC = '' + self.BOLD = '' + self.UNDERLINE = '' + self.CROSSOUT = '' class return_message: @@ -276,4 +296,3 @@ class return_message: def __repr__(self): """Return message suitable for interactive shells.""" return srepr(self.message) - From a2e70612ff0cfacc04ad33c38d93ea3b515befcb Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 10 Apr 2020 12:52:27 +0200 Subject: [PATCH 22/53] interface checking for LAPACK --- src/LAPACK_interface.f90 | 59 ++++++++++++++++++++++++++++++++++ src/commercialFEM_fileList.f90 | 1 + src/crystallite.f90 | 2 -- src/math.f90 | 11 +------ src/rotations.f90 | 2 -- 5 files changed, 61 insertions(+), 14 deletions(-) create mode 100644 src/LAPACK_interface.f90 diff --git a/src/LAPACK_interface.f90 b/src/LAPACK_interface.f90 new file mode 100644 index 000000000..7d3043ed0 --- /dev/null +++ b/src/LAPACK_interface.f90 @@ -0,0 +1,59 @@ +!-------------------------------------------------------------------------------------------------- +!> @author Martin Diehl, Max-Planck-Institut für Eisenforschung GmbH +!> @brief Fortran interfaces for LAPACK routines +!> @details https://www.netlib.org/lapack/ +!-------------------------------------------------------------------------------------------------- +module LAPACK_interface + interface + + subroutine dgeev(jobvl,jobvr,n,a,lda,wr,wi,vl,ldvl,vr,ldvr,work,lwork,info) + use prec + character, intent(in) :: jobvl,jobvr + integer, intent(in) :: n,lda,ldvl,ldvr,lwork + real(pReal), intent(inout), dimension(lda,n) :: a + real(pReal), intent(out), dimension(n) :: wr,wi + real(pReal), intent(out), dimension(ldvl,n) :: vl + real(pReal), intent(out), dimension(ldvr,n) :: vr + real(pReal), intent(out), dimension(max(1,lwork)) :: work + integer, intent(out) :: info + end subroutine dgeev + + subroutine dgesv(n,nrhs,a,lda,ipiv,b,ldb,info) + use prec + integer, intent(in) :: n,nrhs,lda,ldb + real(pReal), intent(inout), dimension(lda,n) :: a + integer, intent(out), dimension(n) :: ipiv + real(pReal), intent(out), dimension(ldb,nrhs) :: b + integer, intent(out) :: info + end subroutine dgesv + + subroutine dgetrf(m,n,a,lda,ipiv,info) + use prec + integer, intent(in) :: m,n,lda + real(pReal), intent(inout), dimension(lda,n) :: a + integer, intent(out), dimension(min(m,n)) :: ipiv + integer, intent(out) :: info + end subroutine dgetrf + + subroutine dgetri(n,a,lda,ipiv,work,lwork,info) + use prec + integer, intent(in) :: n,lda,lwork + real(pReal), intent(inout), dimension(lda,n) :: a + integer, intent(out), dimension(n) :: ipiv + real(pReal), intent(out), dimension(max(1,lwork)) :: work + integer, intent(out) :: info + end subroutine dgetri + + subroutine dsyev(jobz,uplo,n,a,lda,w,work,lwork,info) + use prec + character, intent(in) :: jobz,uplo + integer, intent(in) :: n,lda,lwork + real(pReal), intent(inout), dimension(lda,n) :: a + real(pReal), intent(out), dimension(n) :: w + real(pReal), intent(out), dimension(max(1,lwork)) :: work + integer, intent(out) :: info + end subroutine dsyev + + end interface + +end module LAPACK_interface diff --git a/src/commercialFEM_fileList.f90 b/src/commercialFEM_fileList.f90 index ab26ee9d5..64ad3e1d7 100644 --- a/src/commercialFEM_fileList.f90 +++ b/src/commercialFEM_fileList.f90 @@ -9,6 +9,7 @@ #include "list.f90" #include "future.f90" #include "config.f90" +#include "LAPACK_interface.f90" #include "math.f90" #include "quaternions.f90" #include "Lambert.f90" diff --git a/src/crystallite.f90 b/src/crystallite.f90 index efa066696..9bc254e0c 100644 --- a/src/crystallite.f90 +++ b/src/crystallite.f90 @@ -835,8 +835,6 @@ logical function integrateStress(ipc,ip,el,timeFraction) jacoCounterLp, & jacoCounterLi ! counters to check for Jacobian update logical :: error - external :: & - dgesv integrateStress = .false. diff --git a/src/math.f90 b/src/math.f90 index b0852e8d4..3bd3f7112 100644 --- a/src/math.f90 +++ b/src/math.f90 @@ -9,6 +9,7 @@ module math use prec use IO use numerics + use LAPACK_interface implicit none public @@ -489,9 +490,6 @@ function math_invSym3333(A) real(pReal), dimension(6,6) :: temp66 real(pReal), dimension(6*(64+2)) :: work integer :: ierr_i, ierr_f - external :: & - dgetrf, & - dgetri temp66 = math_sym3333to66(A) call dgetrf(6,6,temp66,6,ipiv6,ierr_i) @@ -518,9 +516,6 @@ subroutine math_invert(InvA, error, A) integer, dimension(size(A,1)) :: ipiv real(pReal), dimension(size(A,1)*(64+2)) :: work integer :: ierr - external :: & - dgetrf, & - dgetri invA = A call dgetrf(size(A,1),size(A,1),invA,size(A,1),ipiv,ierr) @@ -885,8 +880,6 @@ subroutine math_eigh(m,w,v,error) logical, intent(out) :: error integer :: ierr real(pReal), dimension((64+2)*size(m,1)) :: work ! block size of 64 taken from http://www.netlib.org/lapack/double/dsyev.f - external :: & - dsyev v = m ! copy matrix to input (doubles as output) array call dsyev('V','U',size(m,1),v,size(m,1),w,work,size(work,1),ierr) @@ -1042,8 +1035,6 @@ function math_eigvalsh(m) real(pReal), dimension(size(m,1),size(m,1)) :: m_ integer :: ierr real(pReal), dimension((64+2)*size(m,1)) :: work ! block size of 64 taken from http://www.netlib.org/lapack/double/dsyev.f - external :: & - dsyev m_= m ! copy matrix to input (will be destroyed) call dsyev('N','U',size(m,1),m_,size(m,1),math_eigvalsh,work,size(work,1),ierr) diff --git a/src/rotations.f90 b/src/rotations.f90 index 7ce366f74..b4e143f28 100644 --- a/src/rotations.f90 +++ b/src/rotations.f90 @@ -596,8 +596,6 @@ function om2ax(om) result(ax) real(pReal), dimension(3,3) :: VR, devNull, om_ integer :: ierr, i - external :: dgeev - om_ = om ! first get the rotation angle From 59b0a6e825e00ca6d1fe59d18586b959bf4daa8f Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 10 Apr 2020 13:07:05 +0200 Subject: [PATCH 23/53] tuples not needed/avoid eval --- python/tests/test_grid_filters.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/python/tests/test_grid_filters.py b/python/tests/test_grid_filters.py index cdddca89e..acbdbf688 100644 --- a/python/tests/test_grid_filters.py +++ b/python/tests/test_grid_filters.py @@ -24,7 +24,7 @@ class TestGridFilters: n = grid_filters.node_coord0(grid,size) + size/grid*.5 assert np.allclose(c,n) - @pytest.mark.parametrize('mode',[('cell'),('node')]) + @pytest.mark.parametrize('mode',['cell','node']) def test_grid_DNA(self,mode): """Ensure that xx_coord0_gridSizeOrigin is the inverse of xx_coord0.""" grid = np.random.randint(8,32,(3)) @@ -49,7 +49,7 @@ class TestGridFilters: assert np.allclose(grid_filters.node_coord(size,F) [1:-1,1:-1,1:-1],grid_filters.cell_2_node( grid_filters.cell_coord(size,F))[1:-1,1:-1,1:-1]) - @pytest.mark.parametrize('mode',[('cell'),('node')]) + @pytest.mark.parametrize('mode',['cell','node']) def test_coord0_origin(self,mode): origin= np.random.random(3) size = np.random.random(3) # noqa @@ -61,22 +61,24 @@ class TestGridFilters: elif mode == 'node': assert np.allclose(shifted,unshifted+np.broadcast_to(origin,tuple(grid[::-1]+1)+(3,))) - @pytest.mark.parametrize('mode',[('cell'),('node')]) - def test_displacement_avg_vanishes(self,mode): + @pytest.mark.parametrize('function',[grid_filters.cell_displacement_avg, + grid_filters.node_displacement_avg]) + def test_displacement_avg_vanishes(self,function): """Ensure that random fluctuations in F do not result in average displacement.""" - size = np.random.random(3) # noqa + size = np.random.random(3) grid = np.random.randint(8,32,(3)) F = np.random.random(tuple(grid)+(3,3)) F += np.eye(3) - np.average(F,axis=(0,1,2)) - assert np.allclose(eval('grid_filters.{}_displacement_avg(size,F)'.format(mode)),0.0) + assert np.allclose(function(size,F),0.0) - @pytest.mark.parametrize('mode',[('cell'),('node')]) - def test_displacement_fluct_vanishes(self,mode): + @pytest.mark.parametrize('function',[grid_filters.cell_displacement_fluct, + grid_filters.node_displacement_fluct]) + def test_displacement_fluct_vanishes(self,function): """Ensure that constant F does not result in fluctuating displacement.""" - size = np.random.random(3) # noqa + size = np.random.random(3) grid = np.random.randint(8,32,(3)) - F = np.broadcast_to(np.random.random((3,3)), tuple(grid)+(3,3)) # noqa - assert np.allclose(eval('grid_filters.{}_displacement_fluct(size,F)'.format(mode)),0.0) + F = np.broadcast_to(np.random.random((3,3)), tuple(grid)+(3,3)) + assert np.allclose(function(size,F),0.0) def test_regrid(self): size = np.random.random(3) From a6d1e02b3248dd259036275967fd8c1237525e68 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 10 Apr 2020 23:54:38 +0200 Subject: [PATCH 24/53] LAPACK calls the unblocked versions for our small matrices so a work that holds exactly the data seems to be the best choice --- src/math.f90 | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/math.f90 b/src/math.f90 index 3bd3f7112..070751de8 100644 --- a/src/math.f90 +++ b/src/math.f90 @@ -486,15 +486,14 @@ function math_invSym3333(A) real(pReal),dimension(3,3,3,3),intent(in) :: A - integer, dimension(6) :: ipiv6 - real(pReal), dimension(6,6) :: temp66 - real(pReal), dimension(6*(64+2)) :: work - integer :: ierr_i, ierr_f + integer, dimension(6) :: ipiv6 + real(pReal), dimension(6,6) :: temp66 + real(pReal), dimension(6*6) :: work + integer :: ierr_i, ierr_f temp66 = math_sym3333to66(A) call dgetrf(6,6,temp66,6,ipiv6,ierr_i) call dgetri(6,temp66,6,ipiv6,work,size(work,1),ierr_f) - if (ierr_i /= 0 .or. ierr_f /= 0) then call IO_error(400, ext_msg = 'math_invSym3333') else @@ -513,9 +512,9 @@ subroutine math_invert(InvA, error, A) real(pReal), dimension(size(A,1),size(A,1)), intent(out) :: invA logical, intent(out) :: error - integer, dimension(size(A,1)) :: ipiv - real(pReal), dimension(size(A,1)*(64+2)) :: work - integer :: ierr + integer, dimension(size(A,1)) :: ipiv + real(pReal), dimension(size(A,1)**2) :: work + integer :: ierr invA = A call dgetrf(size(A,1),size(A,1),invA,size(A,1),ipiv,ierr) @@ -879,7 +878,7 @@ subroutine math_eigh(m,w,v,error) logical, intent(out) :: error integer :: ierr - real(pReal), dimension((64+2)*size(m,1)) :: work ! block size of 64 taken from http://www.netlib.org/lapack/double/dsyev.f + real(pReal), dimension(size(m,1)**2) :: work v = m ! copy matrix to input (doubles as output) array call dsyev('V','U',size(m,1),v,size(m,1),w,work,size(work,1),ierr) @@ -1034,7 +1033,7 @@ function math_eigvalsh(m) real(pReal), dimension(size(m,1),size(m,1)) :: m_ integer :: ierr - real(pReal), dimension((64+2)*size(m,1)) :: work ! block size of 64 taken from http://www.netlib.org/lapack/double/dsyev.f + real(pReal), dimension(size(m,1)**2) :: work m_= m ! copy matrix to input (will be destroyed) call dsyev('N','U',size(m,1),m_,size(m,1),math_eigvalsh,work,size(work,1),ierr) From 3bfa2d679cb0f95998ed9746d46fa21e8f2dc9ad Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Apr 2020 11:36:37 +0200 Subject: [PATCH 25/53] simpler/correct logic for eu2om --- python/damask/_rotation.py | 24 ++++++++++++------------ src/rotations.f90 | 11 +++++------ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 3336eb6c0..e58de982f 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -474,9 +474,9 @@ class Rotation: q03 = qu[0]**2+qu[3]**2 q12 = qu[1]**2+qu[2]**2 chi = np.sqrt(q03*q12) - if np.abs(chi)< 1.e-6: + if np.abs(q12) < 1.e-6: eu = np.array([np.arctan2(-P*2.0*qu[0]*qu[3],qu[0]**2-qu[3]**2), 0.0, 0.0]) - elif np.abs(q12)< 1.e-6: + elif np.abs(q03) < 1.e-6: eu = np.array([np.arctan2( 2.0*qu[1]*qu[2],qu[1]**2-qu[2]**2), np.pi, 0.0]) else: eu = np.array([np.arctan2((-P*qu[0]*qu[2]+qu[1]*qu[3])*chi, (-P*qu[0]*qu[1]-qu[2]*qu[3])*chi ), @@ -491,14 +491,14 @@ class Rotation: q12_s = qu[...,1:2]**2+qu[...,2:3]**2 chi = np.sqrt(q03_s*q12_s) - eu = np.where(np.abs(chi) < 1.0e-6, + eu = np.where(np.abs(q12_s) < 1.0e-6, np.block([np.arctan2(-P*2.0*qu[...,0:1]*qu[...,3:4],qu[...,0:1]**2-qu[...,3:4]**2), np.zeros(qu.shape[:-1]+(2,))]), np.block([np.arctan2((-P*q02+q13)*chi, (-P*q01-q23)*chi), np.arctan2( 2.0*chi, q03_s-q12_s ), np.arctan2(( P*q02+q13)*chi, (-P*q01+q23)*chi)]) ) - eu = np.where(np.logical_and(np.abs(q03_s) < 1.0e-6, np.abs(chi) > 1.0e-6), + eu = np.where(np.logical_and(np.abs(q03_s) < 1.0e-6, np.abs(q12_s) >= 1.0e-6), np.block([np.arctan2( 2.0*qu[...,1:2]*qu[...,2:3],qu[...,1:2]**2-qu[...,2:3]**2), np.ones( qu.shape[:-1]+(1,))*np.pi, np.zeros(qu.shape[:-1]+(1,))]), @@ -550,7 +550,7 @@ class Rotation: ro = np.where(np.abs(s) < 1.0e-12, [0.0,0.0,P,0.0], np.block([qu[...,1:2]/s,qu[...,2:3]/s,qu[...,3:4]/s, - np.tan(np.arccos(np.clip(qu[:,0:1],-1.0,1.0))) + np.tan(np.arccos(np.clip(qu[...,0:1],-1.0,1.0))) ]) ) ro = np.where(np.abs(qu[...,0:1]) < 1.0e-12, @@ -686,10 +686,10 @@ class Rotation: ee = 0.5*eu cPhi = np.cos(ee[1]) sPhi = np.sin(ee[1]) - qu = np.array([ cPhi*np.cos(ee[0]+ee[2]), - -P*sPhi*np.cos(ee[0]-ee[2]), - -P*sPhi*np.sin(ee[0]-ee[2]), - -P*cPhi*np.sin(ee[0]+ee[2]) ]) + qu = np.array([ cPhi*np.cos(ee[0]+ee[2]), + -P*sPhi*np.cos(ee[0]-ee[2]), + -P*sPhi*np.sin(ee[0]-ee[2]), + -P*cPhi*np.sin(ee[0]+ee[2]) ]) if qu[0] < 0.0: qu*=-1 else: ee = 0.5*eu @@ -776,9 +776,9 @@ class Rotation: ro[3] = np.tan(ro[3]*0.5) else: ax = Rotation.eu2ax(eu) - ro = np.block([ax[:,:3],np.tan(ax[:,3:4]*.5)]) - ro[ax[:,3]>=np.pi,3] = np.inf - ro[np.abs(ax[:,3])<1.e-16] = [ 0.0, 0.0, P, 0.0 ] + ro = np.block([ax[...,:3],np.tan(ax[...,3:4]*.5)]) + ro[ax[...,3]>=np.pi,3] = np.inf + ro[np.abs(ax[...,3])<1.e-16] = [ 0.0, 0.0, P, 0.0 ] return ro @staticmethod diff --git a/src/rotations.f90 b/src/rotations.f90 index 7ce366f74..1630326ad 100644 --- a/src/rotations.f90 +++ b/src/rotations.f90 @@ -432,18 +432,17 @@ pure function qu2eu(qu) result(eu) real(pReal), intent(in), dimension(4) :: qu real(pReal), dimension(3) :: eu - real(pReal) :: q12, q03, chi, chiInv + real(pReal) :: q12, q03, chi q03 = qu(1)**2+qu(4)**2 q12 = qu(2)**2+qu(3)**2 chi = sqrt(q03*q12) - degenerated: if (dEq0(chi)) then - eu = merge([atan2(-P*2.0_pReal*qu(1)*qu(4),qu(1)**2-qu(4)**2), 0.0_pReal, 0.0_pReal], & - [atan2( 2.0_pReal*qu(2)*qu(3),qu(2)**2-qu(3)**2), PI, 0.0_pReal], & - dEq0(q12)) + degenerated: if (dEq0(q12)) then + eu = [atan2(-P*2.0_pReal*qu(1)*qu(4),qu(1)**2-qu(4)**2), 0.0_pReal, 0.0_pReal] + elseif (dEq0(q03)) then + eu = [atan2( 2.0_pReal*qu(2)*qu(3),qu(2)**2-qu(3)**2), PI, 0.0_pReal] else degenerated - chiInv = 1.0_pReal/chi eu = [atan2((-P*qu(1)*qu(3)+qu(2)*qu(4))*chi, (-P*qu(1)*qu(2)-qu(3)*qu(4))*chi ), & atan2( 2.0_pReal*chi, q03-q12 ), & atan2(( P*qu(1)*qu(3)+qu(2)*qu(4))*chi, (-P*qu(1)*qu(2)+qu(3)*qu(4))*chi )] From 4e759d6c9869bea71229ee8a0ec50d0926024c1f Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Apr 2020 12:37:21 +0200 Subject: [PATCH 26/53] more tests for orientation conversion ensure that all parameters are within range and check if multidimensional arrays at least run --- python/damask/_Lambert.py | 2 +- python/tests/test_Rotation.py | 27 ++++++++++++++++----------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/python/damask/_Lambert.py b/python/damask/_Lambert.py index a62f1922a..59abe7f64 100644 --- a/python/damask/_Lambert.py +++ b/python/damask/_Lambert.py @@ -105,7 +105,7 @@ def ball_to_cube(ball): """ ball_ = ball/np.linalg.norm(ball)*R1 if np.isclose(np.linalg.norm(ball),R1,atol=1e-6) else ball rs = np.linalg.norm(ball_) - if rs > R1 and not np.isclose(rs,R1): + if rs > R1+1.e-9: raise ValueError('Coordinate outside of the sphere: {} {} {}.'.format(*ball)) if np.allclose(ball_,0.0,rtol=0.0,atol=1.0e-16): diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 02d561b66..b3d3d881d 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -98,7 +98,7 @@ class TestRotation: if np.isclose(rot.asQuaternion()[0],0.0,atol=atol): ok = ok or np.allclose(m*-1.,o,atol=atol) print(m,o,rot.asQuaternion()) - assert ok + assert ok and np.isclose(np.linalg.norm(o),1.0) def test_AxisAngle(self,default): for rot in default: @@ -111,7 +111,7 @@ class TestRotation: sum_phi = np.unwrap([m[0]+m[2],o[0]+o[2]]) ok = ok or np.isclose(sum_phi[0],sum_phi[1],atol=atol) print(m,o,rot.asQuaternion()) - assert ok + assert ok and (np.zeros(3)-1.e-9 <= o).all() and (o <= np.array([np.pi*2.,np.pi,np.pi*2.])+1.e-9).all() def test_Matrix(self,default): for rot in default: @@ -121,36 +121,41 @@ class TestRotation: if np.isclose(m[3],np.pi,atol=atol): ok = ok or np.allclose(m*np.array([-1.,-1.,-1.,1.]),o,atol=atol) print(m,o,rot.asQuaternion()) - assert ok + assert ok and np.isclose(np.linalg.norm(o[:3]),1.0) and o[3]<=np.pi++1.e-9 def test_Rodriques(self,default): for rot in default: m = rot.asMatrix() o = Rotation.fromRodrigues(rot.asRodrigues()).asMatrix() + ok = np.allclose(m,o,atol=atol) print(m,o) - assert np.allclose(m,o,atol=atol) + assert ok and np.isclose(np.linalg.det(o),1.0) def test_Homochoric(self,default): + cutoff = np.tan(np.pi*.5*(1.-1e-4)) for rot in default: m = rot.asRodrigues() o = Rotation.fromHomochoric(rot.asHomochoric()).asRodrigues() - ok = np.allclose(np.clip(m,None,1.e9),np.clip(o,None,1.e9),atol=atol) - print(m,o,rot.asQuaternion()) + ok = np.allclose(np.clip(m,None,cutoff),np.clip(o,None,cutoff),atol=atol) ok = ok or np.isclose(m[3],0.0,atol=atol) + print(m,o,rot.asQuaternion()) + assert ok and np.isclose(np.linalg.norm(o[:3]),1.0) def test_Cubochoric(self,default): for rot in default: m = rot.asHomochoric() o = Rotation.fromCubochoric(rot.asCubochoric()).asHomochoric() + ok = np.allclose(m,o,atol=atol) print(m,o,rot.asQuaternion()) - assert np.allclose(m,o,atol=atol) + assert ok and np.linalg.norm(o) < (3.*np.pi/4.)**(1./3.) + 1.e-9 def test_Quaternion(self,default): for rot in default: m = rot.asCubochoric() o = Rotation.fromQuaternion(rot.asQuaternion()).asCubochoric() + ok = np.allclose(m,o,atol=atol) print(m,o,rot.asQuaternion()) - assert np.allclose(m,o,atol=atol) + assert ok and o.max() < np.pi**(2./3.)*0.5+1.e-9 @pytest.mark.parametrize('conversion',[Rotation.qu2om, Rotation.qu2eu, @@ -159,7 +164,7 @@ class TestRotation: Rotation.qu2ho]) def test_quaternion_vectorization(self,default,conversion): qu = np.array([rot.asQuaternion() for rot in default]) - #dev_null = conversion(qu.reshape(qu.shape[0]//2,-1,4)) + dev_null = conversion(qu.reshape(qu.shape[0]//2,-1,4)) co = conversion(qu) for q,c in zip(qu,co): print(q,c) @@ -170,7 +175,7 @@ class TestRotation: ]) def test_matrix_vectorization(self,default,conversion): om = np.array([rot.asMatrix() for rot in default]) - #dev_null = conversion(om.reshape(om.shape[0]//2,-1,3,3)) + dev_null = conversion(om.reshape(om.shape[0]//2,-1,3,3)) co = conversion(om) for o,c in zip(om,co): print(o,c) @@ -183,7 +188,7 @@ class TestRotation: ]) def test_Euler_vectorization(self,default,conversion): eu = np.array([rot.asEulers() for rot in default]) - #dev_null = conversion(eu.reshape(eu.shape[0]//2,-1,3)) + dev_null = conversion(eu.reshape(eu.shape[0]//2,-1,3)) co = conversion(eu) for e,c in zip(eu,co): print(e,c) From 99655c9f61060547ff76cfde2121e324f5587cb9 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Apr 2020 13:57:05 +0200 Subject: [PATCH 27/53] more vectorized functions --- python/damask/_rotation.py | 45 ++++++++++++++++++++++------------- python/tests/test_Rotation.py | 32 ++++++++++++++++++++----- 2 files changed, 54 insertions(+), 23 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index e58de982f..56c5d0593 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -767,8 +767,8 @@ class Rotation: def eu2ro(eu): """Bunge-Euler angles to Rodrigues-Frank vector.""" if len(eu.shape) == 1: - ro = Rotation.eu2ax(eu) # convert to axis angle pair representation - if ro[3] >= np.pi: # Differs from original implementation. check convention 5 + ro = Rotation.eu2ax(eu) # convert to axis angle pair representation + if ro[3] >= np.pi: # Differs from original implementation. check convention 5 ro[3] = np.inf elif iszero(ro[3]): ro = np.array([ 0.0, 0.0, P, 0.0 ]) @@ -902,27 +902,38 @@ class Rotation: @staticmethod def ro2ax(ro): """Rodrigues-Frank vector to axis angle pair.""" - ta = ro[3] - - if iszero(ta): - ax = [ 0.0, 0.0, 1.0, 0.0 ] - elif not np.isfinite(ta): - ax = [ ro[0], ro[1], ro[2], np.pi ] + if len(ro.shape) == 1: + if np.abs(ro[3]) < 1.e-6: + ax = np.array([ 0.0, 0.0, 1.0, 0.0 ]) + elif not np.isfinite(ro[3]): + ax = np.array([ ro[0], ro[1], ro[2], np.pi ]) + else: + angle = 2.0*np.arctan(ro[3]) + ta = np.linalg.norm(ro[0:3]) + ax = np.array([ ro[0]*ta, ro[1]*ta, ro[2]*ta, angle ]) else: - angle = 2.0*np.arctan(ta) - ta = 1.0/np.linalg.norm(ro[0:3]) - ax = [ ro[0]/ta, ro[1]/ta, ro[2]/ta, angle ] - return np.array(ax) + with np.errstate(invalid='ignore',divide='ignore'): + ax = np.where(np.isfinite(ro[...,3:4]), + np.block([ro[...,0:3]*np.linalg.norm(ro[...,0:3],axis=-1,keepdims=True),2.*np.arctan(ro[...,3:4])]), + np.block([ro[...,0:3],np.broadcast_to(np.pi,ro[...,3:4].shape)])) + ax[np.abs(ro[...,3]) < 1.e-6] = np.array([ 0.0, 0.0, 1.0, 0.0 ]) + return ax @staticmethod def ro2ho(ro): """Rodrigues-Frank vector to homochoric vector.""" - if iszero(np.sum(ro[0:3]**2.0)): - ho = [ 0.0, 0.0, 0.0 ] + if len(ro.shape) == 1: + if np.sum(ro[0:3]**2.0) < 1.e-6: + ho = np.zeros(3) + else: + f = 2.0*np.arctan(ro[3]) -np.sin(2.0*np.arctan(ro[3])) if np.isfinite(ro[3]) else np.pi + ho = ro[0:3] * (0.75*f)**(1.0/3.0) else: - f = 2.0*np.arctan(ro[3]) -np.sin(2.0*np.arctan(ro[3])) if np.isfinite(ro[3]) else np.pi - ho = ro[0:3] * (0.75*f)**(1.0/3.0) - return np.array(ho) + f = np.where(np.isfinite(ro[...,3:4]),2.0*np.arctan(ro[...,3:4]) -np.sin(2.0*np.arctan(ro[...,3:4])),np.pi) + ho = np.where(np.broadcast_to(np.sum(ro[...,0:3]**2.0,axis=-1,keepdims=True) < 1.e-6,ro[...,0:3].shape), + np.zeros(3), ro[...,0:3]* (0.75*f)**(1.0/3.0)) + + return ho @staticmethod def ro2cu(ro): diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index b3d3d881d..544e088c7 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -123,7 +123,7 @@ class TestRotation: print(m,o,rot.asQuaternion()) assert ok and np.isclose(np.linalg.norm(o[:3]),1.0) and o[3]<=np.pi++1.e-9 - def test_Rodriques(self,default): + def test_Rodrigues(self,default): for rot in default: m = rot.asMatrix() o = Rotation.fromRodrigues(rot.asRodrigues()).asMatrix() @@ -164,18 +164,21 @@ class TestRotation: Rotation.qu2ho]) def test_quaternion_vectorization(self,default,conversion): qu = np.array([rot.asQuaternion() for rot in default]) - dev_null = conversion(qu.reshape(qu.shape[0]//2,-1,4)) + conversion(qu.reshape(qu.shape[0]//2,-1,4)) co = conversion(qu) for q,c in zip(qu,co): print(q,c) assert np.allclose(conversion(q),c) - @pytest.mark.parametrize('conversion',[Rotation.om2eu, + @pytest.mark.parametrize('conversion',[Rotation.om2qu, + Rotation.om2eu, Rotation.om2ax, + Rotation.om2ro, + Rotation.om2ho, ]) def test_matrix_vectorization(self,default,conversion): om = np.array([rot.asMatrix() for rot in default]) - dev_null = conversion(om.reshape(om.shape[0]//2,-1,3,3)) + conversion(om.reshape(om.shape[0]//2,-1,3,3)) co = conversion(om) for o,c in zip(om,co): print(o,c) @@ -185,10 +188,11 @@ class TestRotation: Rotation.eu2om, Rotation.eu2ax, Rotation.eu2ro, + Rotation.eu2ho, ]) def test_Euler_vectorization(self,default,conversion): eu = np.array([rot.asEulers() for rot in default]) - dev_null = conversion(eu.reshape(eu.shape[0]//2,-1,3)) + conversion(eu.reshape(eu.shape[0]//2,-1,3)) co = conversion(eu) for e,c in zip(eu,co): print(e,c) @@ -196,13 +200,29 @@ class TestRotation: @pytest.mark.parametrize('conversion',[Rotation.ax2qu, Rotation.ax2om, + Rotation.ax2eu, Rotation.ax2ro, Rotation.ax2ho, ]) def test_axisAngle_vectorization(self,default,conversion): ax = np.array([rot.asAxisAngle() for rot in default]) - dev_null = conversion(ax.reshape(ax.shape[0]//2,-1,4)) + conversion(ax.reshape(ax.shape[0]//2,-1,4)) co = conversion(ax) for a,c in zip(ax,co): print(a,c) assert np.allclose(conversion(a),c) + + + @pytest.mark.parametrize('conversion',[Rotation.ro2qu, + Rotation.ro2om, + Rotation.ro2eu, + Rotation.ro2ax, + Rotation.ro2ho, + ]) + def test_Rodrigues_vectorization(self,default,conversion): + ro = np.array([rot.asRodrigues() for rot in default]) + conversion(ro.reshape(ro.shape[0]//2,-1,4)) + co = conversion(ro) + for r,c in zip(ro,co): + print(r,c) + assert np.allclose(conversion(r),c) From cb9daccdd7eb21808b9940815f9d56b0af35835a Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Apr 2020 16:14:40 +0200 Subject: [PATCH 28/53] homochoric representation vectorized --- python/damask/_rotation.py | 28 ++++++++++++++++++++-------- python/tests/test_Rotation.py | 14 ++++++++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 56c5d0593..e63bd1205 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -968,19 +968,31 @@ class Rotation: +0.0001703481934140054, -0.00012062065004116828, +0.000059719705868660826, -0.00001980756723965647, +0.000003953714684212874, -0.00000036555001439719544]) - # normalize h and store the magnitude - hmag_squared = np.sum(ho**2.) - if iszero(hmag_squared): - ax = np.array([ 0.0, 0.0, 1.0, 0.0 ]) - else: - hm = hmag_squared + if len(ho.shape) == 1: + # normalize h and store the magnitude + hmag_squared = np.sum(ho**2.) + if iszero(hmag_squared): + ax = np.array([ 0.0, 0.0, 1.0, 0.0 ]) + else: + hm = hmag_squared - # convert the magnitude to the rotation angle + # convert the magnitude to the rotation angle + s = tfit[0] + tfit[1] * hmag_squared + for i in range(2,16): + hm *= hmag_squared + s += tfit[i] * hm + ax = np.append(ho/np.sqrt(hmag_squared),2.0*np.arccos(np.clip(s,-1.0,1.0))) + else: + hmag_squared = np.sum(ho**2.,axis=-1,keepdims=True) + hm = hmag_squared.copy() s = tfit[0] + tfit[1] * hmag_squared for i in range(2,16): hm *= hmag_squared s += tfit[i] * hm - ax = np.append(ho/np.sqrt(hmag_squared),2.0*np.arccos(np.clip(s,-1.0,1.0))) + with np.errstate(invalid='ignore',divide='ignore'): + ax = np.where(np.broadcast_to(np.abs(hmag_squared)<1.e-6,ho.shape[:-1]+(4,)), + np.array([ 0.0, 0.0, 1.0, 0.0 ]), + np.block([ho/np.sqrt(hmag_squared),2.0*np.arccos(np.clip(s,-1.0,1.0))])) return ax @staticmethod diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 544e088c7..2ac819f4c 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -226,3 +226,17 @@ class TestRotation: for r,c in zip(ro,co): print(r,c) assert np.allclose(conversion(r),c) + + @pytest.mark.parametrize('conversion',[Rotation.ho2qu, + Rotation.ho2om, + Rotation.ho2eu, + Rotation.ho2ax, + Rotation.ho2ro, + ]) + def test_homochoric_vectorization(self,default,conversion): + ho = np.array([rot.asHomochoric() for rot in default]) + conversion(ho.reshape(ho.shape[0]//2,-1,3)) + co = conversion(ho) + for h,c in zip(ho,co): + print(h,c) + assert np.allclose(conversion(h),c) From 6e6f512c385bb3158cbaea08b885b1c9dd236f8e Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Apr 2020 17:05:52 +0200 Subject: [PATCH 29/53] fixed test --- PRIVATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVATE b/PRIVATE index 377e4f97a..85e4cb8e5 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 377e4f97a31ca1aa39a0645430c82bed40158001 +Subproject commit 85e4cb8e5b1bd437a6649457457cfd67b64e5a51 From 51104bfc13650f1637a4a675038face3cdb659ea Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Apr 2020 17:19:12 +0200 Subject: [PATCH 30/53] do not transpose for the standard case --- python/damask/_rotation.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index e63bd1205..a20db5aa8 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -446,13 +446,13 @@ class Rotation: qq = qu[0]**2-(qu[1]**2 + qu[2]**2 + qu[3]**2) om = np.diag(qq + 2.0*np.array([qu[1],qu[2],qu[3]])**2) - om[1,0] = 2.0*(qu[2]*qu[1]+qu[0]*qu[3]) - om[0,1] = 2.0*(qu[1]*qu[2]-qu[0]*qu[3]) - om[2,1] = 2.0*(qu[3]*qu[2]+qu[0]*qu[1]) - om[1,2] = 2.0*(qu[2]*qu[3]-qu[0]*qu[1]) - om[0,2] = 2.0*(qu[1]*qu[3]+qu[0]*qu[2]) - om[2,0] = 2.0*(qu[3]*qu[1]-qu[0]*qu[2]) - return om if P > 0.0 else om.T + om[0,1] = 2.0*(qu[2]*qu[1]+qu[0]*qu[3]) + om[1,0] = 2.0*(qu[1]*qu[2]-qu[0]*qu[3]) + om[1,2] = 2.0*(qu[3]*qu[2]+qu[0]*qu[1]) + om[2,1] = 2.0*(qu[2]*qu[3]-qu[0]*qu[1]) + om[2,0] = 2.0*(qu[1]*qu[3]+qu[0]*qu[2]) + om[0,2] = 2.0*(qu[3]*qu[1]-qu[0]*qu[2]) + return om if P < 0.0 else om.T else: qq = qu[...,0:1]**2-(qu[...,1:2]**2 + qu[...,2:3]**2 + qu[...,3:4]**2) om = np.block([qq + 2.0*qu[...,1:2]**2, From fac33ec408b94ab9a193cbc9176a90f1dcdd6b63 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Apr 2020 17:23:54 +0200 Subject: [PATCH 31/53] polishing --- python/damask/_rotation.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index a20db5aa8..42b097a78 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -452,7 +452,6 @@ class Rotation: om[2,1] = 2.0*(qu[2]*qu[3]-qu[0]*qu[1]) om[2,0] = 2.0*(qu[1]*qu[3]+qu[0]*qu[2]) om[0,2] = 2.0*(qu[3]*qu[1]-qu[0]*qu[2]) - return om if P < 0.0 else om.T else: qq = qu[...,0:1]**2-(qu[...,1:2]**2 + qu[...,2:3]**2 + qu[...,3:4]**2) om = np.block([qq + 2.0*qu[...,1:2]**2, @@ -465,7 +464,7 @@ class Rotation: 2.0*(qu[...,2:3]*qu[...,3:4]-qu[...,0:1]*qu[...,1:2]), qq + 2.0*qu[...,3:4]**2, ]).reshape(qu.shape[:-1]+(3,3)) - return om # TODO: TRANSPOSE FOR P = 1 + return om if P < 0.0 else np.swapaxes(om,(-1,-2)) @staticmethod def qu2eu(qu): @@ -823,7 +822,6 @@ class Rotation: q = omc*ax[idx[0]] * ax[idx[1]] om[idx[0],idx[1]] = q + s*ax[idx[2]] om[idx[1],idx[0]] = q - s*ax[idx[2]] - return om if P < 0.0 else om.T else: c = np.cos(ax[...,3:4]) s = np.sin(ax[...,3:4]) @@ -837,7 +835,7 @@ class Rotation: omc*ax[...,0:1]*ax[...,2:3] + s*ax[...,1:2], omc*ax[...,1:2]*ax[...,2:3] - s*ax[...,0:1], c+omc*ax[...,2:3]**2]).reshape(ax.shape[:-1]+(3,3)) - return om # TODO: TRANSPOSE FOR P = 1 + return om if P < 0.0 else np.swapaxes(om,(-1,-2)) @staticmethod def ax2eu(ax): @@ -989,7 +987,7 @@ class Rotation: for i in range(2,16): hm *= hmag_squared s += tfit[i] * hm - with np.errstate(invalid='ignore',divide='ignore'): + with np.errstate(invalid='ignore'): ax = np.where(np.broadcast_to(np.abs(hmag_squared)<1.e-6,ho.shape[:-1]+(4,)), np.array([ 0.0, 0.0, 1.0, 0.0 ]), np.block([ho/np.sqrt(hmag_squared),2.0*np.arccos(np.clip(s,-1.0,1.0))])) From 296a75d452741386ff1c5710eadae60ddfcf130d Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Apr 2020 17:58:15 +0200 Subject: [PATCH 32/53] where not needed --- python/damask/_rotation.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 42b097a78..e728ab6ed 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -501,7 +501,7 @@ class Rotation: np.block([np.arctan2( 2.0*qu[...,1:2]*qu[...,2:3],qu[...,1:2]**2-qu[...,2:3]**2), np.ones( qu.shape[:-1]+(1,))*np.pi, np.zeros(qu.shape[:-1]+(1,))]), - eu) # TODO: Where not needed + eu) # TODO: Where can be nested # reduce Euler angles to definition range, i.e a lower limit of 0.0 eu[np.abs(eu)<1.e-6] = 0.0 eu = np.where(eu<0, (eu+2.0*np.pi)%np.array([2.0*np.pi,np.pi,2.0*np.pi]),eu) @@ -515,7 +515,7 @@ class Rotation: Modified version of the original formulation, should be numerically more stable """ if len(qu.shape) == 1: - if iszero(np.sum(qu[1:4]**2)): # set axis to [001] if the angle is 0/360 + if np.abs(np.sum(qu[1:4]**2)) < 1.e-6: # set axis to [001] if the angle is 0/360 ax = np.array([ 0.0, 0.0, 1.0, 0.0 ]) elif np.abs(qu[0]) > 1.e-6: s = np.sign(qu[0])/np.sqrt(qu[1]**2+qu[2]**2+qu[3]**2) @@ -546,14 +546,13 @@ class Rotation: else: with np.errstate(invalid='ignore',divide='ignore'): s = np.linalg.norm(qu[...,1:4],axis=-1,keepdims=True) - ro = np.where(np.abs(s) < 1.0e-12, - [0.0,0.0,P,0.0], + ro = np.where(np.broadcast_to(np.abs(qu[...,0:1]) < 1.0e-12,qu.shape), + np.block([qu[...,1:2], qu[...,2:3], qu[...,3:4], np.ones(qu.shape[:-1]+(1,))*np.inf]), np.block([qu[...,1:2]/s,qu[...,2:3]/s,qu[...,3:4]/s, np.tan(np.arccos(np.clip(qu[...,0:1],-1.0,1.0))) ]) ) - ro = np.where(np.abs(qu[...,0:1]) < 1.0e-12, - np.block([qu[...,1:2], qu[...,2:3], qu[...,3:4], np.ones(qu.shape[:-1]+(1,))*np.inf]),ro) # TODO: Where not needed + ro[np.abs(s).squeeze(-1) < 1.0e-12] = [0.0,0.0,P,0.0] return ro @staticmethod @@ -1033,4 +1032,7 @@ class Rotation: @staticmethod def cu2ho(cu): """Cubochoric vector to homochoric vector.""" - return cube_to_ball(cu) + if len(cu.shape) == 1: + return cube_to_ball(cu) + else: + raise NotImplementedError From 8c61f67e3453249f02b40ef82ea8ad97781cc395 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Apr 2020 18:14:15 +0200 Subject: [PATCH 33/53] cleaning --- python/damask/_rotation.py | 10 +++++----- python/tests/test_Rotation.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index e728ab6ed..4c164376c 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -517,7 +517,7 @@ class Rotation: if len(qu.shape) == 1: if np.abs(np.sum(qu[1:4]**2)) < 1.e-6: # set axis to [001] if the angle is 0/360 ax = np.array([ 0.0, 0.0, 1.0, 0.0 ]) - elif np.abs(qu[0]) > 1.e-6: + elif qu[0] > 1.e-6: s = np.sign(qu[0])/np.sqrt(qu[1]**2+qu[2]**2+qu[3]**2) omega = 2.0 * np.arccos(np.clip(qu[0],-1.0,1.0)) ax = ax = np.array([ qu[1]*s, qu[2]*s, qu[3]*s, omega ]) @@ -527,10 +527,10 @@ class Rotation: with np.errstate(invalid='ignore',divide='ignore'): s = np.sign(qu[...,0:1])/np.sqrt(qu[...,1:2]**2+qu[...,2:3]**2+qu[...,3:4]**2) omega = 2.0 * np.arccos(np.clip(qu[...,0:1],-1.0,1.0)) - ax = np.where(np.sum(np.abs(qu[:,1:4])**2,axis=-1,keepdims=True) < 1.0e-6, - [0.0, 0.0, 1.0, 0.0], np.block([qu[...,1:4]*s,omega])) - ax = np.where(qu[...,0:1] < 1.0e-6, - np.block([qu[...,1:4],np.ones(qu.shape[:-1]+(1,))*np.pi]),ax) # TODO: Where not needed + ax = np.where(np.broadcast_to(qu[...,0:1] < 1.0e-6,qu.shape), + np.block([qu[...,1:4],np.ones(qu.shape[:-1]+(1,))*np.pi]), + np.block([qu[...,1:4]*s,omega])) + ax[np.sum(np.abs(qu[...,1:4])**2,axis=-1) < 1.0e-6,] = [0.0, 0.0, 1.0, 0.0] return ax @staticmethod diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 2ac819f4c..0007a2aec 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -167,7 +167,7 @@ class TestRotation: conversion(qu.reshape(qu.shape[0]//2,-1,4)) co = conversion(qu) for q,c in zip(qu,co): - print(q,c) + #print(q,c) assert np.allclose(conversion(q),c) @pytest.mark.parametrize('conversion',[Rotation.om2qu, From c57f96cd6eb3e369f5ce8bd4a8b3fab2ca2d51dd Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Apr 2020 18:32:06 +0200 Subject: [PATCH 34/53] also missing --- python/damask/_rotation.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 4c164376c..b2a08d77b 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -1000,7 +1000,10 @@ class Rotation: @staticmethod def ho2cu(ho): """Homochoric vector to cubochoric vector.""" - return ball_to_cube(ho) + if len(ho.shape) == 1: + return ball_to_cube(ho) + else: + raise NotImplementedError #---------- Cubochoric ---------- From c0c37fe6a5c95cf823011b0a3adb54c389c83eef Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Apr 2020 20:42:46 +0200 Subject: [PATCH 35/53] polishing --- python/damask/_rotation.py | 8 ++++---- python/tests/test_Rotation.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index b2a08d77b..c4f0b3c23 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -502,7 +502,7 @@ class Rotation: np.ones( qu.shape[:-1]+(1,))*np.pi, np.zeros(qu.shape[:-1]+(1,))]), eu) # TODO: Where can be nested - # reduce Euler angles to definition range, i.e a lower limit of 0.0 + # reduce Euler angles to definition range eu[np.abs(eu)<1.e-6] = 0.0 eu = np.where(eu<0, (eu+2.0*np.pi)%np.array([2.0*np.pi,np.pi,2.0*np.pi]),eu) return eu @@ -530,7 +530,7 @@ class Rotation: ax = np.where(np.broadcast_to(qu[...,0:1] < 1.0e-6,qu.shape), np.block([qu[...,1:4],np.ones(qu.shape[:-1]+(1,))*np.pi]), np.block([qu[...,1:4]*s,omega])) - ax[np.sum(np.abs(qu[...,1:4])**2,axis=-1) < 1.0e-6,] = [0.0, 0.0, 1.0, 0.0] + ax[np.sum(np.abs(qu[...,1:4])**2,axis=-1) < 1.0e-6,] = [0.0, 0.0, 1.0, 0.0] return ax @staticmethod @@ -559,10 +559,10 @@ class Rotation: def qu2ho(qu): """Quaternion to homochoric vector.""" if len(qu.shape) == 1: - if np.isclose(qu[0],1.0): + omega = 2.0 * np.arccos(np.clip(qu[0],-1.0,1.0)) + if np.abs(omega) < 1.0e-12: ho = np.zeros(3) else: - omega = 2.0 * np.arccos(np.clip(qu[0],-1.0,1.0)) ho = np.array([qu[1], qu[2], qu[3]]) f = 0.75 * ( omega - np.sin(omega) ) ho = ho/np.linalg.norm(ho) * f**(1./3.) diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 0007a2aec..2ac819f4c 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -167,7 +167,7 @@ class TestRotation: conversion(qu.reshape(qu.shape[0]//2,-1,4)) co = conversion(qu) for q,c in zip(qu,co): - #print(q,c) + print(q,c) assert np.allclose(conversion(q),c) @pytest.mark.parametrize('conversion',[Rotation.om2qu, From f41a47ce8b223210a81ece0fa549816d7e8b6b39 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Apr 2020 23:27:25 +0200 Subject: [PATCH 36/53] polishing and slightly stricter tolerances --- python/damask/_rotation.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index c4f0b3c23..e83c5fd1d 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -473,9 +473,9 @@ class Rotation: q03 = qu[0]**2+qu[3]**2 q12 = qu[1]**2+qu[2]**2 chi = np.sqrt(q03*q12) - if np.abs(q12) < 1.e-6: + if np.abs(q12) < 1.e-8: eu = np.array([np.arctan2(-P*2.0*qu[0]*qu[3],qu[0]**2-qu[3]**2), 0.0, 0.0]) - elif np.abs(q03) < 1.e-6: + elif np.abs(q03) < 1.e-8: eu = np.array([np.arctan2( 2.0*qu[1]*qu[2],qu[1]**2-qu[2]**2), np.pi, 0.0]) else: eu = np.array([np.arctan2((-P*qu[0]*qu[2]+qu[1]*qu[3])*chi, (-P*qu[0]*qu[1]-qu[2]*qu[3])*chi ), @@ -490,18 +490,18 @@ class Rotation: q12_s = qu[...,1:2]**2+qu[...,2:3]**2 chi = np.sqrt(q03_s*q12_s) - eu = np.where(np.abs(q12_s) < 1.0e-6, - np.block([np.arctan2(-P*2.0*qu[...,0:1]*qu[...,3:4],qu[...,0:1]**2-qu[...,3:4]**2), - np.zeros(qu.shape[:-1]+(2,))]), - np.block([np.arctan2((-P*q02+q13)*chi, (-P*q01-q23)*chi), - np.arctan2( 2.0*chi, q03_s-q12_s ), - np.arctan2(( P*q02+q13)*chi, (-P*q01+q23)*chi)]) + eu = np.where(np.abs(q12_s) < 1.0e-8, + np.block([np.arctan2(-P*2.0*qu[...,0:1]*qu[...,3:4],qu[...,0:1]**2-qu[...,3:4]**2), + np.zeros(qu.shape[:-1]+(2,))]), + np.where(np.abs(q03_s) < 1.0e-8, + np.block([np.arctan2( 2.0*qu[...,1:2]*qu[...,2:3],qu[...,1:2]**2-qu[...,2:3]**2), + np.broadcast_to(np.pi,qu.shape[:-1]+(1,)), + np.zeros(qu.shape[:-1]+(1,))]), + np.block([np.arctan2((-P*q02+q13)*chi, (-P*q01-q23)*chi), + np.arctan2( 2.0*chi, q03_s-q12_s ), + np.arctan2(( P*q02+q13)*chi, (-P*q01+q23)*chi)]) + ) ) - eu = np.where(np.logical_and(np.abs(q03_s) < 1.0e-6, np.abs(q12_s) >= 1.0e-6), - np.block([np.arctan2( 2.0*qu[...,1:2]*qu[...,2:3],qu[...,1:2]**2-qu[...,2:3]**2), - np.ones( qu.shape[:-1]+(1,))*np.pi, - np.zeros(qu.shape[:-1]+(1,))]), - eu) # TODO: Where can be nested # reduce Euler angles to definition range eu[np.abs(eu)<1.e-6] = 0.0 eu = np.where(eu<0, (eu+2.0*np.pi)%np.array([2.0*np.pi,np.pi,2.0*np.pi]),eu) @@ -528,7 +528,7 @@ class Rotation: s = np.sign(qu[...,0:1])/np.sqrt(qu[...,1:2]**2+qu[...,2:3]**2+qu[...,3:4]**2) omega = 2.0 * np.arccos(np.clip(qu[...,0:1],-1.0,1.0)) ax = np.where(np.broadcast_to(qu[...,0:1] < 1.0e-6,qu.shape), - np.block([qu[...,1:4],np.ones(qu.shape[:-1]+(1,))*np.pi]), + np.block([qu[...,1:4],np.broadcast_to(np.pi,qu.shape[:-1]+(1,))]), np.block([qu[...,1:4]*s,omega])) ax[np.sum(np.abs(qu[...,1:4])**2,axis=-1) < 1.0e-6,] = [0.0, 0.0, 1.0, 0.0] return ax @@ -547,7 +547,7 @@ class Rotation: with np.errstate(invalid='ignore',divide='ignore'): s = np.linalg.norm(qu[...,1:4],axis=-1,keepdims=True) ro = np.where(np.broadcast_to(np.abs(qu[...,0:1]) < 1.0e-12,qu.shape), - np.block([qu[...,1:2], qu[...,2:3], qu[...,3:4], np.ones(qu.shape[:-1]+(1,))*np.inf]), + np.block([qu[...,1:2], qu[...,2:3], qu[...,3:4], np.broadcast_to(np.inf,qu.shape[:-1]+(1,))]), np.block([qu[...,1:2]/s,qu[...,2:3]/s,qu[...,3:4]/s, np.tan(np.arccos(np.clip(qu[...,0:1],-1.0,1.0))) ]) @@ -805,7 +805,7 @@ class Rotation: else: c = np.cos(ax[...,3:4]*.5) s = np.sin(ax[...,3:4]*.5) - qu = np.where(np.abs(ax[...,3:4])<1.e-12,[1.0, 0.0, 0.0, 0.0],np.block([c, ax[...,:3]*s])) + qu = np.where(np.abs(ax[...,3:4])<1.e-6,[1.0, 0.0, 0.0, 0.0],np.block([c, ax[...,:3]*s])) return qu @staticmethod @@ -988,7 +988,7 @@ class Rotation: s += tfit[i] * hm with np.errstate(invalid='ignore'): ax = np.where(np.broadcast_to(np.abs(hmag_squared)<1.e-6,ho.shape[:-1]+(4,)), - np.array([ 0.0, 0.0, 1.0, 0.0 ]), + [ 0.0, 0.0, 1.0, 0.0 ], np.block([ho/np.sqrt(hmag_squared),2.0*np.arccos(np.clip(s,-1.0,1.0))])) return ax From 8aa95776d60b9dfc52ffe8b0a65759a7480288b5 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 12 Apr 2020 00:17:48 +0200 Subject: [PATCH 37/53] change to test not needed --- PRIVATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVATE b/PRIVATE index 85e4cb8e5..232a094c7 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 85e4cb8e5b1bd437a6649457457cfd67b64e5a51 +Subproject commit 232a094c715bcbbd1c6652c4dc4a4a50d402b82f From 04fbc38a4b8b27f49a3c982b5946cb2da33f60a1 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 12 Apr 2020 01:19:11 +0200 Subject: [PATCH 38/53] keep namespace clean and avoid overwriting in fromXXX functions --- python/damask/_rotation.py | 58 +++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index e83c5fd1d..b68ce4d8e 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -2,7 +2,7 @@ import numpy as np from ._Lambert import ball_to_cube, cube_to_ball -P = -1 +_P = -1 def iszero(a): return np.isclose(a,0.0,atol=1.0e-12,rtol=0.0) @@ -89,7 +89,7 @@ class Rotation: other_q = other.quaternion[0] other_p = other.quaternion[1:] R = self.__class__(np.append(self_q*other_q - np.dot(self_p,other_p), - self_q*other_p + other_q*self_p + P * np.cross(self_p,other_p))) + self_q*other_p + other_q*self_p + _P * np.cross(self_p,other_p))) return R.standardize() elif isinstance(other, (tuple,np.ndarray)): if isinstance(other,tuple) or other.shape == (3,): # rotate a single (3)-vector or meshgrid @@ -97,7 +97,7 @@ class Rotation: B = 2.0 * ( self.quaternion[1]*other[0] + self.quaternion[2]*other[1] + self.quaternion[3]*other[2]) - C = 2.0 * P*self.quaternion[0] + C = 2.0 * _P*self.quaternion[0] return np.array([ A*other[0] + B*self.quaternion[1] + C*(self.quaternion[2]*other[2] - self.quaternion[3]*other[1]), @@ -464,7 +464,7 @@ class Rotation: 2.0*(qu[...,2:3]*qu[...,3:4]-qu[...,0:1]*qu[...,1:2]), qq + 2.0*qu[...,3:4]**2, ]).reshape(qu.shape[:-1]+(3,3)) - return om if P < 0.0 else np.swapaxes(om,(-1,-2)) + return om if _P < 0.0 else np.swapaxes(om,(-1,-2)) @staticmethod def qu2eu(qu): @@ -474,13 +474,13 @@ class Rotation: q12 = qu[1]**2+qu[2]**2 chi = np.sqrt(q03*q12) if np.abs(q12) < 1.e-8: - eu = np.array([np.arctan2(-P*2.0*qu[0]*qu[3],qu[0]**2-qu[3]**2), 0.0, 0.0]) + eu = np.array([np.arctan2(-_P*2.0*qu[0]*qu[3],qu[0]**2-qu[3]**2), 0.0, 0.0]) elif np.abs(q03) < 1.e-8: eu = np.array([np.arctan2( 2.0*qu[1]*qu[2],qu[1]**2-qu[2]**2), np.pi, 0.0]) else: - eu = np.array([np.arctan2((-P*qu[0]*qu[2]+qu[1]*qu[3])*chi, (-P*qu[0]*qu[1]-qu[2]*qu[3])*chi ), + eu = np.array([np.arctan2((-_P*qu[0]*qu[2]+qu[1]*qu[3])*chi, (-_P*qu[0]*qu[1]-qu[2]*qu[3])*chi ), np.arctan2( 2.0*chi, q03-q12 ), - np.arctan2(( P*qu[0]*qu[2]+qu[1]*qu[3])*chi, (-P*qu[0]*qu[1]+qu[2]*qu[3])*chi )]) + np.arctan2(( _P*qu[0]*qu[2]+qu[1]*qu[3])*chi, (-_P*qu[0]*qu[1]+qu[2]*qu[3])*chi )]) else: q02 = qu[...,0:1]*qu[...,2:3] q13 = qu[...,1:2]*qu[...,3:4] @@ -491,15 +491,15 @@ class Rotation: chi = np.sqrt(q03_s*q12_s) eu = np.where(np.abs(q12_s) < 1.0e-8, - np.block([np.arctan2(-P*2.0*qu[...,0:1]*qu[...,3:4],qu[...,0:1]**2-qu[...,3:4]**2), + np.block([np.arctan2(-_P*2.0*qu[...,0:1]*qu[...,3:4],qu[...,0:1]**2-qu[...,3:4]**2), np.zeros(qu.shape[:-1]+(2,))]), np.where(np.abs(q03_s) < 1.0e-8, np.block([np.arctan2( 2.0*qu[...,1:2]*qu[...,2:3],qu[...,1:2]**2-qu[...,2:3]**2), np.broadcast_to(np.pi,qu.shape[:-1]+(1,)), np.zeros(qu.shape[:-1]+(1,))]), - np.block([np.arctan2((-P*q02+q13)*chi, (-P*q01-q23)*chi), + np.block([np.arctan2((-_P*q02+q13)*chi, (-_P*q01-q23)*chi), np.arctan2( 2.0*chi, q03_s-q12_s ), - np.arctan2(( P*q02+q13)*chi, (-P*q01+q23)*chi)]) + np.arctan2(( _P*q02+q13)*chi, (-_P*q01+q23)*chi)]) ) ) # reduce Euler angles to definition range @@ -541,7 +541,7 @@ class Rotation: ro = np.array([qu[1], qu[2], qu[3], np.inf]) else: s = np.linalg.norm(qu[1:4]) - ro = np.array([0.0,0.0,P,0.0] if iszero(s) else \ + ro = np.array([0.0,0.0,_P,0.0] if iszero(s) else \ [ qu[1]/s, qu[2]/s, qu[3]/s, np.tan(np.arccos(np.clip(qu[0],-1.0,1.0)))]) else: with np.errstate(invalid='ignore',divide='ignore'): @@ -552,7 +552,7 @@ class Rotation: np.tan(np.arccos(np.clip(qu[...,0:1],-1.0,1.0))) ]) ) - ro[np.abs(s).squeeze(-1) < 1.0e-12] = [0.0,0.0,P,0.0] + ro[np.abs(s).squeeze(-1) < 1.0e-12] = [0.0,0.0,_P,0.0] return ro @staticmethod @@ -636,11 +636,11 @@ class Rotation: # next, find the eigenvalue (1,0j) i = np.where(np.isclose(w,1.0+0.0j))[0][0] ax[0:3] = np.real(vr[0:3,i]) - diagDelta = -P*np.array([om[1,2]-om[2,1],om[2,0]-om[0,2],om[0,1]-om[1,0]]) + diagDelta = -_P*np.array([om[1,2]-om[2,1],om[2,0]-om[0,2],om[0,1]-om[1,0]]) diagDelta[np.abs(diagDelta)<1.e-6] = 1.0 ax[0:3] = np.where(np.abs(diagDelta)<0, ax[0:3],np.abs(ax[0:3])*np.sign(diagDelta)) else: - diag_delta = -P*np.block([om[...,1,2:3]-om[...,2,1:2], + diag_delta = -_P*np.block([om[...,1,2:3]-om[...,2,1:2], om[...,2,0:1]-om[...,0,2:3], om[...,0,1:2]-om[...,1,0:1] ]) @@ -685,18 +685,18 @@ class Rotation: cPhi = np.cos(ee[1]) sPhi = np.sin(ee[1]) qu = np.array([ cPhi*np.cos(ee[0]+ee[2]), - -P*sPhi*np.cos(ee[0]-ee[2]), - -P*sPhi*np.sin(ee[0]-ee[2]), - -P*cPhi*np.sin(ee[0]+ee[2]) ]) + -_P*sPhi*np.cos(ee[0]-ee[2]), + -_P*sPhi*np.sin(ee[0]-ee[2]), + -_P*cPhi*np.sin(ee[0]+ee[2]) ]) if qu[0] < 0.0: qu*=-1 else: ee = 0.5*eu cPhi = np.cos(ee[...,1:2]) sPhi = np.sin(ee[...,1:2]) qu = np.block([ cPhi*np.cos(ee[...,0:1]+ee[...,2:3]), - -P*sPhi*np.cos(ee[...,0:1]-ee[...,2:3]), - -P*sPhi*np.sin(ee[...,0:1]-ee[...,2:3]), - -P*cPhi*np.sin(ee[...,0:1]+ee[...,2:3])]) + -_P*sPhi*np.cos(ee[...,0:1]-ee[...,2:3]), + -_P*sPhi*np.sin(ee[...,0:1]-ee[...,2:3]), + -_P*cPhi*np.sin(ee[...,0:1]+ee[...,2:3])]) qu[qu[...,0]<0.0]*=-1 return qu @@ -741,7 +741,7 @@ class Rotation: if np.abs(alpha)<1.e-6: ax = np.array([ 0.0, 0.0, 1.0, 0.0 ]) else: - ax = -P/tau * np.array([ t*np.cos(delta), t*np.sin(delta), np.sin(sigma) ]) # passive axis angle pair so a minus sign in front + ax = -_P/tau * np.array([ t*np.cos(delta), t*np.sin(delta), np.sin(sigma) ]) # passive axis angle pair so a minus sign in front ax = np.append(ax,alpha) if alpha < 0.0: ax *= -1.0 # ensure alpha is positive else: @@ -753,9 +753,9 @@ class Rotation: with np.errstate(invalid='ignore',divide='ignore'): ax = np.where(np.broadcast_to(np.abs(alpha)<1.0e-12,eu.shape[:-1]+(4,)), [0.0,0.0,1.0,0.0], - np.block([-P/tau*t*np.cos(delta), - -P/tau*t*np.sin(delta), - -P/tau* np.sin(sigma), + np.block([-_P/tau*t*np.cos(delta), + -_P/tau*t*np.sin(delta), + -_P/tau* np.sin(sigma), alpha ])) ax[(alpha<0.0).squeeze()] *=-1 @@ -769,14 +769,14 @@ class Rotation: if ro[3] >= np.pi: # Differs from original implementation. check convention 5 ro[3] = np.inf elif iszero(ro[3]): - ro = np.array([ 0.0, 0.0, P, 0.0 ]) + ro = np.array([ 0.0, 0.0, _P, 0.0 ]) else: ro[3] = np.tan(ro[3]*0.5) else: ax = Rotation.eu2ax(eu) ro = np.block([ax[...,:3],np.tan(ax[...,3:4]*.5)]) ro[ax[...,3]>=np.pi,3] = np.inf - ro[np.abs(ax[...,3])<1.e-16] = [ 0.0, 0.0, P, 0.0 ] + ro[np.abs(ax[...,3])<1.e-16] = [ 0.0, 0.0, _P, 0.0 ] return ro @staticmethod @@ -834,7 +834,7 @@ class Rotation: omc*ax[...,0:1]*ax[...,2:3] + s*ax[...,1:2], omc*ax[...,1:2]*ax[...,2:3] - s*ax[...,0:1], c+omc*ax[...,2:3]**2]).reshape(ax.shape[:-1]+(3,3)) - return om if P < 0.0 else np.swapaxes(om,(-1,-2)) + return om if _P < 0.0 else np.swapaxes(om,(-1,-2)) @staticmethod def ax2eu(ax): @@ -846,7 +846,7 @@ class Rotation: """Axis angle pair to Rodrigues-Frank vector.""" if len(ax.shape) == 1: if np.abs(ax[3])<1.e-6: - ro = [ 0.0, 0.0, P, 0.0 ] + ro = [ 0.0, 0.0, _P, 0.0 ] else: ro = [ax[0], ax[1], ax[2]] # 180 degree case @@ -859,7 +859,7 @@ class Rotation: np.inf, np.tan(ax[...,3:4]*0.5)) ]) - ro[np.abs(ax[...,3])<1.e-6] = [.0,.0,P,.0] + ro[np.abs(ax[...,3])<1.e-6] = [.0,.0,_P,.0] return ro @staticmethod From 3d10266fbc759da1c54b8c15996e2f5d837714c2 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 12 Apr 2020 01:29:11 +0200 Subject: [PATCH 39/53] similar style as for other conversions --- python/damask/_Lambert.py | 4 ---- python/damask/_rotation.py | 7 +++++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/python/damask/_Lambert.py b/python/damask/_Lambert.py index 59abe7f64..a823764e9 100644 --- a/python/damask/_Lambert.py +++ b/python/damask/_Lambert.py @@ -52,8 +52,6 @@ def cube_to_ball(cube): """ cube_ = np.clip(cube,None,np.pi**(2./3.) * 0.5) if np.isclose(np.abs(np.max(cube)),np.pi**(2./3.) * 0.5,atol=1e-6) else cube - if np.abs(np.max(cube_))>np.pi**(2./3.) * 0.5: - raise ValueError('Coordinate outside of the cube: {} {} {}.'.format(*cube)) # transform to the sphere grid via the curved square, and intercept the zero point if np.allclose(cube_,0.0,rtol=0.0,atol=1.0e-16): @@ -105,8 +103,6 @@ def ball_to_cube(ball): """ ball_ = ball/np.linalg.norm(ball)*R1 if np.isclose(np.linalg.norm(ball),R1,atol=1e-6) else ball rs = np.linalg.norm(ball_) - if rs > R1+1.e-9: - raise ValueError('Coordinate outside of the sphere: {} {} {}.'.format(*ball)) if np.allclose(ball_,0.0,rtol=0.0,atol=1.0e-16): cube = np.zeros(3) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index b68ce4d8e..dcd61822f 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -350,6 +350,9 @@ class Rotation: else np.array(homochoric,dtype=float) if P > 0: ho *= -1 # convert from P=1 to P=-1 + if np.linalg.norm(ho) > (3.*np.pi/4.)**(1./3.)+1e-9: + raise ValueError('Coordinate outside of the sphere: {} {} {}.'.format(ho)) + return Rotation(Rotation.ho2qu(ho)) @staticmethod @@ -358,6 +361,10 @@ class Rotation: cu = cubochoric if isinstance(cubochoric, np.ndarray) and cubochoric.dtype == np.dtype(float) \ else np.array(cubochoric,dtype=float) + + if np.abs(np.max(cu))>np.pi**(2./3.) * 0.5+1e-9: + raise ValueError('Coordinate outside of the cube: {} {} {}.'.format(*cu)) + ho = Rotation.cu2ho(cu) if P > 0: ho *= -1 # convert from P=1 to P=-1 From 99ec48aba4712a0e1996bf8d91b0250364ab7496 Mon Sep 17 00:00:00 2001 From: Test User Date: Sun, 12 Apr 2020 19:42:46 +0200 Subject: [PATCH 40/53] [skip ci] updated version information after successful test of v2.0.3-2255-gc74ffae8 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 804287b93..0e875200a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.3-2245-gc7508d85 +v2.0.3-2255-gc74ffae8 From 95e41e0b3f7961c1570124237baee585b989a15a Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 14 Apr 2020 07:22:30 +0200 Subject: [PATCH 41/53] not needed (just linear interpolation) --- src/homogenization.f90 | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/homogenization.f90 b/src/homogenization.f90 index 50331cbdd..cef3c2c5b 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -161,7 +161,6 @@ subroutine homogenization_init allocate(materialpoint_dPdF(3,3,3,3,discretization_nIP,discretization_nElem), source=0.0_pReal) materialpoint_F0 = spread(spread(math_I3,3,discretization_nIP),4,discretization_nElem) ! initialize to identity materialpoint_F = materialpoint_F0 ! initialize to identity - allocate(materialpoint_subF0(3,3,discretization_nIP,discretization_nElem), source=0.0_pReal) allocate(materialpoint_subF(3,3,discretization_nIP,discretization_nElem), source=0.0_pReal) allocate(materialpoint_P(3,3,discretization_nIP,discretization_nElem), source=0.0_pReal) allocate(materialpoint_subFrac(discretization_nIP,discretization_nElem), source=0.0_pReal) @@ -238,8 +237,6 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) enddo - - materialpoint_subF0(1:3,1:3,i,e) = materialpoint_F0(1:3,1:3,i,e) materialpoint_subFrac(i,e) = 0.0_pReal materialpoint_subStep(i,e) = 1.0_pReal/num%subStepSizeHomog ! <> materialpoint_converged(i,e) = .false. ! pretend failed step of twice the required size @@ -326,8 +323,6 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) damageState(material_homogenizationAt(e))%subState0(:,material_homogenizationMemberAt(i,e)) = & damageState(material_homogenizationAt(e))%State (:,material_homogenizationMemberAt(i,e)) - materialpoint_subF0(1:3,1:3,i,e) = materialpoint_subF(1:3,1:3,i,e) - endif steppingNeeded else converged @@ -390,9 +385,9 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) if (materialpoint_subStep(i,e) > num%subStepMinHomog) then materialpoint_requested(i,e) = .true. - materialpoint_subF(1:3,1:3,i,e) = materialpoint_subF0(1:3,1:3,i,e) & - + materialpoint_subStep(i,e) * (materialpoint_F(1:3,1:3,i,e) & - - materialpoint_F0(1:3,1:3,i,e)) + materialpoint_subF(1:3,1:3,i,e) = materialpoint_F0(1:3,1:3,i,e) & + + (materialpoint_F(1:3,1:3,i,e) - materialpoint_F0(1:3,1:3,i,e)) & + * (materialpoint_subStep(i,e)+materialpoint_subFrac(i,e)) materialpoint_subdt(i,e) = materialpoint_subStep(i,e) * dt materialpoint_doneAndHappy(1:2,i,e) = [.false.,.true.] endif From d616c1dda8764af5a333b426ab84a3161c424b65 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 14 Apr 2020 07:45:39 +0200 Subject: [PATCH 42/53] better use explicit arguments --- src/homogenization.f90 | 46 +++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/homogenization.f90 b/src/homogenization.f90 index cef3c2c5b..157b688f5 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -414,7 +414,7 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) IpLooping2: do i = FEsolving_execIP(1),FEsolving_execIP(2) if ( materialpoint_requested(i,e) .and. & ! process requested but... .not. materialpoint_doneAndHappy(1,i,e)) then ! ...not yet done material points - call partitionDeformation(i,e) ! partition deformation onto constituents + call partitionDeformation(materialpoint_subF(1:3,1:3,i,e),i,e) ! partition deformation onto constituents crystallite_dt(1:myNgrains,i,e) = materialpoint_subdt(i,e) ! propagate materialpoint dt to grains crystallite_requested(1:myNgrains,i,e) = .true. ! request calculation for constituents else @@ -441,7 +441,9 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) if (.not. materialpoint_converged(i,e)) then materialpoint_doneAndHappy(1:2,i,e) = [.true.,.false.] else - materialpoint_doneAndHappy(1:2,i,e) = updateState(i,e) + materialpoint_doneAndHappy(1:2,i,e) = updateState(materialpoint_subdt(i,e), & + materialpoint_subF(1:3,1:3,i,e), & + i,e) materialpoint_converged(i,e) = all(materialpoint_doneAndHappy(1:2,i,e)) ! converged if done and happy endif endif @@ -476,26 +478,28 @@ end subroutine materialpoint_stressAndItsTangent !-------------------------------------------------------------------------------------------------- !> @brief partition material point def grad onto constituents !-------------------------------------------------------------------------------------------------- -subroutine partitionDeformation(ip,el) +subroutine partitionDeformation(subF,ip,el) - integer, intent(in) :: & + real(pReal), intent(in), dimension(3,3) :: & + subF + integer, intent(in) :: & ip, & !< integration point el !< element number chosenHomogenization: select case(homogenization_type(material_homogenizationAt(el))) case (HOMOGENIZATION_NONE_ID) chosenHomogenization - crystallite_partionedF(1:3,1:3,1,ip,el) = materialpoint_subF(1:3,1:3,ip,el) + crystallite_partionedF(1:3,1:3,1,ip,el) = subF case (HOMOGENIZATION_ISOSTRAIN_ID) chosenHomogenization call mech_isostrain_partitionDeformation(& crystallite_partionedF(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & - materialpoint_subF(1:3,1:3,ip,el)) + subF) case (HOMOGENIZATION_RGC_ID) chosenHomogenization call mech_RGC_partitionDeformation(& crystallite_partionedF(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & - materialpoint_subF(1:3,1:3,ip,el),& + subF,& ip, & el) end select chosenHomogenization @@ -507,9 +511,13 @@ end subroutine partitionDeformation !> @brief update the internal state of the homogenization scheme and tell whether "done" and !> "happy" with result !-------------------------------------------------------------------------------------------------- -function updateState(ip,el) +function updateState(subdt,subF,ip,el) - integer, intent(in) :: & + real(pReal), intent(in) :: & + subdt !< current time step + real(pReal), intent(in), dimension(3,3) :: & + subF + integer, intent(in) :: & ip, & !< integration point el !< element number logical, dimension(2) :: updateState @@ -519,21 +527,21 @@ function updateState(ip,el) case (HOMOGENIZATION_RGC_ID) chosenHomogenization updateState = & updateState .and. & - mech_RGC_updateState(crystallite_P(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & - crystallite_partionedF(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & - crystallite_partionedF0(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el),& - materialpoint_subF(1:3,1:3,ip,el),& - materialpoint_subdt(ip,el), & - crystallite_dPdF(1:3,1:3,1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & - ip, & - el) + mech_RGC_updateState(crystallite_P(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & + crystallite_partionedF(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & + crystallite_partionedF0(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el),& + subF,& + subdt, & + crystallite_dPdF(1:3,1:3,1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & + ip, & + el) end select chosenHomogenization chosenThermal: select case (thermal_type(material_homogenizationAt(el))) case (THERMAL_adiabatic_ID) chosenThermal updateState = & updateState .and. & - thermal_adiabatic_updateState(materialpoint_subdt(ip,el), & + thermal_adiabatic_updateState(subdt, & ip, & el) end select chosenThermal @@ -542,7 +550,7 @@ function updateState(ip,el) case (DAMAGE_local_ID) chosenDamage updateState = & updateState .and. & - damage_local_updateState(materialpoint_subdt(ip,el), & + damage_local_updateState(subdt, & ip, & el) end select chosenDamage From 08948867441d9dd212da62ee71e10f2353d697ef Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 14 Apr 2020 07:57:25 +0200 Subject: [PATCH 43/53] can be calculated when needed --- src/homogenization.f90 | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/homogenization.f90 b/src/homogenization.f90 index 157b688f5..d6112fe8d 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -43,8 +43,7 @@ module homogenization materialpoint_subF !< def grad of IP to be reached at end of homog inc real(pReal), dimension(:,:), allocatable :: & materialpoint_subFrac, & - materialpoint_subStep, & - materialpoint_subdt + materialpoint_subStep logical, dimension(:,:), allocatable :: & materialpoint_requested, & materialpoint_converged @@ -165,7 +164,6 @@ subroutine homogenization_init allocate(materialpoint_P(3,3,discretization_nIP,discretization_nElem), source=0.0_pReal) allocate(materialpoint_subFrac(discretization_nIP,discretization_nElem), source=0.0_pReal) allocate(materialpoint_subStep(discretization_nIP,discretization_nElem), source=0.0_pReal) - allocate(materialpoint_subdt(discretization_nIP,discretization_nElem), source=0.0_pReal) allocate(materialpoint_requested(discretization_nIP,discretization_nElem), source=.false.) allocate(materialpoint_converged(discretization_nIP,discretization_nElem), source=.true.) allocate(materialpoint_doneAndHappy(2,discretization_nIP,discretization_nElem), source=.true.) @@ -388,7 +386,6 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) materialpoint_subF(1:3,1:3,i,e) = materialpoint_F0(1:3,1:3,i,e) & + (materialpoint_F(1:3,1:3,i,e) - materialpoint_F0(1:3,1:3,i,e)) & * (materialpoint_subStep(i,e)+materialpoint_subFrac(i,e)) - materialpoint_subdt(i,e) = materialpoint_subStep(i,e) * dt materialpoint_doneAndHappy(1:2,i,e) = [.false.,.true.] endif enddo IpLooping1 @@ -415,7 +412,7 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) if ( materialpoint_requested(i,e) .and. & ! process requested but... .not. materialpoint_doneAndHappy(1,i,e)) then ! ...not yet done material points call partitionDeformation(materialpoint_subF(1:3,1:3,i,e),i,e) ! partition deformation onto constituents - crystallite_dt(1:myNgrains,i,e) = materialpoint_subdt(i,e) ! propagate materialpoint dt to grains + crystallite_dt(1:myNgrains,i,e) = dt*materialpoint_subStep(i,e) ! propagate materialpoint dt to grains crystallite_requested(1:myNgrains,i,e) = .true. ! request calculation for constituents else crystallite_requested(1:myNgrains,i,e) = .false. ! calculation for constituents not required anymore @@ -441,7 +438,7 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) if (.not. materialpoint_converged(i,e)) then materialpoint_doneAndHappy(1:2,i,e) = [.true.,.false.] else - materialpoint_doneAndHappy(1:2,i,e) = updateState(materialpoint_subdt(i,e), & + materialpoint_doneAndHappy(1:2,i,e) = updateState(dt*materialpoint_subStep(i,e), & materialpoint_subF(1:3,1:3,i,e), & i,e) materialpoint_converged(i,e) = all(materialpoint_doneAndHappy(1:2,i,e)) ! converged if done and happy From 0de4520580f87f68328c38f3ca1c3a574ca07984 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 14 Apr 2020 08:24:28 +0200 Subject: [PATCH 44/53] directly calculate subF --- src/homogenization.f90 | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/homogenization.f90 b/src/homogenization.f90 index d6112fe8d..543388f1c 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -38,9 +38,6 @@ module homogenization real(pReal), dimension(:,:,:,:,:,:), allocatable, public :: & materialpoint_dPdF !< tangent of first P--K stress at IP - real(pReal), dimension(:,:,:,:), allocatable :: & - materialpoint_subF0, & !< def grad of IP at beginning of homogenization increment - materialpoint_subF !< def grad of IP to be reached at end of homog inc real(pReal), dimension(:,:), allocatable :: & materialpoint_subFrac, & materialpoint_subStep @@ -160,7 +157,6 @@ subroutine homogenization_init allocate(materialpoint_dPdF(3,3,3,3,discretization_nIP,discretization_nElem), source=0.0_pReal) materialpoint_F0 = spread(spread(math_I3,3,discretization_nIP),4,discretization_nElem) ! initialize to identity materialpoint_F = materialpoint_F0 ! initialize to identity - allocate(materialpoint_subF(3,3,discretization_nIP,discretization_nElem), source=0.0_pReal) allocate(materialpoint_P(3,3,discretization_nIP,discretization_nElem), source=0.0_pReal) allocate(materialpoint_subFrac(discretization_nIP,discretization_nElem), source=0.0_pReal) allocate(materialpoint_subStep(discretization_nIP,discretization_nElem), source=0.0_pReal) @@ -200,6 +196,8 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) e, & !< element number mySource, & myNgrains + real(pReal), dimension(3,3) :: & + subF #ifdef DEBUG if (iand(debug_level(debug_homogenization), debug_levelBasic) /= 0) then @@ -383,9 +381,6 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) if (materialpoint_subStep(i,e) > num%subStepMinHomog) then materialpoint_requested(i,e) = .true. - materialpoint_subF(1:3,1:3,i,e) = materialpoint_F0(1:3,1:3,i,e) & - + (materialpoint_F(1:3,1:3,i,e) - materialpoint_F0(1:3,1:3,i,e)) & - * (materialpoint_subStep(i,e)+materialpoint_subFrac(i,e)) materialpoint_doneAndHappy(1:2,i,e) = [.false.,.true.] endif enddo IpLooping1 @@ -405,13 +400,16 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) ! deformation partitioning ! based on materialpoint_subF0,.._subF,crystallite_partionedF0, and homogenization_state, ! results in crystallite_partionedF - !$OMP PARALLEL DO PRIVATE(myNgrains) + !$OMP PARALLEL DO PRIVATE(myNgrains,subF) elementLooping2: do e = FEsolving_execElem(1),FEsolving_execElem(2) myNgrains = homogenization_Ngrains(material_homogenizationAt(e)) IpLooping2: do i = FEsolving_execIP(1),FEsolving_execIP(2) if ( materialpoint_requested(i,e) .and. & ! process requested but... .not. materialpoint_doneAndHappy(1,i,e)) then ! ...not yet done material points - call partitionDeformation(materialpoint_subF(1:3,1:3,i,e),i,e) ! partition deformation onto constituents + subF = materialpoint_F0(1:3,1:3,i,e) & + + (materialpoint_F(1:3,1:3,i,e) - materialpoint_F0(1:3,1:3,i,e)) & + * (materialpoint_subStep(i,e)+materialpoint_subFrac(i,e)) + call partitionDeformation(subF,i,e) ! partition deformation onto constituents crystallite_dt(1:myNgrains,i,e) = dt*materialpoint_subStep(i,e) ! propagate materialpoint dt to grains crystallite_requested(1:myNgrains,i,e) = .true. ! request calculation for constituents else @@ -430,7 +428,7 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) !-------------------------------------------------------------------------------------------------- ! state update - !$OMP PARALLEL DO + !$OMP PARALLEL DO PRIVATE(subF) elementLooping3: do e = FEsolving_execElem(1),FEsolving_execElem(2) IpLooping3: do i = FEsolving_execIP(1),FEsolving_execIP(2) if ( materialpoint_requested(i,e) .and. & @@ -438,8 +436,11 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) if (.not. materialpoint_converged(i,e)) then materialpoint_doneAndHappy(1:2,i,e) = [.true.,.false.] else + subF = materialpoint_F0(1:3,1:3,i,e) & + + (materialpoint_F(1:3,1:3,i,e) - materialpoint_F0(1:3,1:3,i,e)) & + * (materialpoint_subStep(i,e)+materialpoint_subFrac(i,e)) materialpoint_doneAndHappy(1:2,i,e) = updateState(dt*materialpoint_subStep(i,e), & - materialpoint_subF(1:3,1:3,i,e), & + subF, & i,e) materialpoint_converged(i,e) = all(materialpoint_doneAndHappy(1:2,i,e)) ! converged if done and happy endif From 912c064b575d9166f6e649e05c7ad9bebd86c521 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 14 Apr 2020 09:38:32 +0200 Subject: [PATCH 45/53] indicate read-only public variables --- src/homogenization.f90 | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/homogenization.f90 b/src/homogenization.f90 index 543388f1c..795bcce35 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -27,15 +27,17 @@ module homogenization implicit none private -!-------------------------------------------------------------------------------------------------- -! General variables for the homogenization at a material point logical, public :: & terminallyIll = .false. !< at least one material point is terminally ill - real(pReal), dimension(:,:,:,:), allocatable, public :: & + +!-------------------------------------------------------------------------------------------------- +! General variables for the homogenization at a material point + real(pReal), dimension(:,:,:,:), allocatable, public :: & materialpoint_F0, & !< def grad of IP at start of FE increment - materialpoint_F, & !< def grad of IP to be reached at end of FE increment + materialpoint_F !< def grad of IP to be reached at end of FE increment + real(pReal), dimension(:,:,:,:), allocatable, public, protected :: & materialpoint_P !< first P--K stress of IP - real(pReal), dimension(:,:,:,:,:,:), allocatable, public :: & + real(pReal), dimension(:,:,:,:,:,:), allocatable, public, protected :: & materialpoint_dPdF !< tangent of first P--K stress at IP real(pReal), dimension(:,:), allocatable :: & @@ -49,7 +51,7 @@ module homogenization type :: tNumerics integer :: & - nMPstate !< materialpoint state loop limit + nMPstate !< materialpoint state loop limit real(pReal) :: & subStepMinHomog, & !< minimum (relative) size of sub-step allowed during cutback in homogenization subStepSizeHomog, & !< size of first substep when cutback in homogenization From 9d831cf268c464b2ba0e45168be189db0f332dc1 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 14 Apr 2020 09:43:18 +0200 Subject: [PATCH 46/53] not needed as module variable --- src/homogenization.f90 | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/homogenization.f90 b/src/homogenization.f90 index 795bcce35..7c532834e 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -40,15 +40,6 @@ module homogenization real(pReal), dimension(:,:,:,:,:,:), allocatable, public, protected :: & materialpoint_dPdF !< tangent of first P--K stress at IP - real(pReal), dimension(:,:), allocatable :: & - materialpoint_subFrac, & - materialpoint_subStep - logical, dimension(:,:), allocatable :: & - materialpoint_requested, & - materialpoint_converged - logical, dimension(:,:,:), allocatable :: & - materialpoint_doneAndHappy - type :: tNumerics integer :: & nMPstate !< materialpoint state loop limit @@ -160,11 +151,6 @@ subroutine homogenization_init materialpoint_F0 = spread(spread(math_I3,3,discretization_nIP),4,discretization_nElem) ! initialize to identity materialpoint_F = materialpoint_F0 ! initialize to identity allocate(materialpoint_P(3,3,discretization_nIP,discretization_nElem), source=0.0_pReal) - allocate(materialpoint_subFrac(discretization_nIP,discretization_nElem), source=0.0_pReal) - allocate(materialpoint_subStep(discretization_nIP,discretization_nElem), source=0.0_pReal) - allocate(materialpoint_requested(discretization_nIP,discretization_nElem), source=.false.) - allocate(materialpoint_converged(discretization_nIP,discretization_nElem), source=.true.) - allocate(materialpoint_doneAndHappy(2,discretization_nIP,discretization_nElem), source=.true.) write(6,'(/,a)') ' <<<+- homogenization init -+>>>'; flush(6) @@ -200,6 +186,14 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) myNgrains real(pReal), dimension(3,3) :: & subF + real(pReal), dimension(discretization_nIP,discretization_nElem) :: & + materialpoint_subFrac, & + materialpoint_subStep + logical, dimension(discretization_nIP,discretization_nElem) :: & + materialpoint_requested, & + materialpoint_converged + logical, dimension(2,discretization_nIP,discretization_nElem) :: & + materialpoint_doneAndHappy #ifdef DEBUG if (iand(debug_level(debug_homogenization), debug_levelBasic) /= 0) then From bf970bb146c4045432b78f5506858f8f0e216a28 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 14 Apr 2020 09:49:03 +0200 Subject: [PATCH 47/53] 2 space indentation --- src/homogenization.f90 | 158 ++++++++++++++++++++--------------------- 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/src/homogenization.f90 b/src/homogenization.f90 index 7c532834e..eab1e86c1 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -474,29 +474,29 @@ end subroutine materialpoint_stressAndItsTangent !-------------------------------------------------------------------------------------------------- subroutine partitionDeformation(subF,ip,el) - real(pReal), intent(in), dimension(3,3) :: & - subF - integer, intent(in) :: & - ip, & !< integration point - el !< element number + real(pReal), intent(in), dimension(3,3) :: & + subF + integer, intent(in) :: & + ip, & !< integration point + el !< element number - chosenHomogenization: select case(homogenization_type(material_homogenizationAt(el))) + chosenHomogenization: select case(homogenization_type(material_homogenizationAt(el))) - case (HOMOGENIZATION_NONE_ID) chosenHomogenization - crystallite_partionedF(1:3,1:3,1,ip,el) = subF + case (HOMOGENIZATION_NONE_ID) chosenHomogenization + crystallite_partionedF(1:3,1:3,1,ip,el) = subF - case (HOMOGENIZATION_ISOSTRAIN_ID) chosenHomogenization - call mech_isostrain_partitionDeformation(& + case (HOMOGENIZATION_ISOSTRAIN_ID) chosenHomogenization + call mech_isostrain_partitionDeformation(& + crystallite_partionedF(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & + subF) + + case (HOMOGENIZATION_RGC_ID) chosenHomogenization + call mech_RGC_partitionDeformation(& crystallite_partionedF(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & - subF) - - case (HOMOGENIZATION_RGC_ID) chosenHomogenization - call mech_RGC_partitionDeformation(& - crystallite_partionedF(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & - subF,& - ip, & - el) - end select chosenHomogenization + subF,& + ip, & + el) + end select chosenHomogenization end subroutine partitionDeformation @@ -507,47 +507,47 @@ end subroutine partitionDeformation !-------------------------------------------------------------------------------------------------- function updateState(subdt,subF,ip,el) - real(pReal), intent(in) :: & - subdt !< current time step - real(pReal), intent(in), dimension(3,3) :: & - subF - integer, intent(in) :: & - ip, & !< integration point - el !< element number - logical, dimension(2) :: updateState + real(pReal), intent(in) :: & + subdt !< current time step + real(pReal), intent(in), dimension(3,3) :: & + subF + integer, intent(in) :: & + ip, & !< integration point + el !< element number + logical, dimension(2) :: updateState - updateState = .true. - chosenHomogenization: select case(homogenization_type(material_homogenizationAt(el))) - case (HOMOGENIZATION_RGC_ID) chosenHomogenization - updateState = & - updateState .and. & - mech_RGC_updateState(crystallite_P(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & - crystallite_partionedF(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & - crystallite_partionedF0(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el),& - subF,& - subdt, & - crystallite_dPdF(1:3,1:3,1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & - ip, & - el) - end select chosenHomogenization + updateState = .true. + chosenHomogenization: select case(homogenization_type(material_homogenizationAt(el))) + case (HOMOGENIZATION_RGC_ID) chosenHomogenization + updateState = & + updateState .and. & + mech_RGC_updateState(crystallite_P(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & + crystallite_partionedF(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & + crystallite_partionedF0(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el),& + subF,& + subdt, & + crystallite_dPdF(1:3,1:3,1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & + ip, & + el) + end select chosenHomogenization - chosenThermal: select case (thermal_type(material_homogenizationAt(el))) - case (THERMAL_adiabatic_ID) chosenThermal - updateState = & - updateState .and. & - thermal_adiabatic_updateState(subdt, & - ip, & - el) - end select chosenThermal + chosenThermal: select case (thermal_type(material_homogenizationAt(el))) + case (THERMAL_adiabatic_ID) chosenThermal + updateState = & + updateState .and. & + thermal_adiabatic_updateState(subdt, & + ip, & + el) + end select chosenThermal - chosenDamage: select case (damage_type(material_homogenizationAt(el))) - case (DAMAGE_local_ID) chosenDamage - updateState = & - updateState .and. & - damage_local_updateState(subdt, & - ip, & - el) - end select chosenDamage + chosenDamage: select case (damage_type(material_homogenizationAt(el))) + case (DAMAGE_local_ID) chosenDamage + updateState = & + updateState .and. & + damage_local_updateState(subdt, & + ip, & + el) + end select chosenDamage end function updateState @@ -557,31 +557,31 @@ end function updateState !-------------------------------------------------------------------------------------------------- subroutine averageStressAndItsTangent(ip,el) - integer, intent(in) :: & - ip, & !< integration point - el !< element number + integer, intent(in) :: & + ip, & !< integration point + el !< element number - chosenHomogenization: select case(homogenization_type(material_homogenizationAt(el))) - case (HOMOGENIZATION_NONE_ID) chosenHomogenization - materialpoint_P(1:3,1:3,ip,el) = crystallite_P(1:3,1:3,1,ip,el) - materialpoint_dPdF(1:3,1:3,1:3,1:3,ip,el) = crystallite_dPdF(1:3,1:3,1:3,1:3,1,ip,el) + chosenHomogenization: select case(homogenization_type(material_homogenizationAt(el))) + case (HOMOGENIZATION_NONE_ID) chosenHomogenization + materialpoint_P(1:3,1:3,ip,el) = crystallite_P(1:3,1:3,1,ip,el) + materialpoint_dPdF(1:3,1:3,1:3,1:3,ip,el) = crystallite_dPdF(1:3,1:3,1:3,1:3,1,ip,el) - case (HOMOGENIZATION_ISOSTRAIN_ID) chosenHomogenization - call mech_isostrain_averageStressAndItsTangent(& - materialpoint_P(1:3,1:3,ip,el), & - materialpoint_dPdF(1:3,1:3,1:3,1:3,ip,el),& - crystallite_P(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & - crystallite_dPdF(1:3,1:3,1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & - homogenization_typeInstance(material_homogenizationAt(el))) + case (HOMOGENIZATION_ISOSTRAIN_ID) chosenHomogenization + call mech_isostrain_averageStressAndItsTangent(& + materialpoint_P(1:3,1:3,ip,el), & + materialpoint_dPdF(1:3,1:3,1:3,1:3,ip,el),& + crystallite_P(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & + crystallite_dPdF(1:3,1:3,1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & + homogenization_typeInstance(material_homogenizationAt(el))) - case (HOMOGENIZATION_RGC_ID) chosenHomogenization - call mech_RGC_averageStressAndItsTangent(& - materialpoint_P(1:3,1:3,ip,el), & - materialpoint_dPdF(1:3,1:3,1:3,1:3,ip,el),& - crystallite_P(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & - crystallite_dPdF(1:3,1:3,1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & - homogenization_typeInstance(material_homogenizationAt(el))) - end select chosenHomogenization + case (HOMOGENIZATION_RGC_ID) chosenHomogenization + call mech_RGC_averageStressAndItsTangent(& + materialpoint_P(1:3,1:3,ip,el), & + materialpoint_dPdF(1:3,1:3,1:3,1:3,ip,el),& + crystallite_P(1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & + crystallite_dPdF(1:3,1:3,1:3,1:3,1:homogenization_Ngrains(material_homogenizationAt(el)),ip,el), & + homogenization_typeInstance(material_homogenizationAt(el))) + end select chosenHomogenization end subroutine averageStressAndItsTangent From c7e62777585cd33eb871a3c2473e766062c0a497 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 14 Apr 2020 15:34:38 +0200 Subject: [PATCH 48/53] not needed https://stackoverflow.com/questions/19687233 --- src/homogenization.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/src/homogenization.f90 b/src/homogenization.f90 index eab1e86c1..40a6e7280 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -321,7 +321,6 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) if ( (myNgrains == 1 .and. materialpoint_subStep(i,e) <= 1.0 ) .or. & ! single grain already tried internal subStepping in crystallite num%subStepSizeHomog * materialpoint_subStep(i,e) <= num%subStepMinHomog ) then ! would require too small subStep ! cutback makes no sense - !$OMP FLUSH(terminallyIll) if (.not. terminallyIll) then ! so first signals terminally ill... !$OMP CRITICAL (write2out) write(6,*) 'Integration point ', i,' at element ', e, ' terminally ill' From d3eba4151966f3132aacc9f30f3c58e1b40282db Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Tue, 14 Apr 2020 12:21:35 -0400 Subject: [PATCH 49/53] [skip ci] fixed bug of double execution of np.degrees in asAxisAngle --- python/damask/_rotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index bea7aa5e6..ff4025ddc 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -206,7 +206,7 @@ class Rotation: """ ax = Rotation.qu2ax(self.quaternion) if degrees: ax[3] = np.degrees(ax[3]) - return (ax[:3],np.degrees(ax[3])) if pair else ax + return (ax[:3],ax[3]) if pair else ax def asMatrix(self): """Rotation matrix.""" From ca26dbe3819e84ad1bdb84238d2ea0e776ea7c9f Mon Sep 17 00:00:00 2001 From: Test User Date: Wed, 15 Apr 2020 03:38:45 +0200 Subject: [PATCH 50/53] [skip ci] updated version information after successful test of v2.0.3-2291-g03aa6f9c --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 0e875200a..458484abe 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.3-2255-gc74ffae8 +v2.0.3-2291-g03aa6f9c From ae95a96c88804d6f304f32d0fa8ca24d4966ab1e Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 15 Apr 2020 08:53:25 +0200 Subject: [PATCH 51/53] better readable --- src/homogenization.f90 | 54 +++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/src/homogenization.f90 b/src/homogenization.f90 index 40a6e7280..2d3807d18 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -207,7 +207,7 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) #endif !-------------------------------------------------------------------------------------------------- -! initialize restoration points of ... +! initialize restoration points do e = FEsolving_execElem(1),FEsolving_execElem(2) myNgrains = homogenization_Ngrains(material_homogenizationAt(e)) do i = FEsolving_execIP(1),FEsolving_execIP(2); @@ -230,21 +230,21 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) enddo materialpoint_subFrac(i,e) = 0.0_pReal - materialpoint_subStep(i,e) = 1.0_pReal/num%subStepSizeHomog ! <> - materialpoint_converged(i,e) = .false. ! pretend failed step of twice the required size + materialpoint_converged(i,e) = .false. ! pretend failed step ... + materialpoint_subStep(i,e) = 1.0_pReal/num%subStepSizeHomog ! ... larger then the requested calculation materialpoint_requested(i,e) = .true. ! everybody requires calculation if (homogState(material_homogenizationAt(e))%sizeState > 0) & homogState(material_homogenizationAt(e))%subState0(:,material_homogenizationMemberAt(i,e)) = & - homogState(material_homogenizationAt(e))%State0( :,material_homogenizationMemberAt(i,e)) ! ...internal homogenization state + homogState(material_homogenizationAt(e))%State0( :,material_homogenizationMemberAt(i,e)) if (thermalState(material_homogenizationAt(e))%sizeState > 0) & thermalState(material_homogenizationAt(e))%subState0(:,material_homogenizationMemberAt(i,e)) = & - thermalState(material_homogenizationAt(e))%State0( :,material_homogenizationMemberAt(i,e)) ! ...internal thermal state + thermalState(material_homogenizationAt(e))%State0( :,material_homogenizationMemberAt(i,e)) if (damageState(material_homogenizationAt(e))%sizeState > 0) & damageState(material_homogenizationAt(e))%subState0(:,material_homogenizationMemberAt(i,e)) = & - damageState(material_homogenizationAt(e))%State0( :,material_homogenizationMemberAt(i,e)) ! ...internal damage state + damageState(material_homogenizationAt(e))%State0( :,material_homogenizationMemberAt(i,e)) enddo enddo @@ -277,24 +277,13 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) steppingNeeded: if (materialpoint_subStep(i,e) > num%subStepMinHomog) then - ! wind forward grain starting point of... - crystallite_partionedF0 (1:3,1:3,1:myNgrains,i,e) = & - crystallite_partionedF(1:3,1:3,1:myNgrains,i,e) - - crystallite_partionedFp0 (1:3,1:3,1:myNgrains,i,e) = & - crystallite_Fp (1:3,1:3,1:myNgrains,i,e) - - crystallite_partionedLp0 (1:3,1:3,1:myNgrains,i,e) = & - crystallite_Lp (1:3,1:3,1:myNgrains,i,e) - - crystallite_partionedFi0 (1:3,1:3,1:myNgrains,i,e) = & - crystallite_Fi (1:3,1:3,1:myNgrains,i,e) - - crystallite_partionedLi0 (1:3,1:3,1:myNgrains,i,e) = & - crystallite_Li (1:3,1:3,1:myNgrains,i,e) - - crystallite_partionedS0 (1:3,1:3,1:myNgrains,i,e) = & - crystallite_S (1:3,1:3,1:myNgrains,i,e) + ! wind forward grain starting point + crystallite_partionedF0 (1:3,1:3,1:myNgrains,i,e) = crystallite_partionedF(1:3,1:3,1:myNgrains,i,e) + crystallite_partionedFp0(1:3,1:3,1:myNgrains,i,e) = crystallite_Fp (1:3,1:3,1:myNgrains,i,e) + crystallite_partionedLp0(1:3,1:3,1:myNgrains,i,e) = crystallite_Lp (1:3,1:3,1:myNgrains,i,e) + crystallite_partionedFi0(1:3,1:3,1:myNgrains,i,e) = crystallite_Fi (1:3,1:3,1:myNgrains,i,e) + crystallite_partionedLi0(1:3,1:3,1:myNgrains,i,e) = crystallite_Li (1:3,1:3,1:myNgrains,i,e) + crystallite_partionedS0 (1:3,1:3,1:myNgrains,i,e) = crystallite_S (1:3,1:3,1:myNgrains,i,e) do g = 1,myNgrains plasticState (material_phaseAt(g,e))%partionedState0(:,material_phasememberAt(g,i,e)) = & @@ -341,19 +330,14 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) #endif !-------------------------------------------------------------------------------------------------- -! restore... +! restore if (materialpoint_subStep(i,e) < 1.0_pReal) then ! protect against fake cutback from \Delta t = 2 to 1. Maybe that "trick" is not necessary anymore at all? I.e. start with \Delta t = 1 - crystallite_Lp(1:3,1:3,1:myNgrains,i,e) = & - crystallite_partionedLp0(1:3,1:3,1:myNgrains,i,e) - crystallite_Li(1:3,1:3,1:myNgrains,i,e) = & - crystallite_partionedLi0(1:3,1:3,1:myNgrains,i,e) + crystallite_Lp(1:3,1:3,1:myNgrains,i,e) = crystallite_partionedLp0(1:3,1:3,1:myNgrains,i,e) + crystallite_Li(1:3,1:3,1:myNgrains,i,e) = crystallite_partionedLi0(1:3,1:3,1:myNgrains,i,e) endif ! maybe protecting everything from overwriting (not only L) makes even more sense - crystallite_Fp(1:3,1:3,1:myNgrains,i,e) = & - crystallite_partionedFp0(1:3,1:3,1:myNgrains,i,e) - crystallite_Fi(1:3,1:3,1:myNgrains,i,e) = & - crystallite_partionedFi0(1:3,1:3,1:myNgrains,i,e) - crystallite_S(1:3,1:3,1:myNgrains,i,e) = & - crystallite_partionedS0(1:3,1:3,1:myNgrains,i,e) + crystallite_Fp(1:3,1:3,1:myNgrains,i,e) = crystallite_partionedFp0(1:3,1:3,1:myNgrains,i,e) + crystallite_Fi(1:3,1:3,1:myNgrains,i,e) = crystallite_partionedFi0(1:3,1:3,1:myNgrains,i,e) + crystallite_S (1:3,1:3,1:myNgrains,i,e) = crystallite_partionedS0 (1:3,1:3,1:myNgrains,i,e) do g = 1, myNgrains plasticState (material_phaseAt(g,e))%state( :,material_phasememberAt(g,i,e)) = & plasticState (material_phaseAt(g,e))%partionedState0(:,material_phasememberAt(g,i,e)) From 6e48585de1bd6a84851cdea20ba490965cd47ff5 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 15 Apr 2020 13:09:05 +0200 Subject: [PATCH 52/53] prefix for local variables not needed --- src/homogenization.f90 | 85 +++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 46 deletions(-) diff --git a/src/homogenization.f90 b/src/homogenization.f90 index 2d3807d18..8b6d80089 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -187,13 +187,13 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) real(pReal), dimension(3,3) :: & subF real(pReal), dimension(discretization_nIP,discretization_nElem) :: & - materialpoint_subFrac, & - materialpoint_subStep + subFrac, & + subStep logical, dimension(discretization_nIP,discretization_nElem) :: & - materialpoint_requested, & - materialpoint_converged + requested, & + converged logical, dimension(2,discretization_nIP,discretization_nElem) :: & - materialpoint_doneAndHappy + doneAndHappy #ifdef DEBUG if (iand(debug_level(debug_homogenization), debug_levelBasic) /= 0) then @@ -229,10 +229,10 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) enddo - materialpoint_subFrac(i,e) = 0.0_pReal - materialpoint_converged(i,e) = .false. ! pretend failed step ... - materialpoint_subStep(i,e) = 1.0_pReal/num%subStepSizeHomog ! ... larger then the requested calculation - materialpoint_requested(i,e) = .true. ! everybody requires calculation + subFrac(i,e) = 0.0_pReal + converged(i,e) = .false. ! pretend failed step ... + subStep(i,e) = 1.0_pReal/num%subStepSizeHomog ! ... larger then the requested calculation + requested(i,e) = .true. ! everybody requires calculation if (homogState(material_homogenizationAt(e))%sizeState > 0) & homogState(material_homogenizationAt(e))%subState0(:,material_homogenizationMemberAt(i,e)) = & @@ -251,31 +251,30 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) NiterationHomog = 0 cutBackLooping: do while (.not. terminallyIll .and. & - any(materialpoint_subStep(:,FEsolving_execELem(1):FEsolving_execElem(2)) > num%subStepMinHomog)) + any(subStep(:,FEsolving_execELem(1):FEsolving_execElem(2)) > num%subStepMinHomog)) !$OMP PARALLEL DO PRIVATE(myNgrains) elementLooping1: do e = FEsolving_execElem(1),FEsolving_execElem(2) myNgrains = homogenization_Ngrains(material_homogenizationAt(e)) IpLooping1: do i = FEsolving_execIP(1),FEsolving_execIP(2) - converged: if (materialpoint_converged(i,e)) then + if (converged(i,e)) then #ifdef DEBUG if (iand(debug_level(debug_homogenization), debug_levelExtensive) /= 0 & .and. ((e == debug_e .and. i == debug_i) & .or. .not. iand(debug_level(debug_homogenization),debug_levelSelective) /= 0)) then write(6,'(a,1x,f12.8,1x,a,1x,f12.8,1x,a,i8,1x,i2/)') '<< HOMOG >> winding forward from', & - materialpoint_subFrac(i,e), 'to current materialpoint_subFrac', & - materialpoint_subFrac(i,e)+materialpoint_subStep(i,e),'in materialpoint_stressAndItsTangent at el ip',e,i + subFrac(i,e), 'to current subFrac', & + subFrac(i,e)+subStep(i,e),'in materialpoint_stressAndItsTangent at el ip',e,i endif #endif !--------------------------------------------------------------------------------------------------- ! calculate new subStep and new subFrac - materialpoint_subFrac(i,e) = materialpoint_subFrac(i,e) + materialpoint_subStep(i,e) - materialpoint_subStep(i,e) = min(1.0_pReal-materialpoint_subFrac(i,e), & - num%stepIncreaseHomog*materialpoint_subStep(i,e)) ! introduce flexibility for step increase/acceleration + subFrac(i,e) = subFrac(i,e) + subStep(i,e) + subStep(i,e) = min(1.0_pReal-subFrac(i,e),num%stepIncreaseHomog*subStep(i,e)) ! introduce flexibility for step increase/acceleration - steppingNeeded: if (materialpoint_subStep(i,e) > num%subStepMinHomog) then + steppingNeeded: if (subStep(i,e) > num%subStepMinHomog) then ! wind forward grain starting point crystallite_partionedF0 (1:3,1:3,1:myNgrains,i,e) = crystallite_partionedF(1:3,1:3,1:myNgrains,i,e) @@ -306,9 +305,9 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) endif steppingNeeded - else converged - if ( (myNgrains == 1 .and. materialpoint_subStep(i,e) <= 1.0 ) .or. & ! single grain already tried internal subStepping in crystallite - num%subStepSizeHomog * materialpoint_subStep(i,e) <= num%subStepMinHomog ) then ! would require too small subStep + else + if ( (myNgrains == 1 .and. subStep(i,e) <= 1.0 ) .or. & ! single grain already tried internal subStepping in crystallite + num%subStepSizeHomog * subStep(i,e) <= num%subStepMinHomog ) then ! would require too small subStep ! cutback makes no sense if (.not. terminallyIll) then ! so first signals terminally ill... !$OMP CRITICAL (write2out) @@ -317,21 +316,21 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) endif terminallyIll = .true. ! ...and kills all others else ! cutback makes sense - materialpoint_subStep(i,e) = num%subStepSizeHomog * materialpoint_subStep(i,e) ! crystallite had severe trouble, so do a significant cutback + subStep(i,e) = num%subStepSizeHomog * subStep(i,e) ! crystallite had severe trouble, so do a significant cutback #ifdef DEBUG if (iand(debug_level(debug_homogenization), debug_levelExtensive) /= 0 & .and. ((e == debug_e .and. i == debug_i) & .or. .not. iand(debug_level(debug_homogenization), debug_levelSelective) /= 0)) then write(6,'(a,1x,f12.8,a,i8,1x,i2/)') & - '<< HOMOG >> cutback step in materialpoint_stressAndItsTangent with new materialpoint_subStep:',& - materialpoint_subStep(i,e),' at el ip',e,i + '<< HOMOG >> cutback step in materialpoint_stressAndItsTangent with new subStep:',& + subStep(i,e),' at el ip',e,i endif #endif !-------------------------------------------------------------------------------------------------- ! restore - if (materialpoint_subStep(i,e) < 1.0_pReal) then ! protect against fake cutback from \Delta t = 2 to 1. Maybe that "trick" is not necessary anymore at all? I.e. start with \Delta t = 1 + if (subStep(i,e) < 1.0_pReal) then ! protect against fake cutback from \Delta t = 2 to 1. Maybe that "trick" is not necessary anymore at all? I.e. start with \Delta t = 1 crystallite_Lp(1:3,1:3,1:myNgrains,i,e) = crystallite_partionedLp0(1:3,1:3,1:myNgrains,i,e) crystallite_Li(1:3,1:3,1:myNgrains,i,e) = crystallite_partionedLi0(1:3,1:3,1:myNgrains,i,e) endif ! maybe protecting everything from overwriting (not only L) makes even more sense @@ -356,11 +355,11 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) damageState(material_homogenizationAt(e))%State( :,material_homogenizationMemberAt(i,e)) = & damageState(material_homogenizationAt(e))%subState0(:,material_homogenizationMemberAt(i,e)) endif - endif converged + endif - if (materialpoint_subStep(i,e) > num%subStepMinHomog) then - materialpoint_requested(i,e) = .true. - materialpoint_doneAndHappy(1:2,i,e) = [.false.,.true.] + if (subStep(i,e) > num%subStepMinHomog) then + requested(i,e) = .true. + doneAndHappy(1:2,i,e) = [.false.,.true.] endif enddo IpLooping1 enddo elementLooping1 @@ -369,8 +368,8 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) NiterationMPstate = 0 convergenceLooping: do while (.not. terminallyIll .and. & - any( materialpoint_requested(:,FEsolving_execELem(1):FEsolving_execElem(2)) & - .and. .not. materialpoint_doneAndHappy(1,:,FEsolving_execELem(1):FEsolving_execElem(2)) & + any( requested(:,FEsolving_execELem(1):FEsolving_execElem(2)) & + .and. .not. doneAndHappy(1,:,FEsolving_execELem(1):FEsolving_execElem(2)) & ) .and. & NiterationMPstate < num%nMPstate) NiterationMPstate = NiterationMPstate + 1 @@ -383,13 +382,11 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) elementLooping2: do e = FEsolving_execElem(1),FEsolving_execElem(2) myNgrains = homogenization_Ngrains(material_homogenizationAt(e)) IpLooping2: do i = FEsolving_execIP(1),FEsolving_execIP(2) - if ( materialpoint_requested(i,e) .and. & ! process requested but... - .not. materialpoint_doneAndHappy(1,i,e)) then ! ...not yet done material points + if(requested(i,e) .and. .not. doneAndHappy(1,i,e)) then ! requested but not yet done subF = materialpoint_F0(1:3,1:3,i,e) & - + (materialpoint_F(1:3,1:3,i,e) - materialpoint_F0(1:3,1:3,i,e)) & - * (materialpoint_subStep(i,e)+materialpoint_subFrac(i,e)) + + (materialpoint_F(1:3,1:3,i,e)-materialpoint_F0(1:3,1:3,i,e))*(subStep(i,e)+subFrac(i,e)) call partitionDeformation(subF,i,e) ! partition deformation onto constituents - crystallite_dt(1:myNgrains,i,e) = dt*materialpoint_subStep(i,e) ! propagate materialpoint dt to grains + crystallite_dt(1:myNgrains,i,e) = dt*subStep(i,e) ! propagate materialpoint dt to grains crystallite_requested(1:myNgrains,i,e) = .true. ! request calculation for constituents else crystallite_requested(1:myNgrains,i,e) = .false. ! calculation for constituents not required anymore @@ -403,25 +400,21 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) ! based on crystallite_partionedF0,.._partionedF ! incrementing by crystallite_dt - materialpoint_converged = crystallite_stress() !ToDo: MD not sure if that is the best logic + converged = crystallite_stress() !ToDo: MD not sure if that is the best logic !-------------------------------------------------------------------------------------------------- ! state update !$OMP PARALLEL DO PRIVATE(subF) elementLooping3: do e = FEsolving_execElem(1),FEsolving_execElem(2) IpLooping3: do i = FEsolving_execIP(1),FEsolving_execIP(2) - if ( materialpoint_requested(i,e) .and. & - .not. materialpoint_doneAndHappy(1,i,e)) then - if (.not. materialpoint_converged(i,e)) then - materialpoint_doneAndHappy(1:2,i,e) = [.true.,.false.] + if (requested(i,e) .and. .not. doneAndHappy(1,i,e)) then + if (.not. converged(i,e)) then + doneAndHappy(1:2,i,e) = [.true.,.false.] else subF = materialpoint_F0(1:3,1:3,i,e) & - + (materialpoint_F(1:3,1:3,i,e) - materialpoint_F0(1:3,1:3,i,e)) & - * (materialpoint_subStep(i,e)+materialpoint_subFrac(i,e)) - materialpoint_doneAndHappy(1:2,i,e) = updateState(dt*materialpoint_subStep(i,e), & - subF, & - i,e) - materialpoint_converged(i,e) = all(materialpoint_doneAndHappy(1:2,i,e)) ! converged if done and happy + + (materialpoint_F(1:3,1:3,i,e)-materialpoint_F0(1:3,1:3,i,e))*(subStep(i,e)+subFrac(i,e)) + doneAndHappy(1:2,i,e) = updateState(dt*subStep(i,e),subF,i,e) + converged(i,e) = all(doneAndHappy(1:2,i,e)) ! converged if done and happy endif endif enddo IpLooping3 From 875b3b632165c6db9fc89aeb238774ca0fdbe9b4 Mon Sep 17 00:00:00 2001 From: Test User Date: Wed, 15 Apr 2020 19:00:06 +0200 Subject: [PATCH 53/53] [skip ci] updated version information after successful test of v2.0.3-2303-g2a6132b7 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 458484abe..7be7a7f6c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.3-2291-g03aa6f9c +v2.0.3-2303-g2a6132b7