From ea763fd941e86e979fbc82aba1ce3b313d6d54f7 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Sat, 27 Feb 2021 11:15:01 -0500 Subject: [PATCH 1/3] generalized stereographic projection to cope with all three directions (x,y,Z) --- python/damask/util.py | 15 ++++++++++----- python/tests/test_util.py | 18 +++++++++--------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/python/damask/util.py b/python/damask/util.py index cda532bc0..6f148c418 100644 --- a/python/damask/util.py +++ b/python/damask/util.py @@ -193,7 +193,7 @@ def scale_to_coprime(v): return m -def project_stereographic(vector,normalize=False): +def project_stereographic(vector,normalize=False,direction='z'): """ Apply stereographic projection to vector. @@ -203,16 +203,21 @@ def project_stereographic(vector,normalize=False): Vector coordinates to be projected. normalize : bool Ensure unit length for vector. Defaults to False. + direction : str or int + Projection direction 'x'|0, 'y'|1, or 'z'|2. + Defaults to 'z'. Returns ------- - coordinates : numpy.ndarray of shape (...,2) + coordinates : numpy.ndarray of shape (...,3) Projected coordinates. """ - v_ = vector/np.linalg.norm(vector,axis=-1,keepdims=True) if normalize else vector - return np.block([v_[...,:2]/(1+np.abs(v_[...,2:3])), - np.zeros_like(v_[...,2:3])]) + shift = 2-('xyzXYZ'.index(direction)%3 if isinstance(direction,str) else int(direction)) + v_ = np.roll(vector/np.linalg.norm(vector,axis=-1,keepdims=True) if normalize else vector, + shift,axis=-1) + return np.roll(np.block([v_[...,:2]/(1+np.abs(v_[...,2:3])),np.zeros_like(v_[...,2:3])]), + -shift,axis=-1) def execution_stamp(class_name,function_name=None): diff --git a/python/tests/test_util.py b/python/tests/test_util.py index eb1084b09..3b0052070 100644 --- a/python/tests/test_util.py +++ b/python/tests/test_util.py @@ -49,17 +49,17 @@ class TestUtil: dist_sampled = np.histogram(centers[selected],bins)[0]/N_samples*np.sum(dist) assert np.sqrt(((dist - dist_sampled) ** 2).mean()) < .025 and selected.shape[0]==N_samples - @pytest.mark.parametrize('point,normalize,answer', + @pytest.mark.parametrize('point,normalize,direction,answer', [ - ([1,0,0],False,[1,0,0]), - ([1,0,0],True, [1,0,0]), - ([0,1,1],False,[0,0.5,0]), - ([0,1,1],True, [0,0.41421356,0]), - ([1,1,1],False,[0.5,0.5,0]), - ([1,1,1],True, [0.3660254, 0.3660254, 0]), + ([1,0,0],False,'z',[1,0,0]), + ([1,0,0],True, 'z',[1,0,0]), + ([0,1,1],False,'z',[0,0.5,0]), + ([0,1,1],True, 'y',[0,0,0.41421356]), + ([1,1,1],False,'x',[0,0.5,0.5]), + ([1,1,1],True, 'y',[0.3660254, 0,0.3660254]), ]) - def test_project_stereographic(self,point,normalize,answer): - assert np.allclose(util.project_stereographic(np.array(point),normalize=normalize),answer) + def test_project_stereographic(self,point,normalize,direction,answer): + assert np.allclose(util.project_stereographic(np.array(point),normalize=normalize,direction=direction),answer) @pytest.mark.parametrize('fro,to,mode,answer', [ From 175d724dedf9b05c176ee0c7d828fe825d163bd4 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Sat, 27 Feb 2021 18:32:53 -0500 Subject: [PATCH 2/3] added keepdims=False option to project_stereographic --- python/damask/util.py | 26 ++++++++++++++++++++------ python/tests/test_util.py | 19 ++++++++++--------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/python/damask/util.py b/python/damask/util.py index 6f148c418..951eb921e 100644 --- a/python/damask/util.py +++ b/python/damask/util.py @@ -193,7 +193,7 @@ def scale_to_coprime(v): return m -def project_stereographic(vector,normalize=False,direction='z'): +def project_stereographic(vector,direction='z',normalize=True,keepdims=False): """ Apply stereographic projection to vector. @@ -201,23 +201,37 @@ def project_stereographic(vector,normalize=False,direction='z'): ---------- vector : numpy.ndarray of shape (...,3) Vector coordinates to be projected. - normalize : bool - Ensure unit length for vector. Defaults to False. direction : str or int - Projection direction 'x'|0, 'y'|1, or 'z'|2. + Projection direction 'x' | 0, 'y' | 1, or 'z' | 2. Defaults to 'z'. + normalize : bool + Ensure unit length of input vector. Defaults to True. + keepdims : bool + Maintain three-dimensional output coordinates. + Default two-dimensional output uses right-handed frame spanned by + the next and next-next axis relative to the projection direction, + e.g. x-y when projecting along z and z-x when projecting along y. Returns ------- - coordinates : numpy.ndarray of shape (...,3) + coordinates : numpy.ndarray of shape (...,2 | 3) Projected coordinates. + Examples + -------- + >>> project_stereographic(np.ones(3)) + [0.3660254, 0.3660254] + >>> project_stereographic(np.ones(3),direction='x',normalize=False,keepdims=True) + [0, 0.5, 0.5] + >>> project_stereographic([0,1,1],direction='y',normalize=True,keepdims=False) + [0.41421356, 0] + """ shift = 2-('xyzXYZ'.index(direction)%3 if isinstance(direction,str) else int(direction)) v_ = np.roll(vector/np.linalg.norm(vector,axis=-1,keepdims=True) if normalize else vector, shift,axis=-1) return np.roll(np.block([v_[...,:2]/(1+np.abs(v_[...,2:3])),np.zeros_like(v_[...,2:3])]), - -shift,axis=-1) + -shift if keepdims else 0,axis=-1)[...,:3 if keepdims else 2] def execution_stamp(class_name,function_name=None): diff --git a/python/tests/test_util.py b/python/tests/test_util.py index 3b0052070..397926682 100644 --- a/python/tests/test_util.py +++ b/python/tests/test_util.py @@ -49,17 +49,18 @@ class TestUtil: dist_sampled = np.histogram(centers[selected],bins)[0]/N_samples*np.sum(dist) assert np.sqrt(((dist - dist_sampled) ** 2).mean()) < .025 and selected.shape[0]==N_samples - @pytest.mark.parametrize('point,normalize,direction,answer', + @pytest.mark.parametrize('point,direction,normalize,keepdims,answer', [ - ([1,0,0],False,'z',[1,0,0]), - ([1,0,0],True, 'z',[1,0,0]), - ([0,1,1],False,'z',[0,0.5,0]), - ([0,1,1],True, 'y',[0,0,0.41421356]), - ([1,1,1],False,'x',[0,0.5,0.5]), - ([1,1,1],True, 'y',[0.3660254, 0,0.3660254]), + ([1,0,0],'z',False,True, [1,0,0]), + ([1,0,0],'z',True, False,[1,0]), + ([0,1,1],'z',False,True, [0,0.5,0]), + ([0,1,1],'y',True, False,[0.41421356,0]), + ([1,1,0],'x',False,False,[0.5,0]), + ([1,1,1],'y',True, True, [0.3660254, 0,0.3660254]), ]) - def test_project_stereographic(self,point,normalize,direction,answer): - assert np.allclose(util.project_stereographic(np.array(point),normalize=normalize,direction=direction),answer) + def test_project_stereographic(self,point,direction,normalize,keepdims,answer): + assert np.allclose(util.project_stereographic(np.array(point),direction=direction, + normalize=normalize,keepdims=keepdims),answer) @pytest.mark.parametrize('fro,to,mode,answer', [ From 464c62e7e707468e598bb47396b18965331bd7fc Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Sat, 27 Feb 2021 18:46:20 -0500 Subject: [PATCH 3/3] abandoned integer aliases for projection directions --- python/damask/util.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/damask/util.py b/python/damask/util.py index 951eb921e..61ec5e5cb 100644 --- a/python/damask/util.py +++ b/python/damask/util.py @@ -201,8 +201,8 @@ def project_stereographic(vector,direction='z',normalize=True,keepdims=False): ---------- vector : numpy.ndarray of shape (...,3) Vector coordinates to be projected. - direction : str or int - Projection direction 'x' | 0, 'y' | 1, or 'z' | 2. + direction : str + Projection direction 'x', 'y', or 'z'. Defaults to 'z'. normalize : bool Ensure unit length of input vector. Defaults to True. @@ -227,7 +227,7 @@ def project_stereographic(vector,direction='z',normalize=True,keepdims=False): [0.41421356, 0] """ - shift = 2-('xyzXYZ'.index(direction)%3 if isinstance(direction,str) else int(direction)) + shift = 'zyx'.index(direction) v_ = np.roll(vector/np.linalg.norm(vector,axis=-1,keepdims=True) if normalize else vector, shift,axis=-1) return np.roll(np.block([v_[...,:2]/(1+np.abs(v_[...,2:3])),np.zeros_like(v_[...,2:3])]),