From ea763fd941e86e979fbc82aba1ce3b313d6d54f7 Mon Sep 17 00:00:00 2001
From: Philip Eisenlohr <eisenlohr@egr.msu.edu>
Date: Sat, 27 Feb 2021 11:15:01 -0500
Subject: [PATCH 1/4] 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 <eisenlohr@egr.msu.edu>
Date: Sat, 27 Feb 2021 18:32:53 -0500
Subject: [PATCH 2/4] 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 <eisenlohr@egr.msu.edu>
Date: Sat, 27 Feb 2021 18:46:20 -0500
Subject: [PATCH 3/4] 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])]),

From 1be6950c0b9d66e9f30b4e8ae4da786c772e5066 Mon Sep 17 00:00:00 2001
From: Test User <damask_user@mpie.de>
Date: Sun, 28 Feb 2021 16:32:50 +0100
Subject: [PATCH 4/4] [skip ci] updated version information after successful
 test of v3.0.0-alpha2-553-g2691ef211

---
 VERSION | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/VERSION b/VERSION
index 83ef4eefa..e41ad05a9 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v3.0.0-alpha2-548-gc483dc609
+v3.0.0-alpha2-553-g2691ef211