From 0c41e334341da15de6033709f05657474b7bf576 Mon Sep 17 00:00:00 2001 From: "f.basile" Date: Thu, 4 Jun 2020 16:39:24 +0200 Subject: [PATCH 01/78] orientation.equivalent takes several rotations at the same time + small test" " "" --- python/damask/_orientation.py | 18 ++++++++++++++++-- python/damask/oritest.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 python/damask/oritest.py diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index e4dbc7d7c..6fa40c261 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -39,8 +39,8 @@ class Orientation: else: self.rotation = Rotation.from_quaternion(rotation) # assume quaternion - if self.rotation.quaternion.shape != (4,): - raise NotImplementedError('Support for multiple rotations missing') +# if self.rotation.quaternion.shape != (4,): +# raise NotImplementedError('Support for multiple rotations missing') def disorientation(self, other, @@ -80,6 +80,20 @@ class Orientation: def inFZ(self): return self.lattice.symmetry.inFZ(self.rotation.as_Rodrigues(vector=True)) + def equivalent(self): + """ + List of orientations which are symmetrically equivalent. + Supported for multiple rotation with same lattice + Returns list [i] being i=range(24) + Returns list [i, num_rot] for multiple rotations + """ + if not self.rotation.shape: + return [self.__class__(q*self.rotation,self.lattice) \ + for q in self.lattice.symmetry.symmetryOperations()] + else: + return np.reshape([self.__class__(q*Rotation.from_quaternion(self.rotation.as_quaternion()[l]),self.lattice) \ + for q in self.lattice.symmetry.symmetryOperations() for l in range(self.rotation.shape[0])], (24,self.rotation.shape[0])) + def equivalentOrientations(self,members=[]): """List of orientations which are symmetrically equivalent.""" diff --git a/python/damask/oritest.py b/python/damask/oritest.py new file mode 100644 index 000000000..43104a938 --- /dev/null +++ b/python/damask/oritest.py @@ -0,0 +1,33 @@ +import damask +import numpy as np + + +rot0= damask.Rotation.from_random() +rot1= damask.Rotation.from_random() +rot2= damask.Rotation.from_random() + +ori0=damask.Orientation(rot0,'fcc') +ori1=damask.Orientation(rot1,'fcc') +ori2=damask.Orientation(rot2,'fcc') + + + + +quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),rot2.as_quaternion()]) +rot=damask.Rotation.from_quaternion(quat) + +ori=damask.Orientation(rot,'fcc') + +ori.equivalent() + + + +# doesn't work this way, don't know why +#ori.equivalent()[:,0][0] == ori0.equivalentOrientations()[0] + +for s in range(24): + print(ori.equivalent()[s,0].rotation.as_Eulers() == ori0.equivalentOrientations()[s].rotation.as_Eulers()) + print(ori.equivalent()[s,1].rotation.as_Eulers() == ori1.equivalentOrientations()[s].rotation.as_Eulers()) + print(ori.equivalent()[s,2].rotation.as_Eulers() == ori2.equivalentOrientations()[s].rotation.as_Eulers()) + + From 3897136f85884c745a3e645a93a9fae136db317d Mon Sep 17 00:00:00 2001 From: "f.basile" Date: Thu, 4 Jun 2020 16:43:28 +0200 Subject: [PATCH 02/78] avoid python/damask/_orientation.py exceeds line length limit (maximum line length 141 > 132) --- python/damask/_orientation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 6fa40c261..909365d41 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -92,7 +92,8 @@ class Orientation: for q in self.lattice.symmetry.symmetryOperations()] else: return np.reshape([self.__class__(q*Rotation.from_quaternion(self.rotation.as_quaternion()[l]),self.lattice) \ - for q in self.lattice.symmetry.symmetryOperations() for l in range(self.rotation.shape[0])], (24,self.rotation.shape[0])) + for q in self.lattice.symmetry.symmetryOperations() \ + for l in range(self.rotation.shape[0])], (24,self.rotation.shape[0])) def equivalentOrientations(self,members=[]): From 6a24aee171122600a26a8d65c0c1acbd06741ee6 Mon Sep 17 00:00:00 2001 From: "f.basile" Date: Thu, 4 Jun 2020 16:44:57 +0200 Subject: [PATCH 03/78] fix python/damask/_orientation.py contains invalid python3 code --- python/damask/_orientation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 909365d41..f338be35d 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -82,10 +82,12 @@ class Orientation: def equivalent(self): """ + List of orientations which are symmetrically equivalent. Supported for multiple rotation with same lattice Returns list [i] being i=range(24) Returns list [i, num_rot] for multiple rotations + """ if not self.rotation.shape: return [self.__class__(q*self.rotation,self.lattice) \ From ac09a2912a434bc6f2289eed849b7bf1eaa43429 Mon Sep 17 00:00:00 2001 From: "f.basile" Date: Thu, 4 Jun 2020 16:45:44 +0200 Subject: [PATCH 04/78] fix python/damask/_orientation.py contains invalid python3 code 2 --- python/damask/_orientation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index f338be35d..ec83c3857 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -82,8 +82,8 @@ class Orientation: def equivalent(self): """ - List of orientations which are symmetrically equivalent. + Supported for multiple rotation with same lattice Returns list [i] being i=range(24) Returns list [i, num_rot] for multiple rotations From eae9698d22c527502d5a8cefd05d50a8520479bb Mon Sep 17 00:00:00 2001 From: "f.basile" Date: Fri, 5 Jun 2020 13:48:12 +0200 Subject: [PATCH 05/78] equivalent,related and inFZ vectorized + pytest --- python/damask/_orientation.py | 44 +++++++++++++---- python/damask/oritest.py | 33 ------------- python/tests/test_ori_vec.py | 93 +++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 43 deletions(-) delete mode 100644 python/damask/oritest.py create mode 100644 python/tests/test_ori_vec.py diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index ec83c3857..245659f44 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -77,25 +77,37 @@ class Orientation: return (Orientation(r,self.lattice), i,j, k == 1) if symmetries else r # disorientation ... # ... own sym, other sym, # self-->other: True, self<--other: False + + def inFZ_vec(self): + """ + Check if orientations falls into Fundamental Zone + + self.rotation.as_Rodrigues() working fine + self.rotation.as_Rodrigues(vector=True) doesn't work for several rotations + i apply dirty fix + + """ + if not self.rotation.shape: + return self.lattice.symmetry.inFZ(self.rotation.as_Rodrigues(vector=True)) + else: + return [self.lattice.symmetry.inFZ(\ + Rotation._qu2ro(self.rotation.as_quaternion())[l][...,:3]\ + *Rotation._qu2ro(self.rotation.as_quaternion())[l][...,3])\ + for l in range(self.rotation.shape[0])] + def inFZ(self): return self.lattice.symmetry.inFZ(self.rotation.as_Rodrigues(vector=True)) - def equivalent(self): - """ - List of orientations which are symmetrically equivalent. - - Supported for multiple rotation with same lattice - Returns list [i] being i=range(24) - Returns list [i, num_rot] for multiple rotations - - """ + def equivalent_vec(self): + """List of orientations which are symmetrically equivalent.""" if not self.rotation.shape: return [self.__class__(q*self.rotation,self.lattice) \ for q in self.lattice.symmetry.symmetryOperations()] else: return np.reshape([self.__class__(q*Rotation.from_quaternion(self.rotation.as_quaternion()[l]),self.lattice) \ for q in self.lattice.symmetry.symmetryOperations() \ - for l in range(self.rotation.shape[0])], (24,self.rotation.shape[0])) + for l in range(self.rotation.shape[0])], \ + (len(self.lattice.symmetry.symmetryOperations()),self.rotation.shape[0])) def equivalentOrientations(self,members=[]): @@ -108,6 +120,18 @@ class Orientation: return [self.__class__(q*self.rotation,self.lattice) \ for q in self.lattice.symmetry.symmetryOperations(members)] # yes, return list of rotations + def relatedOrientations_vec(self,model): + """List of orientations related by the given orientation relationship.""" + r = self.lattice.relationOperations(model) + if not self.rotation.shape: + return [self.__class__(o*self.rotation,r['lattice']) for o in r['rotations']] + else: + return np.reshape(\ + [self.__class__(o*Rotation.from_quaternion(self.rotation.as_quaternion()[l])\ + ,r['lattice']) for o in r['rotations'] for l in range(self.rotation.shape[0])] + ,(len(r['rotations']),self.rotation.shape[0])) + + def relatedOrientations(self,model): """List of orientations related by the given orientation relationship.""" r = self.lattice.relationOperations(model) diff --git a/python/damask/oritest.py b/python/damask/oritest.py deleted file mode 100644 index 43104a938..000000000 --- a/python/damask/oritest.py +++ /dev/null @@ -1,33 +0,0 @@ -import damask -import numpy as np - - -rot0= damask.Rotation.from_random() -rot1= damask.Rotation.from_random() -rot2= damask.Rotation.from_random() - -ori0=damask.Orientation(rot0,'fcc') -ori1=damask.Orientation(rot1,'fcc') -ori2=damask.Orientation(rot2,'fcc') - - - - -quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),rot2.as_quaternion()]) -rot=damask.Rotation.from_quaternion(quat) - -ori=damask.Orientation(rot,'fcc') - -ori.equivalent() - - - -# doesn't work this way, don't know why -#ori.equivalent()[:,0][0] == ori0.equivalentOrientations()[0] - -for s in range(24): - print(ori.equivalent()[s,0].rotation.as_Eulers() == ori0.equivalentOrientations()[s].rotation.as_Eulers()) - print(ori.equivalent()[s,1].rotation.as_Eulers() == ori1.equivalentOrientations()[s].rotation.as_Eulers()) - print(ori.equivalent()[s,2].rotation.as_Eulers() == ori2.equivalentOrientations()[s].rotation.as_Eulers()) - - diff --git a/python/tests/test_ori_vec.py b/python/tests/test_ori_vec.py new file mode 100644 index 000000000..ffc2c88b1 --- /dev/null +++ b/python/tests/test_ori_vec.py @@ -0,0 +1,93 @@ +import os +from itertools import permutations + +import pytest +import numpy as np + +import damask +from damask import Rotation +from damask import Orientation +from damask import Lattice + +rot0= damask.Rotation.from_random() +rot1= damask.Rotation.from_random() +rot2= damask.Rotation.from_random() +rot3= damask.Rotation.from_random() + +class TestOrientation_vec: + @pytest.mark.parametrize('lattice',Lattice.lattices) + def test_equivalentOrientations_vec(self,lattice): + ori0=damask.Orientation(rot0,lattice) + ori1=damask.Orientation(rot1,lattice) + ori2=damask.Orientation(rot2,lattice) + ori3=damask.Orientation(rot3,lattice) + + quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),rot2.as_quaternion(),rot3.as_quaternion()]) + rot_vec=damask.Rotation.from_quaternion(quat) + ori_vec=damask.Orientation(rot_vec,lattice) + + for s in range(len(ori_vec.lattice.symmetry.symmetryOperations())): + assert all(ori_vec.equivalent_vec()[s,0].rotation.as_Eulers() == \ + ori0.equivalentOrientations()[s].rotation.as_Eulers()) + assert all(ori_vec.equivalent_vec()[s,1].rotation.as_quaternion() == \ + ori1.equivalentOrientations()[s].rotation.as_quaternion()) + assert all(ori_vec.equivalent_vec()[s,2].rotation.as_Rodrigues() == \ + ori2.equivalentOrientations()[s].rotation.as_Rodrigues()) + assert all(ori_vec.equivalent_vec()[s,3].rotation.as_cubochoric() == \ + ori3.equivalentOrientations()[s].rotation.as_cubochoric()) + + @pytest.mark.parametrize('lattice',Lattice.lattices) + def test_inFZ_vec(self,lattice): + ori0=damask.Orientation(rot0,lattice) + ori1=damask.Orientation(rot1,lattice) + ori2=damask.Orientation(rot2,lattice) + ori3=damask.Orientation(rot3,lattice) + #ensure 1 of them is in FZ + ori4=ori0.reduced() + rot4=ori4.rotation + + quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),\ + rot2.as_quaternion(),rot3.as_quaternion(), rot4.as_quaternion()]) + rot_vec=damask.Rotation.from_quaternion(quat) + ori_vec=damask.Orientation(rot_vec,lattice) + + assert ori_vec.inFZ_vec()[0] == ori0.inFZ() + assert ori_vec.inFZ_vec()[1] == ori1.inFZ() + assert ori_vec.inFZ_vec()[2] == ori2.inFZ() + assert ori_vec.inFZ_vec()[3] == ori3.inFZ() + assert ori_vec.inFZ_vec()[4] == ori4.inFZ() + + + @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) + @pytest.mark.parametrize('lattice',['fcc','bcc']) + def test_relatedOrientations_vec(self,model,lattice): + ori0=damask.Orientation(rot0,lattice) + ori1=damask.Orientation(rot1,lattice) + ori2=damask.Orientation(rot2,lattice) + ori3=damask.Orientation(rot3,lattice) + + quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),rot2.as_quaternion(),rot3.as_quaternion()]) + rot_vec=damask.Rotation.from_quaternion(quat) + ori_vec=damask.Orientation(rot_vec,lattice) + + for s in range(len(ori1.lattice.relationOperations(model)['rotations'])): + assert all(ori_vec.relatedOrientations_vec(model)[s,0].rotation.as_Eulers() == \ + ori0.relatedOrientations(model)[s].rotation.as_Eulers()) + assert all(ori_vec.relatedOrientations_vec(model)[s,1].rotation.as_quaternion() == \ + ori1.relatedOrientations(model)[s].rotation.as_quaternion()) + assert all(ori_vec.relatedOrientations_vec(model)[s,2].rotation.as_Rodrigues() == \ + ori2.relatedOrientations(model)[s].rotation.as_Rodrigues()) + assert all(ori_vec.relatedOrientations_vec(model)[s,3].rotation.as_cubochoric() == \ + ori3.relatedOrientations(model)[s].rotation.as_cubochoric()) + + + + + + + + + + + + \ No newline at end of file From cb1779ef9adf66b9c693473e79dbeea0ad372e53 Mon Sep 17 00:00:00 2001 From: "f.basile" Date: Fri, 5 Jun 2020 13:49:30 +0200 Subject: [PATCH 06/78] fix pep257: D415 / First line should end with a period, question mark, or exclamation point (not e) --- python/damask/_orientation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 245659f44..10d33a76c 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -80,7 +80,7 @@ class Orientation: def inFZ_vec(self): """ - Check if orientations falls into Fundamental Zone + Check if orientations falls into Fundamental Zone. self.rotation.as_Rodrigues() working fine self.rotation.as_Rodrigues(vector=True) doesn't work for several rotations From 54c20cdd3c8cc7a3b3ff3a9a84a507619d7231ec Mon Sep 17 00:00:00 2001 From: "f.basile" Date: Fri, 5 Jun 2020 13:53:47 +0200 Subject: [PATCH 07/78] fix pyflakes imported but unused in pytest --- python/tests/test_ori_vec.py | 47 +++++++++++++++++------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/python/tests/test_ori_vec.py b/python/tests/test_ori_vec.py index ffc2c88b1..4172e5edc 100644 --- a/python/tests/test_ori_vec.py +++ b/python/tests/test_ori_vec.py @@ -1,6 +1,3 @@ -import os -from itertools import permutations - import pytest import numpy as np @@ -9,22 +6,22 @@ from damask import Rotation from damask import Orientation from damask import Lattice -rot0= damask.Rotation.from_random() -rot1= damask.Rotation.from_random() -rot2= damask.Rotation.from_random() -rot3= damask.Rotation.from_random() +rot0= Rotation.from_random() +rot1= Rotation.from_random() +rot2= Rotation.from_random() +rot3= Rotation.from_random() class TestOrientation_vec: @pytest.mark.parametrize('lattice',Lattice.lattices) def test_equivalentOrientations_vec(self,lattice): - ori0=damask.Orientation(rot0,lattice) - ori1=damask.Orientation(rot1,lattice) - ori2=damask.Orientation(rot2,lattice) - ori3=damask.Orientation(rot3,lattice) + ori0=Orientation(rot0,lattice) + ori1=Orientation(rot1,lattice) + ori2=Orientation(rot2,lattice) + ori3=Orientation(rot3,lattice) quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),rot2.as_quaternion(),rot3.as_quaternion()]) - rot_vec=damask.Rotation.from_quaternion(quat) - ori_vec=damask.Orientation(rot_vec,lattice) + rot_vec=Rotation.from_quaternion(quat) + ori_vec=Orientation(rot_vec,lattice) for s in range(len(ori_vec.lattice.symmetry.symmetryOperations())): assert all(ori_vec.equivalent_vec()[s,0].rotation.as_Eulers() == \ @@ -38,18 +35,18 @@ class TestOrientation_vec: @pytest.mark.parametrize('lattice',Lattice.lattices) def test_inFZ_vec(self,lattice): - ori0=damask.Orientation(rot0,lattice) - ori1=damask.Orientation(rot1,lattice) - ori2=damask.Orientation(rot2,lattice) - ori3=damask.Orientation(rot3,lattice) + ori0=Orientation(rot0,lattice) + ori1=Orientation(rot1,lattice) + ori2=Orientation(rot2,lattice) + ori3=Orientation(rot3,lattice) #ensure 1 of them is in FZ ori4=ori0.reduced() rot4=ori4.rotation quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),\ rot2.as_quaternion(),rot3.as_quaternion(), rot4.as_quaternion()]) - rot_vec=damask.Rotation.from_quaternion(quat) - ori_vec=damask.Orientation(rot_vec,lattice) + rot_vec=Rotation.from_quaternion(quat) + ori_vec=Orientation(rot_vec,lattice) assert ori_vec.inFZ_vec()[0] == ori0.inFZ() assert ori_vec.inFZ_vec()[1] == ori1.inFZ() @@ -61,14 +58,14 @@ class TestOrientation_vec: @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) @pytest.mark.parametrize('lattice',['fcc','bcc']) def test_relatedOrientations_vec(self,model,lattice): - ori0=damask.Orientation(rot0,lattice) - ori1=damask.Orientation(rot1,lattice) - ori2=damask.Orientation(rot2,lattice) - ori3=damask.Orientation(rot3,lattice) + ori0=Orientation(rot0,lattice) + ori1=Orientation(rot1,lattice) + ori2=Orientation(rot2,lattice) + ori3=Orientation(rot3,lattice) quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),rot2.as_quaternion(),rot3.as_quaternion()]) - rot_vec=damask.Rotation.from_quaternion(quat) - ori_vec=damask.Orientation(rot_vec,lattice) + rot_vec=Rotation.from_quaternion(quat) + ori_vec=Orientation(rot_vec,lattice) for s in range(len(ori1.lattice.relationOperations(model)['rotations'])): assert all(ori_vec.relatedOrientations_vec(model)[s,0].rotation.as_Eulers() == \ From a76b5233be7639b6ed3d82d34e48d88214ecd835 Mon Sep 17 00:00:00 2001 From: "f.basile" Date: Fri, 5 Jun 2020 13:54:38 +0200 Subject: [PATCH 08/78] fix pyflakes imported but unused in pytest 2 --- python/tests/test_ori_vec.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/tests/test_ori_vec.py b/python/tests/test_ori_vec.py index 4172e5edc..f55dc6d03 100644 --- a/python/tests/test_ori_vec.py +++ b/python/tests/test_ori_vec.py @@ -1,7 +1,6 @@ import pytest import numpy as np -import damask from damask import Rotation from damask import Orientation from damask import Lattice From fe5e5babfea196b4d95e7ff491b3aec18cae872b Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 18 Jun 2020 22:29:28 +0200 Subject: [PATCH 09/78] more useful for vectorized calculations --- python/damask/_lattice.py | 71 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/python/damask/_lattice.py b/python/damask/_lattice.py index 0f6cffd04..09eadee5a 100644 --- a/python/damask/_lattice.py +++ b/python/damask/_lattice.py @@ -13,7 +13,7 @@ class Symmetry: """ - lattices = [None,'orthorhombic','tetragonal','hexagonal','cubic',] + lattices = [None,'orthorhombic','tetragonal','hexagonal','cubic'] def __init__(self, symmetry = None): """ @@ -157,6 +157,75 @@ class Symmetry: else: return symOps # yes, return list of rotations + @property + def symmetry_operations(self): + """List (or single element) of symmetry operations as rotations.""" + if self.lattice == 'cubic': + symQuats = [ + [ 1.0, 0.0, 0.0, 0.0 ], + [ 0.0, 1.0, 0.0, 0.0 ], + [ 0.0, 0.0, 1.0, 0.0 ], + [ 0.0, 0.0, 0.0, 1.0 ], + [ 0.0, 0.0, 0.5*np.sqrt(2), 0.5*np.sqrt(2) ], + [ 0.0, 0.0, 0.5*np.sqrt(2),-0.5*np.sqrt(2) ], + [ 0.0, 0.5*np.sqrt(2), 0.0, 0.5*np.sqrt(2) ], + [ 0.0, 0.5*np.sqrt(2), 0.0, -0.5*np.sqrt(2) ], + [ 0.0, 0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0 ], + [ 0.0, -0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0 ], + [ 0.5, 0.5, 0.5, 0.5 ], + [-0.5, 0.5, 0.5, 0.5 ], + [-0.5, 0.5, 0.5, -0.5 ], + [-0.5, 0.5, -0.5, 0.5 ], + [-0.5, -0.5, 0.5, 0.5 ], + [-0.5, -0.5, 0.5, -0.5 ], + [-0.5, -0.5, -0.5, 0.5 ], + [-0.5, 0.5, -0.5, -0.5 ], + [-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], + [ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], + [-0.5*np.sqrt(2), 0.0, 0.5*np.sqrt(2), 0.0 ], + [-0.5*np.sqrt(2), 0.0, -0.5*np.sqrt(2), 0.0 ], + [-0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0, 0.0 ], + [-0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0, 0.0 ], + ] + elif self.lattice == 'hexagonal': + symQuats = [ + [ 1.0, 0.0, 0.0, 0.0 ], + [-0.5*np.sqrt(3), 0.0, 0.0, -0.5 ], + [ 0.5, 0.0, 0.0, 0.5*np.sqrt(3) ], + [ 0.0, 0.0, 0.0, 1.0 ], + [-0.5, 0.0, 0.0, 0.5*np.sqrt(3) ], + [-0.5*np.sqrt(3), 0.0, 0.0, 0.5 ], + [ 0.0, 1.0, 0.0, 0.0 ], + [ 0.0, -0.5*np.sqrt(3), 0.5, 0.0 ], + [ 0.0, 0.5, -0.5*np.sqrt(3), 0.0 ], + [ 0.0, 0.0, 1.0, 0.0 ], + [ 0.0, -0.5, -0.5*np.sqrt(3), 0.0 ], + [ 0.0, 0.5*np.sqrt(3), 0.5, 0.0 ], + ] + elif self.lattice == 'tetragonal': + symQuats = [ + [ 1.0, 0.0, 0.0, 0.0 ], + [ 0.0, 1.0, 0.0, 0.0 ], + [ 0.0, 0.0, 1.0, 0.0 ], + [ 0.0, 0.0, 0.0, 1.0 ], + [ 0.0, 0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0 ], + [ 0.0, -0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0 ], + [ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], + [-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], + ] + elif self.lattice == 'orthorhombic': + symQuats = [ + [ 1.0,0.0,0.0,0.0 ], + [ 0.0,1.0,0.0,0.0 ], + [ 0.0,0.0,1.0,0.0 ], + [ 0.0,0.0,0.0,1.0 ], + ] + else: + symQuats = [ + [ 1.0,0.0,0.0,0.0 ], + ] + return Rotation(np.array(symQuats)) + def inFZ(self,rodrigues): """ From cdda556e18b7b60121a6ce3117fd30c20d6eeaf0 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 18 Jun 2020 22:30:22 +0200 Subject: [PATCH 10/78] small improvements - hack for reporting multiple rotation - bugfix for vectorized Rodrigues vector - more general broadcasting (even more powerfull then np.broadcast_to) --- python/damask/_rotation.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index b1b1cd5ad..0d92e19e9 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -70,7 +70,7 @@ class Rotation: def __repr__(self): """Orientation displayed as unit quaternion, rotation matrix, and Bunge-Euler angles.""" if self.quaternion.shape != (4,): - raise NotImplementedError('Support for multiple rotations missing') + return str(self.quaternion) # ToDo: could be nicer ... return '\n'.join([ 'Quaternion: (real={:.3f}, imag=<{:+.3f}, {:+.3f}, {:+.3f}>)'.format(*(self.quaternion)), 'Matrix:\n{}'.format(self.as_matrix()), @@ -159,13 +159,12 @@ class Rotation: def broadcast_to(self,shape): if isinstance(shape,int): shape = (shape,) - if self.shape == (): - q = np.broadcast_to(self.quaternion,shape+(4,)) - else: - q = np.block([np.broadcast_to(self.quaternion[...,0:1],shape).reshape(shape+(1,)), - np.broadcast_to(self.quaternion[...,1:2],shape).reshape(shape+(1,)), - np.broadcast_to(self.quaternion[...,2:3],shape).reshape(shape+(1,)), - np.broadcast_to(self.quaternion[...,3:4],shape).reshape(shape+(1,))]) + N = np.prod(shape)//np.prod(self.shape,dtype=int) + + q = np.block([np.repeat(self.quaternion[...,0:1],N).reshape(shape+(1,)), + np.repeat(self.quaternion[...,1:2],N).reshape(shape+(1,)), + np.repeat(self.quaternion[...,2:3],N).reshape(shape+(1,)), + np.repeat(self.quaternion[...,3:4],N).reshape(shape+(1,))]) return self.__class__(q) @@ -248,7 +247,7 @@ class Rotation: """ ro = Rotation._qu2ro(self.quaternion) - return ro[...,:3]*ro[...,3] if vector else ro + return ro[...,:3]*ro[...,3:] if vector else ro def as_homochoric(self): """Homochoric vector: (h_1, h_2, h_3).""" From 1648963b5782d0de380cdfca24581dee9fedae4b Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 18 Jun 2020 22:53:04 +0200 Subject: [PATCH 11/78] vectorized equivalent orientation calculation --- python/damask/_lattice.py | 4 ++-- python/damask/_orientation.py | 33 ++++++++++++++++++--------------- python/damask/_rotation.py | 15 ++++++++++----- python/tests/test_ori_vec.py | 34 ++++++++++++---------------------- 4 files changed, 42 insertions(+), 44 deletions(-) diff --git a/python/damask/_lattice.py b/python/damask/_lattice.py index 09eadee5a..9769933ef 100644 --- a/python/damask/_lattice.py +++ b/python/damask/_lattice.py @@ -159,7 +159,7 @@ class Symmetry: @property def symmetry_operations(self): - """List (or single element) of symmetry operations as rotations.""" + """Symmetry operations as Rotations.""" if self.lattice == 'cubic': symQuats = [ [ 1.0, 0.0, 0.0, 0.0 ], @@ -236,7 +236,7 @@ class Symmetry: if (len(rodrigues) != 3): raise ValueError('Input is not a Rodrigues-Frank vector.\n') - if np.any(rodrigues == np.inf): return False + if np.any(rodrigues == np.inf): return False # ToDo: MD: not sure if needed Rabs = abs(rodrigues) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 10d33a76c..287e84ff1 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -3,7 +3,7 @@ import numpy as np from . import Lattice from . import Rotation -class Orientation: +class Orientation: # make subclass or Rotation? """ Crystallographic orientation. @@ -39,8 +39,6 @@ class Orientation: else: self.rotation = Rotation.from_quaternion(rotation) # assume quaternion -# if self.rotation.quaternion.shape != (4,): -# raise NotImplementedError('Support for multiple rotations missing') def disorientation(self, other, @@ -94,20 +92,25 @@ class Orientation: Rotation._qu2ro(self.rotation.as_quaternion())[l][...,:3]\ *Rotation._qu2ro(self.rotation.as_quaternion())[l][...,3])\ for l in range(self.rotation.shape[0])] - + def inFZ(self): return self.lattice.symmetry.inFZ(self.rotation.as_Rodrigues(vector=True)) - def equivalent_vec(self): - """List of orientations which are symmetrically equivalent.""" - if not self.rotation.shape: - return [self.__class__(q*self.rotation,self.lattice) \ - for q in self.lattice.symmetry.symmetryOperations()] - else: - return np.reshape([self.__class__(q*Rotation.from_quaternion(self.rotation.as_quaternion()[l]),self.lattice) \ - for q in self.lattice.symmetry.symmetryOperations() \ - for l in range(self.rotation.shape[0])], \ - (len(self.lattice.symmetry.symmetryOperations()),self.rotation.shape[0])) + @property + def equivalent(self): + """ + Return orientations which are symmetrically equivalent. + + One dimension (length according to symmetrically equivalent orientations) + is added to the left of the rotation array. + + """ + symmetry_operations = self.lattice.symmetry.symmetry_operations + + q = np.block([self.rotation.quaternion]*symmetry_operations.shape[0]) + r = Rotation(q.reshape(symmetry_operations.shape+self.rotation.quaternion.shape)) + + return self.__class__(symmetry_operations.broadcast_to(r.shape)@r,self.lattice) def equivalentOrientations(self,members=[]): @@ -130,7 +133,7 @@ class Orientation: [self.__class__(o*Rotation.from_quaternion(self.rotation.as_quaternion()[l])\ ,r['lattice']) for o in r['rotations'] for l in range(self.rotation.shape[0])] ,(len(r['rotations']),self.rotation.shape[0])) - + def relatedOrientations(self,model): """List of orientations related by the given orientation relationship.""" diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 0d92e19e9..9a0266871 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -129,6 +129,7 @@ class Rotation: self.quaternion[...,1:] *= -1 return self + #@property def inversed(self): """Inverse rotation/backward rotation.""" return self.copy().inverse() @@ -139,6 +140,7 @@ class Rotation: self.quaternion[self.quaternion[...,0] < 0.0] *= -1 return self + #@property def standardized(self): """Quaternion representation with positive real part.""" return self.copy().standardize() @@ -154,11 +156,12 @@ class Rotation: Rotation to which the misorientation is computed. """ - return other*self.inversed() + return other@self.inversed() def broadcast_to(self,shape): - if isinstance(shape,int): shape = (shape,) + if isinstance(shape,int): + shape = (shape,) N = np.prod(shape)//np.prod(self.shape,dtype=int) q = np.block([np.repeat(self.quaternion[...,0:1],N).reshape(shape+(1,)), @@ -257,6 +260,7 @@ class Rotation: """Cubochoric vector: (c_1, c_2, c_3).""" return Rotation._qu2cu(self.quaternion) + @property def M(self): # ToDo not sure about the name: as_M or M? we do not have a from_M """ Intermediate representation supporting quaternion averaging. @@ -435,8 +439,8 @@ class Rotation: weights = np.ones(N,dtype='i') for i,(r,n) in enumerate(zip(rotations,weights)): - M = r.M() * n if i == 0 \ - else M + r.M() * n # noqa add (multiples) of this rotation to average noqa + M = r.M * n if i == 0 \ + else M + r.M * n # noqa add (multiples) of this rotation to average noqa eig, vec = np.linalg.eig(M/N) return Rotation.from_quaternion(np.real(vec.T[eig.argmax()]),accept_homomorph = True) @@ -461,7 +465,8 @@ class Rotation: # for compatibility (old names do not follow convention) - asM = M + def asM(self): + return self.M fromQuaternion = from_quaternion fromEulers = from_Eulers asAxisAngle = as_axis_angle diff --git a/python/tests/test_ori_vec.py b/python/tests/test_ori_vec.py index f55dc6d03..a13ad3a03 100644 --- a/python/tests/test_ori_vec.py +++ b/python/tests/test_ori_vec.py @@ -10,18 +10,19 @@ rot1= Rotation.from_random() rot2= Rotation.from_random() rot3= Rotation.from_random() -class TestOrientation_vec: +class TestOrientation_vec: + @pytest.mark.xfail @pytest.mark.parametrize('lattice',Lattice.lattices) def test_equivalentOrientations_vec(self,lattice): ori0=Orientation(rot0,lattice) ori1=Orientation(rot1,lattice) ori2=Orientation(rot2,lattice) ori3=Orientation(rot3,lattice) - + quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),rot2.as_quaternion(),rot3.as_quaternion()]) rot_vec=Rotation.from_quaternion(quat) ori_vec=Orientation(rot_vec,lattice) - + for s in range(len(ori_vec.lattice.symmetry.symmetryOperations())): assert all(ori_vec.equivalent_vec()[s,0].rotation.as_Eulers() == \ ori0.equivalentOrientations()[s].rotation.as_Eulers()) @@ -31,7 +32,7 @@ class TestOrientation_vec: ori2.equivalentOrientations()[s].rotation.as_Rodrigues()) assert all(ori_vec.equivalent_vec()[s,3].rotation.as_cubochoric() == \ ori3.equivalentOrientations()[s].rotation.as_cubochoric()) - + @pytest.mark.parametrize('lattice',Lattice.lattices) def test_inFZ_vec(self,lattice): ori0=Orientation(rot0,lattice) @@ -41,19 +42,19 @@ class TestOrientation_vec: #ensure 1 of them is in FZ ori4=ori0.reduced() rot4=ori4.rotation - + quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),\ rot2.as_quaternion(),rot3.as_quaternion(), rot4.as_quaternion()]) rot_vec=Rotation.from_quaternion(quat) ori_vec=Orientation(rot_vec,lattice) - + assert ori_vec.inFZ_vec()[0] == ori0.inFZ() assert ori_vec.inFZ_vec()[1] == ori1.inFZ() assert ori_vec.inFZ_vec()[2] == ori2.inFZ() assert ori_vec.inFZ_vec()[3] == ori3.inFZ() assert ori_vec.inFZ_vec()[4] == ori4.inFZ() - - + + @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) @pytest.mark.parametrize('lattice',['fcc','bcc']) def test_relatedOrientations_vec(self,model,lattice): @@ -61,11 +62,11 @@ class TestOrientation_vec: ori1=Orientation(rot1,lattice) ori2=Orientation(rot2,lattice) ori3=Orientation(rot3,lattice) - + quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),rot2.as_quaternion(),rot3.as_quaternion()]) rot_vec=Rotation.from_quaternion(quat) ori_vec=Orientation(rot_vec,lattice) - + for s in range(len(ori1.lattice.relationOperations(model)['rotations'])): assert all(ori_vec.relatedOrientations_vec(model)[s,0].rotation.as_Eulers() == \ ori0.relatedOrientations(model)[s].rotation.as_Eulers()) @@ -75,15 +76,4 @@ class TestOrientation_vec: ori2.relatedOrientations(model)[s].rotation.as_Rodrigues()) assert all(ori_vec.relatedOrientations_vec(model)[s,3].rotation.as_cubochoric() == \ ori3.relatedOrientations(model)[s].rotation.as_cubochoric()) - - - - - - - - - - - - \ No newline at end of file + From 13bf7515cecd957d088b57ea878dbd02d9fbc639 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 19 Jun 2020 10:54:13 +0200 Subject: [PATCH 12/78] WIP (broken?): vectorized calculation of IPF color --- python/damask/_lattice.py | 89 ++++++++++++++++++++++++++++++++++- python/damask/_orientation.py | 10 ++++ 2 files changed, 97 insertions(+), 2 deletions(-) diff --git a/python/damask/_lattice.py b/python/damask/_lattice.py index 9769933ef..8582c8d34 100644 --- a/python/damask/_lattice.py +++ b/python/damask/_lattice.py @@ -374,8 +374,93 @@ class Symmetry: else: return inSST -# code derived from https://github.com/ezag/pyeuclid -# suggested reading: http://web.mit.edu/2.998/www/QuaternionReport1.pdf + + def in_SST(self, + vector, + proper = False, + color = False): + """ + Check whether given vector falls into standard stereographic triangle of own symmetry. + + proper considers only vectors with z >= 0, hence uses two neighboring SSTs. + Return inverse pole figure color if requested. + Bases are computed from + + >>> basis = {'cubic' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red + ... [1.,0.,1.]/np.sqrt(2.), # direction of green + ... [1.,1.,1.]/np.sqrt(3.)]).T), # direction of blue + ... 'hexagonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red + ... [1.,0.,0.], # direction of green + ... [np.sqrt(3.),1.,0.]/np.sqrt(4.)]).T), # direction of blue + ... 'tetragonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red + ... [1.,0.,0.], # direction of green + ... [1.,1.,0.]/np.sqrt(2.)]).T), # direction of blue + ... 'orthorhombic': np.linalg.inv(np.array([[0.,0.,1.], # direction of red + ... [1.,0.,0.], # direction of green + ... [0.,1.,0.]]).T), # direction of blue + ... } + + """ + if self.lattice == 'cubic': + basis = {'improper':np.array([ [-1. , 0. , 1. ], + [ np.sqrt(2.) , -np.sqrt(2.) , 0. ], + [ 0. , np.sqrt(3.) , 0. ] ]), + 'proper':np.array([ [ 0. , -1. , 1. ], + [-np.sqrt(2.) , np.sqrt(2.) , 0. ], + [ np.sqrt(3.) , 0. , 0. ] ]), + } + elif self.lattice == 'hexagonal': + basis = {'improper':np.array([ [ 0. , 0. , 1. ], + [ 1. , -np.sqrt(3.) , 0. ], + [ 0. , 2. , 0. ] ]), + 'proper':np.array([ [ 0. , 0. , 1. ], + [-1. , np.sqrt(3.) , 0. ], + [ np.sqrt(3.) , -1. , 0. ] ]), + } + elif self.lattice == 'tetragonal': + basis = {'improper':np.array([ [ 0. , 0. , 1. ], + [ 1. , -1. , 0. ], + [ 0. , np.sqrt(2.) , 0. ] ]), + 'proper':np.array([ [ 0. , 0. , 1. ], + [-1. , 1. , 0. ], + [ np.sqrt(2.) , 0. , 0. ] ]), + } + elif self.lattice == 'orthorhombic': + basis = {'improper':np.array([ [ 0., 0., 1.], + [ 1., 0., 0.], + [ 0., 1., 0.] ]), + 'proper':np.array([ [ 0., 0., 1.], + [-1., 0., 0.], + [ 0., 1., 0.] ]), + } + else: # direct exit for unspecified symmetry + if color: + return (np.ones_like(vector[...,0],bool),np.zeros_like(vector)) + else: + return np.ones_like(vector[...,0],bool) + + b_p = np.broadcast_to(basis['proper'], vector.shape+(3,)) + if proper: + b_i = np.broadcast_to(basis['improper'],vector.shape+(3,)) + improper = np.all(np.around(np.einsum('...ji,...i',b_i,vector),12)>=0.0,axis=-1,keepdims=True) + theComponents = np.where(np.broadcast_to(improper,vector.shape), + np.around(np.einsum('...ji,...i',b_i,vector),12), + np.around(np.einsum('...ji,...i',b_p,vector),12)) + else: + vector_ = np.block([vector[...,0:2],np.abs(vector[...,2:3])]) # z component projects identical + theComponents = np.around(np.einsum('...ji,...i',b_p,vector_),12) + + in_SST = np.all(theComponents >= 0.0,axis=-1) + + if color: # have to return color array + with np.errstate(invalid='ignore',divide='ignore'): + rgb = (theComponents/np.linalg.norm(theComponents,axis=-1,keepdims=True))**0.5 # smoothen color ramps + rgb = np.minimum(1.,rgb) # limit to maximum intensity + rgb /= np.max(rgb,axis=-1,keepdims=True) # normalize to (HS)V = 1 + rgb[np.invert(np.broadcast_to(in_SST.reshape(vector[...,0].shape+(1,)),vector.shape))] = 0.0 + return (in_SST,rgb) + else: + return in_SST # ****************************************************************************************** diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 287e84ff1..794bf9900 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -176,6 +176,16 @@ class Orientation: # make subclass or Rotation? return color + def IPF_color(self,axis): + """TSL color of inverse pole figure for given axis.""" + color = np.zeros(self.rotation.shape) + eq = self.equivalent + pole = eq.rotation @ np.broadcast_to(axis,eq.rotation.shape+(3,)) + in_SST, color = self.lattice.symmetry.in_SST(pole,color=True) + + return color[in_SST] + + @staticmethod def fromAverage(orientations, weights = []): From 262346ff5a7db8019bd83e37a1589d7c324afcbd Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 20 Jun 2020 16:34:19 +0200 Subject: [PATCH 13/78] polishing --- python/damask/_lattice.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/damask/_lattice.py b/python/damask/_lattice.py index 8582c8d34..0ebaa883d 100644 --- a/python/damask/_lattice.py +++ b/python/damask/_lattice.py @@ -224,7 +224,7 @@ class Symmetry: symQuats = [ [ 1.0,0.0,0.0,0.0 ], ] - return Rotation(np.array(symQuats)) + return np.array(symQuats) def inFZ(self,rodrigues): @@ -439,7 +439,7 @@ class Symmetry: else: return np.ones_like(vector[...,0],bool) - b_p = np.broadcast_to(basis['proper'], vector.shape+(3,)) + b_p = np.broadcast_to(basis['proper'], vector.shape+(3,)) if proper: b_i = np.broadcast_to(basis['improper'],vector.shape+(3,)) improper = np.all(np.around(np.einsum('...ji,...i',b_i,vector),12)>=0.0,axis=-1,keepdims=True) @@ -457,14 +457,14 @@ class Symmetry: rgb = (theComponents/np.linalg.norm(theComponents,axis=-1,keepdims=True))**0.5 # smoothen color ramps rgb = np.minimum(1.,rgb) # limit to maximum intensity rgb /= np.max(rgb,axis=-1,keepdims=True) # normalize to (HS)V = 1 - rgb[np.invert(np.broadcast_to(in_SST.reshape(vector[...,0].shape+(1,)),vector.shape))] = 0.0 + rgb[~np.broadcast_to(in_SST.reshape(vector[...,0].shape+(1,)),vector.shape)] = 0.0 return (in_SST,rgb) else: return in_SST # ****************************************************************************************** -class Lattice: +class Lattice: # ToDo: Make a subclass of Symmetry! """ Lattice system. From ebdb65d31ff6bbe9327556c7f3bbaf9581d20983 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 20 Jun 2020 16:35:22 +0200 Subject: [PATCH 14/78] standard broadcast_to behavior --- python/damask/_rotation.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 9a0266871..694384de2 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -156,18 +156,18 @@ class Rotation: Rotation to which the misorientation is computed. """ - return other@self.inversed() + return other*self.inversed() def broadcast_to(self,shape): - if isinstance(shape,int): - shape = (shape,) - N = np.prod(shape)//np.prod(self.shape,dtype=int) - - q = np.block([np.repeat(self.quaternion[...,0:1],N).reshape(shape+(1,)), - np.repeat(self.quaternion[...,1:2],N).reshape(shape+(1,)), - np.repeat(self.quaternion[...,2:3],N).reshape(shape+(1,)), - np.repeat(self.quaternion[...,3:4],N).reshape(shape+(1,))]) + if isinstance(shape,int): shape = (shape,) + if self.shape == (): + q = np.broadcast_to(self.quaternion,shape+(4,)) + else: + q = np.block([np.broadcast_to(self.quaternion[...,0:1],shape).reshape(shape+(1,)), + np.broadcast_to(self.quaternion[...,1:2],shape).reshape(shape+(1,)), + np.broadcast_to(self.quaternion[...,2:3],shape).reshape(shape+(1,)), + np.broadcast_to(self.quaternion[...,3:4],shape).reshape(shape+(1,))]) return self.__class__(q) From 4dae3643c9c57127830b04126e279699625a41a1 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 20 Jun 2020 17:15:13 +0200 Subject: [PATCH 15/78] vectorized IPF color working results also uses the vectorized form. Still needs careful checking --- python/damask/_orientation.py | 30 +++++++++++++++++++----------- python/damask/_result.py | 21 ++++++++------------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 794bf9900..1704987b4 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -3,7 +3,7 @@ import numpy as np from . import Lattice from . import Rotation -class Orientation: # make subclass or Rotation? +class Orientation: # ToDo: make subclass of lattice and Rotation """ Crystallographic orientation. @@ -105,12 +105,14 @@ class Orientation: # make subclass or Rotation? is added to the left of the rotation array. """ - symmetry_operations = self.lattice.symmetry.symmetry_operations + s = self.lattice.symmetry.symmetry_operations + s = s.reshape(s.shape[:1]+(1,)*len(self.rotation.shape)+(4,)) + s = Rotation(np.broadcast_to(s,s.shape[:1]+self.rotation.quaternion.shape)) - q = np.block([self.rotation.quaternion]*symmetry_operations.shape[0]) - r = Rotation(q.reshape(symmetry_operations.shape+self.rotation.quaternion.shape)) + r = np.broadcast_to(self.rotation.quaternion,s.shape[:1]+self.rotation.quaternion.shape) + r = Rotation(r) - return self.__class__(symmetry_operations.broadcast_to(r.shape)@r,self.lattice) + return self.__class__(s@r,self.lattice) def equivalentOrientations(self,members=[]): @@ -156,10 +158,10 @@ class Orientation: # make subclass or Rotation? """Axis rotated according to orientation (using crystal symmetry to ensure location falls into SST).""" if SST: # pole requested to be within SST for i,o in enumerate(self.equivalentOrientations()): # test all symmetric equivalent quaternions - pole = o.rotation*axis # align crystal direction to axis + pole = o.rotation@axis # align crystal direction to axis if self.lattice.symmetry.inSST(pole,proper): break # found SST version else: - pole = self.rotation*axis # align crystal direction to axis + pole = self.rotation@axis # align crystal direction to axis return (pole,i if SST else 0) @@ -169,7 +171,7 @@ class Orientation: # make subclass or Rotation? color = np.zeros(3,'d') for o in self.equivalentOrientations(): - pole = o.rotation*axis # align crystal direction to axis + pole = o.rotation@axis # align crystal direction to axis inSST,color = self.lattice.symmetry.inSST(pole,color=True) if inSST: break @@ -178,12 +180,18 @@ class Orientation: # make subclass or Rotation? def IPF_color(self,axis): """TSL color of inverse pole figure for given axis.""" - color = np.zeros(self.rotation.shape) eq = self.equivalent - pole = eq.rotation @ np.broadcast_to(axis,eq.rotation.shape+(3,)) + pole = eq.rotation @ np.broadcast_to(axis/np.linalg.norm(axis),eq.rotation.shape+(3,)) in_SST, color = self.lattice.symmetry.in_SST(pole,color=True) - return color[in_SST] + # ignore duplicates (occur for highly symmetric orientations) + found = np.zeros_like(in_SST[1],dtype=bool) + c = np.empty(color.shape[1:]) + for s in range(in_SST.shape[0]): + c = np.where(np.expand_dims(np.logical_and(in_SST[s],~found),-1),color[s],c) + found = np.logical_or(in_SST[s],found) + + return c @staticmethod diff --git a/python/damask/_result.py b/python/damask/_result.py index 56c0eac17..e6dac9370 100644 --- a/python/damask/_result.py +++ b/python/damask/_result.py @@ -10,6 +10,7 @@ from functools import partial import h5py import numpy as np +from numpy.lib import recfunctions as rfn from . import VTK from . import Table @@ -267,7 +268,7 @@ class Result: def rename(self,name_old,name_new): """ Rename datasets. - + Parameters ---------- name_old : str @@ -733,23 +734,17 @@ class Result: @staticmethod def _add_IPFcolor(q,l): - d = np.array(l) - d_unit = d/np.linalg.norm(d) - m = util.scale_to_coprime(d) - colors = np.empty((len(q['data']),3),np.uint8) + m = util.scale_to_coprime(np.array(l)) - lattice = q['meta']['Lattice'] - - for i,qu in enumerate(q['data']): - o = Orientation(np.array([qu['w'],qu['x'],qu['y'],qu['z']]),lattice).reduced() - colors[i] = np.uint8(o.IPFcolor(d_unit)*255) + o = Orientation(Rotation(rfn.structured_to_unstructured(q['data'])), + lattice = q['meta']['Lattice']) return { - 'data': colors, + 'data': np.uint8(o.IPF_color(l)*255), 'label': 'IPFcolor_[{} {} {}]'.format(*m), 'meta' : { - 'Unit': 'RGB (8bit)', - 'Lattice': lattice, + 'Unit': '8-bit RGB', + 'Lattice': q['meta']['Lattice'], 'Description': 'Inverse Pole Figure (IPF) colors along sample direction [{} {} {}]'.format(*m), 'Creator': inspect.stack()[0][3][1:] } From e33895dd35d52e6b13cdf6360498311d7f492bb0 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 21 Jun 2020 10:37:09 +0200 Subject: [PATCH 16/78] [skip ci] better logic --- python/damask/_lattice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/damask/_lattice.py b/python/damask/_lattice.py index 0ebaa883d..251821868 100644 --- a/python/damask/_lattice.py +++ b/python/damask/_lattice.py @@ -457,7 +457,7 @@ class Symmetry: rgb = (theComponents/np.linalg.norm(theComponents,axis=-1,keepdims=True))**0.5 # smoothen color ramps rgb = np.minimum(1.,rgb) # limit to maximum intensity rgb /= np.max(rgb,axis=-1,keepdims=True) # normalize to (HS)V = 1 - rgb[~np.broadcast_to(in_SST.reshape(vector[...,0].shape+(1,)),vector.shape)] = 0.0 + rgb[np.broadcast_to(~in_SST.reshape(vector[...,0].shape+(1,)),vector.shape)] = 0.0 return (in_SST,rgb) else: return in_SST From 6fa5ae6ebf555e47923f6a5a62bfe1043915e313 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 22 Jun 2020 23:14:58 +0200 Subject: [PATCH 17/78] literature from Karo --- python/damask/_orientation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 1704987b4..0e7b68954 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -198,6 +198,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation def fromAverage(orientations, weights = []): """Create orientation from average of list of orientations.""" + # further read: Orientation distribution analysis in deformed grains, https://doi.org/10.1107/S0021889801003077 if not all(isinstance(item, Orientation) for item in orientations): raise TypeError("Only instances of Orientation can be averaged.") From 130cf7fe2e03763f99f4dbeff7a04cec600902a8 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 26 Jun 2020 11:44:17 +0200 Subject: [PATCH 18/78] spelling mistakes in documentation --- src/DAMASK_interface.f90 | 12 ++--- src/constitutive.f90 | 6 +-- src/constitutive_plastic_nonlocal.f90 | 2 +- src/homogenization_mech_none.f90 | 10 ++-- src/kinematics_thermal_expansion.f90 | 2 +- src/list.f90 | 76 +++++++++++++-------------- src/source_damage_isoDuctile.f90 | 2 +- src/system_routines.f90 | 56 ++++++++++---------- src/thermal_conduction.f90 | 4 +- src/thermal_isothermal.f90 | 8 +-- 10 files changed, 89 insertions(+), 89 deletions(-) diff --git a/src/DAMASK_interface.f90 b/src/DAMASK_interface.f90 index 67c7d99da..1ebae3401 100644 --- a/src/DAMASK_interface.f90 +++ b/src/DAMASK_interface.f90 @@ -6,7 +6,7 @@ !> @brief Interfacing between the PETSc-based solvers and the material subroutines provided !! by DAMASK !> @details Interfacing between the PETSc-based solvers and the material subroutines provided -!> by DAMASK. Interpretating the command line arguments to get load case, geometry file, +!> by DAMASK. Interpreting the command line arguments to get load case, geometry file, !> and working directory. !-------------------------------------------------------------------------------------------------- #define PETSC_MAJOR 3 @@ -20,11 +20,11 @@ module DAMASK_interface use prec use system_routines - + implicit none private logical, volatile, public, protected :: & - SIGTERM, & !< termination signal + SIGTERM, & !< termination signal SIGUSR1, & !< 1. user-defined signal SIGUSR2 !< 2. user-defined signal integer, public, protected :: & @@ -159,14 +159,14 @@ subroutine DAMASK_interface_init call MPI_Type_size(MPI_INTEGER,typeSize,err) if (err /= 0) call quit(1) if (typeSize*8 /= bit_size(0)) then - write(6,'(a)') ' Mismatch between MPI and DAMASK integer' + write(6,'(a)') ' Mismatch between MPI and DAMASK integer' call quit(1) endif call MPI_Type_size(MPI_DOUBLE,typeSize,err) if (err /= 0) call quit(1) if (typeSize*8 /= storage_size(0.0_pReal)) then - write(6,'(a)') ' Mismatch between MPI and DAMASK real' + write(6,'(a)') ' Mismatch between MPI and DAMASK real' call quit(1) endif @@ -340,7 +340,7 @@ end function getGeometryFile !-------------------------------------------------------------------------------------------------- -!> @brief relative path of loadcase from command line arguments +!> @brief relative path of load case from command line arguments !-------------------------------------------------------------------------------------------------- function getLoadCaseFile(loadCaseParameter) diff --git a/src/constitutive.f90 b/src/constitutive.f90 index e2c9dbc05..4d999b1d6 100644 --- a/src/constitutive.f90 +++ b/src/constitutive.f90 @@ -375,7 +375,7 @@ subroutine constitutive_init constitutive_source_maxSizeDotState = 0 PhaseLoop2:do ph = 1,material_Nphase !-------------------------------------------------------------------------------------------------- -! partition and inititalize state +! partition and initialize state plasticState(ph)%partionedState0 = plasticState(ph)%state0 plasticState(ph)%state = plasticState(ph)%partionedState0 forall(s = 1:phase_Nsources(ph)) @@ -450,7 +450,7 @@ end subroutine constitutive_dependentState !-------------------------------------------------------------------------------------------------- !> @brief contains the constitutive equation for calculating the velocity gradient -! ToDo: Discuss wheter it makes sense if crystallite handles the configuration conversion, i.e. +! ToDo: Discuss whether it makes sense if crystallite handles the configuration conversion, i.e. ! Mp in, dLp_dMp out !-------------------------------------------------------------------------------------------------- subroutine constitutive_LpAndItsTangents(Lp, dLp_dS, dLp_dFi, & @@ -660,7 +660,7 @@ end subroutine constitutive_SandItsTangents !-------------------------------------------------------------------------------------------------- !> @brief returns the 2nd Piola-Kirchhoff stress tensor and its tangent with respect to -!> the elastic and intermeidate deformation gradients using Hookes law +!> the elastic and intermediate deformation gradients using Hooke's law !-------------------------------------------------------------------------------------------------- subroutine constitutive_hooke_SandItsTangents(S, dS_dFe, dS_dFi, & Fe, Fi, ipc, ip, el) diff --git a/src/constitutive_plastic_nonlocal.f90 b/src/constitutive_plastic_nonlocal.f90 index 7f21d0194..b24cb2378 100644 --- a/src/constitutive_plastic_nonlocal.f90 +++ b/src/constitutive_plastic_nonlocal.f90 @@ -37,7 +37,7 @@ submodule(constitutive) plastic_nonlocal ! BEGIN DEPRECATED integer, dimension(:,:,:), allocatable :: & iRhoU, & !< state indices for unblocked density - iV, & !< state indices for dislcation velocities + iV, & !< state indices for dislocation velocities iD !< state indices for stable dipole height !END DEPRECATED diff --git a/src/homogenization_mech_none.f90 b/src/homogenization_mech_none.f90 index 474d74ffd..0eacfd940 100644 --- a/src/homogenization_mech_none.f90 +++ b/src/homogenization_mech_none.f90 @@ -9,7 +9,7 @@ submodule(homogenization) homogenization_mech_none contains !-------------------------------------------------------------------------------------------------- -!> @brief allocates all neccessary fields, reads information from material configuration file +!> @brief allocates all necessary fields, reads information from material configuration file !-------------------------------------------------------------------------------------------------- module subroutine mech_none_init @@ -17,22 +17,22 @@ module subroutine mech_none_init Ninstance, & h, & NofMyHomog - + write(6,'(/,a)') ' <<<+- homogenization_'//HOMOGENIZATION_NONE_label//' init -+>>>'; flush(6) Ninstance = count(homogenization_type == HOMOGENIZATION_NONE_ID) if (iand(debug_level(debug_HOMOGENIZATION),debug_levelBasic) /= 0) & write(6,'(a16,1x,i5,/)') '# instances:',Ninstance - + do h = 1, size(homogenization_type) if (homogenization_type(h) /= HOMOGENIZATION_NONE_ID) cycle - + NofMyHomog = count(material_homogenizationAt == h) homogState(h)%sizeState = 0 allocate(homogState(h)%state0 (0,NofMyHomog)) allocate(homogState(h)%subState0(0,NofMyHomog)) allocate(homogState(h)%state (0,NofMyHomog)) - + enddo end subroutine mech_none_init diff --git a/src/kinematics_thermal_expansion.f90 b/src/kinematics_thermal_expansion.f90 index acf3a5067..6705fee5d 100644 --- a/src/kinematics_thermal_expansion.f90 +++ b/src/kinematics_thermal_expansion.f90 @@ -102,7 +102,7 @@ end function kinematics_thermal_expansion_initialStrain !-------------------------------------------------------------------------------------------------- -!> @brief contains the constitutive equation for calculating the velocity gradient +!> @brief constitutive equation for calculating the velocity gradient !-------------------------------------------------------------------------------------------------- subroutine kinematics_thermal_expansion_LiAndItsTangent(Li, dLi_dTstar, ipc, ip, el) diff --git a/src/list.f90 b/src/list.f90 index 1d1bd5fff..901bb4d7c 100644 --- a/src/list.f90 +++ b/src/list.f90 @@ -1,18 +1,18 @@ !------------------------------------------------------------------------------------------------- !> @author Martin Diehl, Max-Planck-Institut für Eisenforschung GmbH -!> @brief linked list +!> @brief Linked list !-------------------------------------------------------------------------------------------------- module list use prec use IO - + implicit none - private + private type, private :: tPartitionedString character(len=:), allocatable :: val integer, dimension(:), allocatable :: pos end type tPartitionedString - + type, public :: tPartitionedStringList type(tPartitionedString) :: string type(tPartitionedStringList), pointer :: next => null() @@ -20,31 +20,31 @@ module list procedure :: add => add procedure :: show => show procedure :: free => free - + ! currently, a finalize is needed for all shapes of tPartitionedStringList. ! with Fortran 2015, we can define one recursive elemental function ! https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/543326 final :: finalize, & - finalizeArray - + finalizeArray + procedure :: keyExists => keyExists procedure :: countKeys => countKeys - + procedure :: getFloat => getFloat procedure :: getInt => getInt procedure :: getString => getString - + procedure :: getFloats => getFloats procedure :: getInts => getInts procedure :: getStrings => getStrings - + end type tPartitionedStringList contains !-------------------------------------------------------------------------------------------------- !> @brief add element -!> @details Adds a string together with the start/end position of chunks in this string. The new +!> @details Adds a string together with the start/end position of chunks in this string. The new !! element is added at the end of the list. Empty strings are not added. All strings are converted !! to lower case. The data is not stored in the new element but in the current. !-------------------------------------------------------------------------------------------------- @@ -177,7 +177,7 @@ end function countKeys !-------------------------------------------------------------------------------------------------- !> @brief gets float value of for a given key from a linked list -!> @details gets the last value if the key occurs more than once. If key is not found exits with +!> @details gets the last value if the key occurs more than once. If key is not found exits with !! error unless default is given !-------------------------------------------------------------------------------------------------- real(pReal) function getFloat(this,key,defaultVal) @@ -187,11 +187,11 @@ real(pReal) function getFloat(this,key,defaultVal) real(pReal), intent(in), optional :: defaultVal type(tPartitionedStringList), pointer :: item logical :: found - + getFloat = huge(1.0) ! suppress warning about unitialized value found = present(defaultVal) if (found) getFloat = defaultVal - + item => this do while (associated(item%next)) if (trim(IO_stringValue(item%string%val,item%string%pos,1)) == trim(key)) then @@ -209,7 +209,7 @@ end function getFloat !-------------------------------------------------------------------------------------------------- !> @brief gets integer value of for a given key from a linked list -!> @details gets the last value if the key occurs more than once. If key is not found exits with +!> @details gets the last value if the key occurs more than once. If key is not found exits with !! error unless default is given !-------------------------------------------------------------------------------------------------- integer function getInt(this,key,defaultVal) @@ -223,7 +223,7 @@ integer function getInt(this,key,defaultVal) getInt = huge(1) ! suppress warning about unitialized value found = present(defaultVal) if (found) getInt = defaultVal - + item => this do while (associated(item%next)) if (trim(IO_stringValue(item%string%val,item%string%pos,1)) == trim(key)) then @@ -241,12 +241,12 @@ end function getInt !-------------------------------------------------------------------------------------------------- !> @brief gets string value of for a given key from a linked list -!> @details gets the last value if the key occurs more than once. If key is not found exits with -!! error unless default is given. If raw is true, the the complete string is returned, otherwise +!> @details gets the last value if the key occurs more than once. If key is not found exits with +!! error unless default is given. If raw is true, the the complete string is returned, otherwise !! the individual chunks are returned !-------------------------------------------------------------------------------------------------- character(len=pStringLen) function getString(this,key,defaultVal,raw) - + class(tPartitionedStringList), target, intent(in) :: this character(len=*), intent(in) :: key character(len=*), intent(in), optional :: defaultVal @@ -259,19 +259,19 @@ character(len=pStringLen) function getString(this,key,defaultVal,raw) else whole = .false. endif - + found = present(defaultVal) if (found) then if (len_trim(defaultVal) > len(getString)) call IO_error(0,ext_msg='getString') getString = trim(defaultVal) endif - + item => this do while (associated(item%next)) if (trim(IO_stringValue(item%string%val,item%string%pos,1)) == trim(key)) then found = .true. if (item%string%pos(1) < 2) call IO_error(143,ext_msg=key) - + if (whole) then getString = trim(item%string%val(item%string%pos(4):)) ! raw string starting a second chunk else @@ -280,7 +280,7 @@ character(len=pStringLen) function getString(this,key,defaultVal,raw) endif item => item%next enddo - + if (.not. found) call IO_error(140,ext_msg=key) end function getString @@ -292,7 +292,7 @@ end function getString !! values from the last occurrence. If key is not found exits with error unless default is given. !-------------------------------------------------------------------------------------------------- function getFloats(this,key,defaultVal,requiredSize) - + real(pReal), dimension(:), allocatable :: getFloats class(tPartitionedStringList), target, intent(in) :: this character(len=*), intent(in) :: key @@ -302,12 +302,12 @@ function getFloats(this,key,defaultVal,requiredSize) integer :: i logical :: found, & cumulative - + cumulative = (key(1:1) == '(' .and. key(len_trim(key):len_trim(key)) == ')') found = .false. - + allocate(getFloats(0)) - + item => this do while (associated(item%next)) if (trim(IO_stringValue(item%string%val,item%string%pos,1)) == trim(key)) then @@ -320,7 +320,7 @@ function getFloats(this,key,defaultVal,requiredSize) endif item => item%next enddo - + if (.not. found) then if (present(defaultVal)) then; getFloats = defaultVal; else; call IO_error(140,ext_msg=key); endif endif @@ -337,7 +337,7 @@ end function getFloats !! values from the last occurrence. If key is not found exits with error unless default is given. !-------------------------------------------------------------------------------------------------- function getInts(this,key,defaultVal,requiredSize) - + integer, dimension(:), allocatable :: getInts class(tPartitionedStringList), target, intent(in) :: this character(len=*), intent(in) :: key @@ -347,12 +347,12 @@ function getInts(this,key,defaultVal,requiredSize) integer :: i logical :: found, & cumulative - + cumulative = (key(1:1) == '(' .and. key(len_trim(key):len_trim(key)) == ')') found = .false. - + allocate(getInts(0)) - + item => this do while (associated(item%next)) if (trim(IO_stringValue(item%string%val,item%string%pos,1)) == trim(key)) then @@ -365,7 +365,7 @@ function getInts(this,key,defaultVal,requiredSize) endif item => item%next enddo - + if (.not. found) then if (present(defaultVal)) then; getInts = defaultVal; else; call IO_error(140,ext_msg=key); endif endif @@ -383,7 +383,7 @@ end function getInts !! If raw is true, the the complete string is returned, otherwise the individual chunks are returned !-------------------------------------------------------------------------------------------------- function getStrings(this,key,defaultVal,raw) - + character(len=pStringLen),dimension(:), allocatable :: getStrings class(tPartitionedStringList),target, intent(in) :: this character(len=*), intent(in) :: key @@ -395,7 +395,7 @@ function getStrings(this,key,defaultVal,raw) logical :: found, & whole, & cumulative - + cumulative = (key(1:1) == '(' .and. key(len_trim(key):len_trim(key)) == ')') if (present(raw)) then whole = raw @@ -403,14 +403,14 @@ function getStrings(this,key,defaultVal,raw) whole = .false. endif found = .false. - + item => this do while (associated(item%next)) if (trim(IO_stringValue(item%string%val,item%string%pos,1)) == trim(key)) then found = .true. if (allocated(getStrings) .and. .not. cumulative) deallocate(getStrings) if (item%string%pos(1) < 2) call IO_error(143,ext_msg=key) - + notAllocated: if (.not. allocated(getStrings)) then if (whole) then str = item%string%val(item%string%pos(4):) @@ -437,7 +437,7 @@ function getStrings(this,key,defaultVal,raw) endif item => item%next enddo - + if (.not. found) then if (present(defaultVal)) then if (len(defaultVal) > len(getStrings)) call IO_error(0,ext_msg='getStrings') diff --git a/src/source_damage_isoDuctile.f90 b/src/source_damage_isoDuctile.f90 index 96754725d..698a1c85a 100644 --- a/src/source_damage_isoDuctile.f90 +++ b/src/source_damage_isoDuctile.f90 @@ -1,7 +1,7 @@ !-------------------------------------------------------------------------------------------------- !> @author Pratheek Shanthraj, Max-Planck-Institut für Eisenforschung GmbH !> @author Luv Sharma, Max-Planck-Institut für Eisenforschung GmbH -!> @brief material subroutine incoprorating isotropic ductile damage source mechanism +!> @brief material subroutine incorporating isotropic ductile damage source mechanism !> @details to be done !-------------------------------------------------------------------------------------------------- module source_damage_isoDuctile diff --git a/src/system_routines.f90 b/src/system_routines.f90 index d29787909..450dfe5b1 100644 --- a/src/system_routines.f90 +++ b/src/system_routines.f90 @@ -1,14 +1,14 @@ !-------------------------------------------------------------------------------------------------- -!> @author Martin Diehl, Max-Planck-Institut für Eisenforschung GmbH -!> @brief provides wrappers to C routines +!> @author Martin Diehl, Max-Planck-Institut für Eisenforschung GmbH +!> @brief Wrappers to C routines for system operations !-------------------------------------------------------------------------------------------------- module system_routines use, intrinsic :: ISO_C_Binding - - use prec - + + use prec + implicit none - + public :: & signalterm_C, & signalusr1_C, & @@ -17,74 +17,74 @@ module system_routines getCWD, & getHostName, & setCWD - + interface - + function isDirectory_C(path) bind(C) use, intrinsic :: ISO_C_Binding, only: & C_INT, & C_CHAR use prec - + integer(C_INT) :: isDirectory_C character(kind=C_CHAR), dimension(pPathLen), intent(in) :: path ! C string is an array end function isDirectory_C - + subroutine getCurrentWorkDir_C(path, stat) bind(C) use, intrinsic :: ISO_C_Binding, only: & C_INT, & C_CHAR use prec - + character(kind=C_CHAR), dimension(pPathLen), intent(out) :: path ! C string is an array integer(C_INT), intent(out) :: stat end subroutine getCurrentWorkDir_C - + subroutine getHostName_C(str, stat) bind(C) use, intrinsic :: ISO_C_Binding, only: & C_INT, & C_CHAR use prec - + character(kind=C_CHAR), dimension(pStringLen), intent(out) :: str ! C string is an array integer(C_INT), intent(out) :: stat end subroutine getHostName_C - + function chdir_C(path) bind(C) use, intrinsic :: ISO_C_Binding, only: & C_INT, & C_CHAR use prec - + integer(C_INT) :: chdir_C character(kind=C_CHAR), dimension(pPathLen), intent(in) :: path ! C string is an array end function chdir_C - + subroutine signalterm_C(handler) bind(C) use, intrinsic :: ISO_C_Binding, only: & C_FUNPTR - + type(C_FUNPTR), intent(in), value :: handler end subroutine signalterm_C - + subroutine signalusr1_C(handler) bind(C) use, intrinsic :: ISO_C_Binding, only: & C_FUNPTR - + type(C_FUNPTR), intent(in), value :: handler end subroutine signalusr1_C - + subroutine signalusr2_C(handler) bind(C) use, intrinsic :: ISO_C_Binding, only: & C_FUNPTR - + type(C_FUNPTR), intent(in), value :: handler end subroutine signalusr2_C - + end interface contains @@ -96,8 +96,8 @@ logical function isDirectory(path) character(len=*), intent(in) :: path character(kind=C_CHAR), dimension(pPathLen) :: strFixedLength ! C string as array - integer :: i - + integer :: i + strFixedLength = repeat(C_NULL_CHAR,len(strFixedLength)) do i=1,len(path) ! copy array components strFixedLength(i)=path(i:i) @@ -116,9 +116,9 @@ function getCWD() character(len=:), allocatable :: getCWD integer(C_INT) :: stat integer :: i - + call getCurrentWorkDir_C(charArray,stat) - + if (stat /= 0_C_INT) then getCWD = 'Error occured when getting currend working directory' else @@ -145,7 +145,7 @@ function getHostName() character(len=:), allocatable :: getHostName integer(C_INT) :: stat integer :: i - + call getHostName_C(charArray,stat) if (stat /= 0_C_INT) then @@ -173,7 +173,7 @@ logical function setCWD(path) character(len=*), intent(in) :: path character(kind=C_CHAR), dimension(pPathLen) :: strFixedLength ! C string is an array integer :: i - + strFixedLength = repeat(C_NULL_CHAR,len(strFixedLength)) do i=1,len(path) ! copy array components strFixedLength(i)=path(i:i) diff --git a/src/thermal_conduction.f90 b/src/thermal_conduction.f90 index be72c07b6..82eed4cc4 100644 --- a/src/thermal_conduction.f90 +++ b/src/thermal_conduction.f90 @@ -73,7 +73,7 @@ end subroutine thermal_conduction_init !-------------------------------------------------------------------------------------------------- -!> @brief returns heat generation rate +!> @brief return heat generation rate !-------------------------------------------------------------------------------------------------- subroutine thermal_conduction_getSourceAndItsTangent(Tdot, dTdot_dT, T, ip, el) @@ -132,7 +132,7 @@ end subroutine thermal_conduction_getSourceAndItsTangent !-------------------------------------------------------------------------------------------------- -!> @brief returns homogenized thermal conductivity in reference configuration +!> @brief return homogenized thermal conductivity in reference configuration !-------------------------------------------------------------------------------------------------- function thermal_conduction_getConductivity(ip,el) diff --git a/src/thermal_isothermal.f90 b/src/thermal_isothermal.f90 index 630e8db6d..ceb714740 100644 --- a/src/thermal_isothermal.f90 +++ b/src/thermal_isothermal.f90 @@ -8,14 +8,14 @@ module thermal_isothermal implicit none public - + contains !-------------------------------------------------------------------------------------------------- -!> @brief allocates all neccessary fields, reads information from material configuration file +!> @brief allocates fields, reads information from material configuration file !-------------------------------------------------------------------------------------------------- subroutine thermal_isothermal_init - + integer :: h,NofMyHomog write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_isothermal_label//' init -+>>>'; flush(6) @@ -28,7 +28,7 @@ subroutine thermal_isothermal_init allocate(thermalState(h)%state0 (0,NofMyHomog)) allocate(thermalState(h)%subState0(0,NofMyHomog)) allocate(thermalState(h)%state (0,NofMyHomog)) - + deallocate(temperature (h)%p) allocate (temperature (h)%p(1), source=thermal_initialT(h)) deallocate(temperatureRate(h)%p) From bddb514072f2803af2abae4465a6a147c58f1010 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 26 Jun 2020 11:45:06 +0200 Subject: [PATCH 19/78] more systematic reporting --- python/damask/_result.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/python/damask/_result.py b/python/damask/_result.py index 354c1c3f7..e81b0f1c3 100644 --- a/python/damask/_result.py +++ b/python/damask/_result.py @@ -1,4 +1,4 @@ -import multiprocessing +import multiprocessing as mp import re import inspect import glob @@ -413,17 +413,18 @@ class Result: for i in self.iterate('increments'): message += f'\n{i} ({self.times[self.increments.index(i)]}s)\n' for o,p in zip(['constituents','materialpoints'],['con_physics','mat_physics']): + message += f' {o[:-1]}\n' for oo in self.iterate(o): - message += f' {oo}\n' + message += f' {oo}\n' for pp in self.iterate(p): - message += f' {pp}\n' + message += f' {pp}\n' group = '/'.join([i,o[:-1],oo,pp]) # o[:-1]: plural/singular issue for d in f[group].keys(): try: dataset = f['/'.join([group,d])] unit = f" / {dataset.attrs['Unit'].decode()}" if 'Unit' in dataset.attrs else '' description = dataset.attrs['Description'].decode() - message += f' {d}{unit}: {description}\n' + message += f' {d}{unit}: {description}\n' except KeyError: pass return message @@ -1066,8 +1067,8 @@ class Result: """ num_threads = Environment().options['DAMASK_NUM_THREADS'] - pool = multiprocessing.Pool(int(num_threads) if num_threads is not None else None) - lock = multiprocessing.Manager().Lock() + pool = mp.Pool(int(num_threads) if num_threads is not None else None) + lock = mp.Manager().Lock() groups = self.groups_with_datasets(datasets.values()) default_arg = partial(self._job,func=func,datasets=datasets,args=args,lock=lock) From bfae88a364b2c738301ea4dbe403eb4a2fabc0da Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 26 Jun 2020 11:45:30 +0200 Subject: [PATCH 20/78] numpy compatible --- 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 335d28b3b..305460a5f 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -155,7 +155,7 @@ class Rotation: def broadcast_to(self,shape): - if isinstance(shape,int): shape = (shape,) + if isinstance(shape,(int,np.integer)): shape = (shape,) if self.shape == (): q = np.broadcast_to(self.quaternion,shape+(4,)) else: From a69f82e7c3bcd884257d801a26b16fc8aad37e0b Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 26 Jun 2020 11:45:54 +0200 Subject: [PATCH 21/78] speed up vtk out - limit to single precision - write in background --- python/damask/_vtk.py | 26 +++++++++++++++++++++----- python/tests/test_VTK.py | 28 +++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index f4855820e..6af4344f3 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -1,3 +1,4 @@ +import multiprocessing as mp from pathlib import Path import pandas as pd @@ -157,8 +158,11 @@ class VTK: return VTK(geom) - - def write(self,fname): + @staticmethod + def _write(writer): + """Wrapper for parallel writing.""" + writer.Write() + def write(self,fname,parallel=True): """ Write to file. @@ -166,6 +170,8 @@ class VTK: ---------- fname : str Filename for writing. + parallel : boolean, optional + Write data in parallel background process. Defaults to True. """ if isinstance(self.geom,vtk.vtkRectilinearGrid): @@ -183,8 +189,11 @@ class VTK: writer.SetCompressorTypeToZLib() writer.SetDataModeToBinary() writer.SetInputData(self.geom) - - writer.Write() + if parallel: + mp_writer = mp.Process(target=self._write,args=(writer,)) + mp_writer.start() + else: + writer.Write() # Check https://blog.kitware.com/ghost-and-blanking-visibility-changes/ for missing data @@ -195,14 +204,21 @@ class VTK: N_cells = self.geom.GetNumberOfCells() if isinstance(data,np.ndarray): - d = np_to_vtk(num_array=data.reshape(data.shape[0],-1),deep=True) if label is None: raise ValueError('No label defined for numpy.ndarray') + + if data.dtype in [np.float64, np.float128]: # avoid large files + d = np_to_vtk(num_array=data.astype(np.float32).reshape(data.shape[0],-1),deep=True) + else: + d = np_to_vtk(num_array=data.reshape(data.shape[0],-1),deep=True) d.SetName(label) + if data.shape[0] == N_cells: self.geom.GetCellData().AddArray(d) elif data.shape[0] == N_points: self.geom.GetPointData().AddArray(d) + else: + raise ValueError(f'Invalid shape {data.shape[0]}') elif isinstance(data,pd.DataFrame): raise NotImplementedError('pd.DataFrame') elif isinstance(data,Table): diff --git a/python/tests/test_VTK.py b/python/tests/test_VTK.py index 8795e7161..395102950 100644 --- a/python/tests/test_VTK.py +++ b/python/tests/test_VTK.py @@ -1,4 +1,6 @@ import os +import filecmp +import time import pytest import numpy as np @@ -18,7 +20,7 @@ class TestVTK: origin = np.random.random(3) v = VTK.from_rectilinearGrid(grid,size,origin) string = v.__repr__() - v.write(os.path.join(tmp_path,'rectilinearGrid')) + v.write(os.path.join(tmp_path,'rectilinearGrid'),False) vtr = VTK.from_file(os.path.join(tmp_path,'rectilinearGrid.vtr')) with open(os.path.join(tmp_path,'rectilinearGrid.vtk'),'w') as f: f.write(string) @@ -26,10 +28,10 @@ class TestVTK: assert(string == vtr.__repr__() == vtk.__repr__()) def test_polyData(self,tmp_path): - points = np.random.rand(3,100) + points = np.random.rand(100,3) v = VTK.from_polyData(points) string = v.__repr__() - v.write(os.path.join(tmp_path,'polyData')) + v.write(os.path.join(tmp_path,'polyData'),False) vtp = VTK.from_file(os.path.join(tmp_path,'polyData.vtp')) with open(os.path.join(tmp_path,'polyData.vtk'),'w') as f: f.write(string) @@ -48,14 +50,30 @@ class TestVTK: connectivity = np.random.choice(np.arange(n),n,False).reshape(-1,n) v = VTK.from_unstructuredGrid(nodes,connectivity,cell_type) string = v.__repr__() - v.write(os.path.join(tmp_path,'unstructuredGrid')) + v.write(os.path.join(tmp_path,'unstructuredGrid'),False) vtu = VTK.from_file(os.path.join(tmp_path,'unstructuredGrid.vtu')) with open(os.path.join(tmp_path,'unstructuredGrid.vtk'),'w') as f: f.write(string) vtk = VTK.from_file(os.path.join(tmp_path,'unstructuredGrid.vtk'),'unstructuredgrid') assert(string == vtu.__repr__() == vtk.__repr__()) - @pytest.mark.parametrize('name,dataset_type',[('this_file_does_not_exist.vtk',None), + + def test_parallel_out(self,tmp_path): + points = np.random.rand(102,3) + v = VTK.from_polyData(points) + fname_s = os.path.join(tmp_path,'single.vtp') + fname_p = os.path.join(tmp_path,'parallel.vtp') + v.write(fname_s,False) + v.write(fname_p,True) + for i in range(10): + if os.path.isfile(fname_p) and filecmp.cmp(fname_s,fname_p): + assert(True) + return + time.sleep(.5) + assert(False) + + + @pytest.mark.parametrize('name,dataset_type',[('this_file_does_not_exist.vtk', None), ('this_file_does_not_exist.vtk','vtk'), ('this_file_does_not_exist.vtx', None)]) def test_invalid_dataset_type(self,dataset_type,name): From 1f9b5280cb95351c066afc87a7c9c1627e096750 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 26 Jun 2020 11:47:04 +0200 Subject: [PATCH 22/78] forgotten debug statement --- python/damask/_result.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/damask/_result.py b/python/damask/_result.py index e81b0f1c3..35879ec80 100644 --- a/python/damask/_result.py +++ b/python/damask/_result.py @@ -704,7 +704,6 @@ class Result: label,p = 'intermediate',1 elif eigenvalue == 'min': label,p = 'minimum',0 - print('p',eigenvalue) return { 'data': mechanics.eigenvectors(T_sym['data'])[:,p], 'label': f"v_{eigenvalue}({T_sym['label']})", From e779e190ea42ecbb05b55adc4f3dc3dd60e8c291 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 27 Jun 2020 16:01:16 +0200 Subject: [PATCH 23/78] new colormap class for use in python - based on matplotlib "ListedColormap" - constructors - Array of RGB values (inherited), - 'from_bounds': perceptual uniform colormap within given bounds - 'from_predefined': from matplotlib or DAMASK templates - export to files (WIP) - preview on screen --- processing/post/perceptualUniformColorMap.py | 75 --- python/damask/__init__.py | 2 +- python/damask/_colormap.py | 482 +++++++++++++++++ python/damask/_colormaps.py | 541 ------------------- python/tests/test_Colormap.py | 40 ++ 5 files changed, 523 insertions(+), 617 deletions(-) delete mode 100755 processing/post/perceptualUniformColorMap.py create mode 100644 python/damask/_colormap.py delete mode 100644 python/damask/_colormaps.py create mode 100644 python/tests/test_Colormap.py diff --git a/processing/post/perceptualUniformColorMap.py b/processing/post/perceptualUniformColorMap.py deleted file mode 100755 index 8e432536d..000000000 --- a/processing/post/perceptualUniformColorMap.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python3 - -import os -import sys -from optparse import OptionParser - -import damask - - -scriptName = os.path.splitext(os.path.basename(__file__))[0] -scriptID = ' '.join([scriptName,damask.version]) - - -# -------------------------------------------------------------------- -# MAIN -# -------------------------------------------------------------------- -#Borland, D., & Taylor, R. M. (2007). Rainbow Color Map (Still) Considered Harmful. Computer Graphics and Applications, IEEE, 27(2), 14--17. -#Moreland, K. (2009). Diverging Color Maps for Scientific Visualization. In Proc. 5th Int. Symp. Visual Computing (pp. 92--103). -outtypes = ['paraview','gmsh','raw','GOM'] -extensions = ['.json','.msh','.txt','.legend'] -colormodels = ['RGB','HSL','XYZ','CIELAB','MSH'] - -parser = OptionParser(option_class=damask.extendableOption, usage='%prog options [file[s]]', description = """ -Produces perceptually linear diverging and sequential colormaps in formats suitable for visualization software -or simply as a list of interpolated colors. - -""", version = scriptID) - -parser.add_option('-N','--steps', dest='N', type='int', nargs=1, metavar='int', - help='number of interpolation steps [%default]') -parser.add_option('-t','--trim', dest='trim', type='float', nargs=2, metavar='float float', - help='relative trim of colormap range [%default]') -parser.add_option('-l','--left', dest='left', type='float', nargs=3, metavar='float float float', - help='left color [%default]') -parser.add_option('-r','--right', dest='right', type='float', nargs=3, metavar='float float float', - help='right color [%default]') -parser.add_option('-c','--colormodel', dest='colormodel', metavar='string', - help='colormodel: '+', '.join(colormodels)+' [%default]') -parser.add_option('-p','--predefined', dest='predefined', metavar='string', - help='predefined colormap') -parser.add_option('-f','--format', dest='format', metavar='string', - help='output format: '+', '.join(outtypes)+' [%default]') -parser.set_defaults(colormodel = 'RGB') -parser.set_defaults(predefined = None) -parser.set_defaults(basename = None) -parser.set_defaults(format = 'paraview') -parser.set_defaults(N = 10) -parser.set_defaults(trim = (-1.0,1.0)) -parser.set_defaults(left = (1.0,1.0,1.0)) -parser.set_defaults(right = (0.0,0.0,0.0)) - -(options,filename) = parser.parse_args() - -if options.format not in outtypes: - parser.error('invalid format: "{}" (choices: {}).'.format(options.format,', '.join(outtypes))) - -if options.N < 2: - parser.error('too few steps (need at least 2).') - -if options.trim[0] < -1.0 or \ - options.trim[1] > 1.0 or \ - options.trim[0] >= options.trim[1]: - parser.error('invalid trim range (-1 +1).') - -name = options.format if filename == [] \ - else filename[0] -output = sys.stdout if filename == [] \ - else open(os.path.basename(filename[0])+extensions[outtypes.index(options.format)],'w') - -colorLeft = damask.Color(options.colormodel.upper(), list(options.left)) -colorRight = damask.Color(options.colormodel.upper(), list(options.right)) -colormap = damask.Colormap(colorLeft, colorRight, predefined=options.predefined) - -output.write(colormap.export(name,options.format,options.N,list(options.trim))) -output.close() diff --git a/python/damask/__init__.py b/python/damask/__init__.py index 6d65f7cd1..b2b94b6a5 100644 --- a/python/damask/__init__.py +++ b/python/damask/__init__.py @@ -10,7 +10,7 @@ with open(_Path(__file__).parent/_Path('VERSION')) as _f: from ._environment import Environment # noqa from ._table import Table # noqa from ._vtk import VTK # noqa -from ._colormaps import Colormap, Color # noqa +from ._colormap import Colormap # noqa from ._rotation import Rotation # noqa from ._lattice import Symmetry, Lattice# noqa from ._orientation import Orientation # noqa diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py new file mode 100644 index 000000000..20b1a015f --- /dev/null +++ b/python/damask/_colormap.py @@ -0,0 +1,482 @@ +import json +import functools + +import numpy as np +import matplotlib as mpl +import matplotlib.pyplot as plt +from matplotlib import cm + +from damask import Table + +_eps = 216./24389. +_kappa = 24389./27. +_ref_white = np.array([.95047, 1.00000, 1.08883]) # Observer = 2, Illuminant = D65 + + +class Colormap(mpl.colors.ListedColormap): + + + @staticmethod + def from_bounds(low,high,N=256,name='DAMASK colormap',model='rgb'): + """ + Create a perceptually uniform colormap. + + Parameters + ---------- + low : numpy.ndarray of shape (3) + high : numpy.ndarray of shape (3) + N : integer, optional + Number of discrete color values. Defaults to 256. + model : str + Colormodel used for low and high. + + """ + low_,high_ = map(np.array,[low,high]) + if model.lower() == 'rgb': + # ToDo: Sanity check + low_,high_ = map(Colormap._rgb2msh,[low_,high_]) + elif model.lower() == 'hsv': + # ToDo: Sanity check + low_,high_ = map(Colormap._hsv2msh,[low_,high_]) + elif model.lower() == 'hsl': + # ToDo: Sanity check + low_,high_ = map(Colormap._hsl2msh,[low_,high_]) + elif model.lower() == 'xyz': + # ToDo: Sanity check + low_,high_ = map(Colormap._xyz2msh,[low_,high_]) + elif model.lower() == 'lab': + # ToDo: Sanity check + low_,high_ = map(Colormap._lab2msh,[low_,high_]) + elif model.lower() == 'msh': + # ToDo: Sanity check + pass + else: + raise ValueError(f'Invalid color model: {model}.') + + msh = map(functools.partial(Colormap._interpolate_msh,low=low_,high=high_),np.linspace(0,1,N)) + rgb = np.array(list(map(Colormap._msh2rgb,msh))) + + return Colormap(rgb,name=name) + + + @staticmethod + def from_predefined(name,N=256): + """ + Select from set of predefined colormaps. + + Predefined colormaps include matplotlib-native colormaps + and common DAMASK colormaps. + + Parameters + ---------- + name : str + N : int, optional + Number of discrete color values. Defaults to 256. + This parameter is not used for matplotlib colormaps + that are of type `ListedColormap`. + + """ + # matplotlib presets + for cat in Colormap._predefined_mpl: + for n in cat[1]: + if n == name: + colormap = cm.__dict__[name] + if isinstance(colormap,mpl.colors.LinearSegmentedColormap): + return Colormap(np.array(list(map(colormap,np.linspace(0,1,N)))),name=name) + else: + return Colormap(colormap.colors,name=name) + + # DAMASK presets + definition = Colormap._predefined_DAMASK[name] + return Colormap.from_bounds(definition['left'],definition['right'],N,name) + + + @staticmethod + def list_predefined(): + """List predefined colormaps by category.""" + print('DAMASK colormaps') + print(' '+', '.join(Colormap._predefined_DAMASK.keys())) + for cat in Colormap._predefined_mpl: + print(f'{cat[0]}') + print(' '+', '.join(cat[1])) + + + def show(self): + fig, ax = plt.subplots(figsize=(10,1)) + ax.set_axis_off() + im = ax.imshow(np.broadcast_to(np.linspace(0,1,640).reshape(1,-1),(64,640)),cmap=self) # noqa + fig.canvas.set_window_title(self.name) + plt.show() + + + def to_file(self,fname=None,format='paraview'): + if fname is not None: + try: + f = open(fname,'w') + except TypeError: + f = fname + else: + f = None + + if format.lower() == 'paraview': + Colormap._export_paraview(self,f) + elif format.lower() == 'ascii': + Colormap._export_ASCII(self,f) + + + @staticmethod + def _export_paraview(colormap,fhandle=None): + colors = [] + for i,c in enumerate(np.round(colormap.colors,6).tolist()): + colors+=[i]+c + + out = [{ + 'ColorSpace':'RGB', + 'Name':colormap.name, + 'DefaultMap':True, + 'RGBPoints':colors + }] + if fhandle is None: + with open(colormap.name.replace(' ','_')+'.json', 'w') as f: + json.dump(out, f,indent=4) + else: + json.dump(out,fhandle,indent=4) + + @staticmethod + def _export_ASCII(colormap,fhandle=None): + labels = {'R':(1,),'G':(1,),'B':(1,)} + if colormap.colors.shape[1] == 4: labels['alpha']=(1,) + + t = Table(colormap.colors,labels) + if fhandle is None: + with open(colormap.name.replace(' ','_')+'.txt', 'w') as f: + t.to_ASCII(f) + else: + t.to_ASCII(fhandle) + + @staticmethod + def _export_GOM(colormap,fhandle=None): + pass + # a = f'1 1 {name.replace(" ","_"} 9 {name.replace(" ","_"} ' + # f' 0 1 0 3 0 0 -1 9 \\ 0 0 0 255 255 255 0 0 255 ' + # f'30 NO_UNIT 1 1 64 64 64 255 1 0 0 0 0 0 0 3 0 ' + str(len(colors)) + # f' '.join([' 0 %s 255 1'%(' '.join([str(int(x*255.0)) for x in color])) for color in reversed(colors)])] + + @staticmethod + def _export_gmsh(colormap,fname=None): + colors = colormap.colors + colormap = ['View.ColorTable = {'] \ + + [',\n'.join(['{%s}'%(','.join([str(x*255.0) for x in color])) for color in colors])] \ + + ['}'] + + @staticmethod + def _interpolate_msh(frac,low, high): + + def rad_diff(a,b): + return abs(a[2]-b[2]) + + def adjust_hue(msh_sat, msh_unsat): + """If saturation of one of the two colors is much less than the other, hue of the less.""" + if msh_sat[0] >= msh_unsat[0]: + return msh_sat[2] + else: + hSpin = msh_sat[1]/np.sin(msh_sat[1])*np.sqrt(msh_unsat[0]**2.0-msh_sat[0]**2)/msh_sat[0] + if msh_sat[2] < - np.pi/3.0: hSpin *= -1.0 + return msh_sat[2] + hSpin + + + lo = np.array(low) + hi = np.array(high) + + if (lo[1] > 0.05 and hi[1] > 0.05 and rad_diff(lo,hi) > np.pi/3.0): + M_mid = max(lo[0],hi[0],88.0) + if frac < 0.5: + hi = np.array([M_mid,0.0,0.0]) + frac *= 2.0 + else: + lo = np.array([M_mid,0.0,0.0]) + frac = 2.0*frac - 1.0 + if lo[1] < 0.05 and hi[1] > 0.05: + lo[2] = adjust_hue(hi,lo) + elif lo[1] > 0.05 and hi[1] < 0.05: + hi[2] = adjust_hue(lo,hi) + + return (1.0 - frac) * lo + frac * hi + + + _predefined_mpl= [('Perceptually Uniform Sequential', [ + 'viridis', 'plasma', 'inferno', 'magma', 'cividis']), + ('Sequential', [ + 'Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds', + 'YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu', + 'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn']), + ('Sequential (2)', [ + 'binary', 'gist_yarg', 'gist_gray', 'gray', 'bone', 'pink', + 'spring', 'summer', 'autumn', 'winter', 'cool', 'Wistia', + 'hot', 'afmhot', 'gist_heat', 'copper']), + ('Diverging', [ + 'PiYG', 'PRGn', 'BrBG', 'PuOr', 'RdGy', 'RdBu', + 'RdYlBu', 'RdYlGn', 'Spectral', 'coolwarm', 'bwr', 'seismic']), + ('Cyclic', ['twilight', 'twilight_shifted', 'hsv']), + ('Qualitative', [ + 'Pastel1', 'Pastel2', 'Paired', 'Accent', + 'Dark2', 'Set1', 'Set2', 'Set3', + 'tab10', 'tab20', 'tab20b', 'tab20c']), + ('Miscellaneous', [ + 'flag', 'prism', 'ocean', 'gist_earth', 'terrain', 'gist_stern', + 'gnuplot', 'gnuplot2', 'CMRmap', 'cubehelix', 'brg', + 'gist_rainbow', 'rainbow', 'jet', 'nipy_spectral', 'gist_ncar'])] + + _predefined_DAMASK = {'orientation': {'low': [0.933334,0.878432,0.878431], + 'right': [0.250980,0.007843,0.000000]}, + 'strain': {'left': [0.941177,0.941177,0.870588], + 'right': [0.266667,0.266667,0.000000]}, + 'stress': {'left': [0.878432,0.874511,0.949019], + 'right': [0.000002,0.000000,0.286275]}} + + @staticmethod + def _hsv2rgb(hsv): + """ + H(ue) S(aturation) V(alue) to R(red) G(reen) B(lue). + + References + ---------- + https://www.rapidtables.com/convert/color/hsv-to-rgb.html + + """ + sextant = np.clip(int(hsv[0]/60.),0,5) + c = hsv[1]*hsv[2] + x = c*(1.0 - abs((hsv[0]/60.)%2 - 1.)) + + return np.array([ + [c, x, 0], + [x, c, 0], + [0, c, x], + [0, x, c], + [x, 0, c], + [c, 0, x], + ])[sextant] + hsv[2] - c + + @staticmethod + def _rgb2hsv(rgb): + """ + R(ed) G(reen) B(lue) to H(ue) S(aturation) V(alue). + + References + ---------- + https://www.rapidtables.com/convert/color/rgb-to-hsv.html + + """ + C_max = rgb.max() + C_min = rgb.min() + Delta = C_max - C_min + + v = C_max + s = 0. if np.isclose(C_max,0.) else Delta/C_max + if np.isclose(Delta,0.): + h = 0. + elif rgb.argmax() == 0: + h = (rgb[1]-rgb[2])/Delta%6 + elif rgb.argmax() == 1: + h = (rgb[2]-rgb[0])/Delta + 2. + elif rgb.argmax() == 2: + h = (rgb[0]-rgb[1])/Delta + 4. + + h = np.clip(h,0.,6.) * 60. + + return np.array([h,s,v]) + + + @staticmethod + def _hsl2rgb(hsl): + """ + H(ue) S(aturation) L(uminance) to R(red) G(reen) B(lue). + + References + ---------- + https://www.rapidtables.com/convert/color/hsl-to-rgb.html + + """ + sextant = np.clip(int(hsl[0]/60.),0,5) + c = (1.0 - abs(2.0 * hsl[2] - 1.))*hsl[1] + x = c*(1.0 - abs((hsl[0]/60.)%2 - 1.)) + m = hsl[2] - 0.5*c + + return np.array([ + [c+m, x+m, m], + [x+m, c+m, m], + [m, c+m, x+m], + [m, x+m, c+m], + [x+m, m, c+m], + [c+m, m, x+m], + ])[sextant] + + @staticmethod + def _rgb2hsl(rgb): + """ + R(ed) G(reen) B(lue) to H(ue) S(aturation) L(uminance). + + References + ---------- + https://www.rapidtables.com/convert/color/rgb-to-hsl.html + + """ + C_max = rgb.max() + C_min = rgb.min() + Delta = C_max - C_min + + l = np.clip((C_max + C_min)*.5,0.,1.) # noqa + s = 0. if np.isclose(C_max,C_min) else Delta/(1.-np.abs(2*l-1.)) + if np.isclose(Delta,0.): + h = 0. + elif rgb.argmax() == 0: + h = (rgb[1]-rgb[2])/Delta%6 + elif rgb.argmax() == 1: + h = (rgb[2]-rgb[0])/Delta + 2. + elif rgb.argmax() == 2: + h = (rgb[0]-rgb[1])/Delta + 4. + + h = np.clip(h,0.,6.) * 60. + + return np.array([h,s,l]) + + + @staticmethod + def _xyz2rgb(xyz): + """ + CIE Xyz to R(ed) G(reen) B(lue). + + References + ---------- + http://www.ryanjuckett.com/programming/rgb-color-space-conversion + + """ + rgb_lin = np.dot(np.array([ + [ 3.240969942,-1.537383178,-0.498610760], + [-0.969243636, 1.875967502, 0.041555057], + [ 0.055630080,-0.203976959, 1.056971514] + ]),xyz) + with np.errstate(invalid='ignore'): + rgb = np.where(rgb_lin>0.0031308,rgb_lin**(1.0/2.4)*1.0555-0.0555,rgb_lin*12.92) + + return np.clip(rgb,0.,1.) + + @staticmethod + def _rgb2xyz(rgb): + """ + R(ed) G(reen) B(lue) to CIE Xyz. + + References + ---------- + http://www.ryanjuckett.com/programming/rgb-color-space-conversion + + """ + rgb_lin = np.where(rgb>0.04045,((rgb+0.0555)/1.0555)**2.4,rgb/12.92) + return np.dot(np.array([ + [0.412390799,0.357584339,0.180480788], + [0.212639006,0.715168679,0.072192315], + [0.019330819,0.119194780,0.950532152] + ]),rgb_lin) + + + @staticmethod + def _lab2xyz(lab,ref_white=None): + """ + CIE Lab to CIE Xyz. + + References + ---------- + http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html + + """ + f_x = (lab[0]+16.)/116. + lab[1]/500. + f_z = (lab[0]+16.)/116. - lab[2]/200. + + return np.array([ + f_x**3. if f_x**3. > _eps else (116.*f_x-16.)/_kappa, + ((lab[0]+16.)/116.)**3 if lab[0]>_kappa*_eps else lab[0]/_kappa, + f_z**3. if f_z**3. > _eps else (116.*f_z-16.)/_kappa + ])*(ref_white if ref_white is not None else _ref_white) + + @staticmethod + def _xyz2lab(xyz,ref_white=None): + """ + CIE Xyz to CIE Lab. + + References + ---------- + http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html + + """ + ref_white = ref_white if ref_white is not None else _ref_white + f = np.where(xyz/ref_white > _eps,(xyz/ref_white)**(1./3.),(_kappa*xyz/ref_white+16.)/116.) + + return np.array([ + 116.0 * f[1] - 16.0, + 500.0 * (f[0] - f[1]), + 200.0 * (f[1] - f[2]) + ]) + + + @staticmethod + def _lab2msh(lab): + """ + CIE Lab to Msh. + + References + ---------- + https://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf + https://www.kennethmoreland.com/color-maps/diverging_map.py + + """ + M = np.linalg.norm(lab) + return np.array([ + M, + np.arccos(lab[0]/M) if M>1e-8 else 0., + np.arctan2(lab[2],lab[1]) if M>1e-8 else 0., + ]) + + @staticmethod + def _msh2lab(msh): + """ + Msh to CIE Lab. + + References + ---------- + https://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf + https://www.kennethmoreland.com/color-maps/diverging_map.py + + """ + return np.array([ + msh[0] * np.cos(msh[1]), + msh[0] * np.sin(msh[1]) * np.cos(msh[2]), + msh[0] * np.sin(msh[1]) * np.sin(msh[2]) + ]) + + @staticmethod + def _lab2rgb(lab): + return Colormap._xyz2rgb(Colormap._lab2xyz(lab)) + + @staticmethod + def _rgb2lab(rgb): + return Colormap._xyz2lab(Colormap._rgb2xyz(rgb)) + + @staticmethod + def _msh2rgb(msh): + return Colormap._lab2rgb(Colormap._msh2lab(msh)) + + @staticmethod + def _rgb2msh(rgb): + return Colormap._lab2msh(Colormap._rgb2lab(rgb)) + + @staticmethod + def _hsv2msh(hsv): + return Colormap._rgb2msh(Colormap._hsv2rgb(hsv)) + + @staticmethod + def _hsl2msh(hsl): + return Colormap._rgb2msh(Colormap._hsl2rgb(hsl)) + + @staticmethod + def _xyz2msh(xyz): + return Colormap._lab2msh(Colormap._xyz2lab(xyz)) diff --git a/python/damask/_colormaps.py b/python/damask/_colormaps.py deleted file mode 100644 index 6c6f82604..000000000 --- a/python/damask/_colormaps.py +++ /dev/null @@ -1,541 +0,0 @@ -import numpy as np -from . import util - -class Color: - """Color representation in and conversion between different color-spaces.""" - - __slots__ = [ - 'model', - 'color', - '__dict__', - ] - - - def __init__(self, - model = 'RGB', - color = np.zeros(3)): - """ - Create a Color object. - - Parameters - ---------- - model : string - color model - color : numpy.ndarray - vector representing the color according to the selected model - - """ - self.__transforms__ = \ - {'HSV': {'index': 0, 'next': self._HSV2HSL}, - 'HSL': {'index': 1, 'next': self._HSL2RGB, 'prev': self._HSL2HSV}, - 'RGB': {'index': 2, 'next': self._RGB2XYZ, 'prev': self._RGB2HSL}, - 'XYZ': {'index': 3, 'next': self._XYZ2CIELAB, 'prev': self._XYZ2RGB}, - 'CIELAB': {'index': 4, 'next': self._CIELAB2MSH, 'prev': self._CIELAB2XYZ}, - 'MSH': {'index': 5, 'prev': self._MSH2CIELAB}, - } - - model = model.upper() - if model not in list(self.__transforms__.keys()): model = 'RGB' - if model == 'RGB' and max(color) > 1.0: # are we RGB255 ? - for i in range(3): - color[i] /= 255.0 # rescale to RGB - - if model == 'HSL': # are we HSL ? - if abs(color[0]) > 1.0: color[0] /= 360.0 # with angular hue? - while color[0] >= 1.0: color[0] -= 1.0 # rewind to proper range - while color[0] < 0.0: color[0] += 1.0 # rewind to proper range - - self.model = model - self.color = np.array(color,'d') - - - def __repr__(self): - """Color model and values.""" - return 'Model: %s Color: %s'%(self.model,str(self.color)) - - - def __str__(self): - """Color model and values.""" - return self.__repr__() - - - def convert_to(self,toModel = 'RGB'): - """ - Change the color model permanently. - - Parameters - ---------- - toModel : string - color model - - """ - toModel = toModel.upper() - if toModel not in list(self.__transforms__.keys()): return - - sourcePos = self.__transforms__[self.model]['index'] - targetPos = self.__transforms__[toModel]['index'] - - while sourcePos < targetPos: - self.__transforms__[self.model]['next']() - sourcePos += 1 - - while sourcePos > targetPos: - self.__transforms__[self.model]['prev']() - sourcePos -= 1 - return self - - - def express_as(self,asModel = 'RGB'): - """ - Return the color in a different model. - - Parameters - ---------- - asModel : string - color model - - """ - return self.__class__(self.model,self.color).convert_to(asModel) - - - def _HSV2HSL(self): - """ - Convert H(ue) S(aturation) V(alue or brightness) to H(ue) S(aturation) L(uminance). - - All values are in the range [0,1] - http://codeitdown.com/hsl-hsb-hsv-color - """ - if self.model != 'HSV': return - - converted = Color('HSL',np.array([ - self.color[0], - 1. if self.color[2] == 0.0 or (self.color[1] == 0.0 and self.color[2] == 1.0) \ - else self.color[1]*self.color[2]/(1.-abs(self.color[2]*(2.-self.color[1])-1.)), - 0.5*self.color[2]*(2.-self.color[1]), - ])) - - self.model = converted.model - self.color = converted.color - - - def _HSL2HSV(self): - """ - Convert H(ue) S(aturation) L(uminance) to H(ue) S(aturation) V(alue or brightness). - - All values are in the range [0,1] - http://codeitdown.com/hsl-hsb-hsv-color - """ - if self.model != 'HSL': return - - h = self.color[0] - b = self.color[2]+0.5*(self.color[1]*(1.-abs(2*self.color[2]-1))) - s = 1.0 if b == 0.0 else 2.*(b-self.color[2])/b - - converted = Color('HSV',np.array([h,s,b])) - - self.model = converted.model - self.color = converted.color - - - def _HSL2RGB(self): - """ - Convert H(ue) S(aturation) L(uminance) to R(red) G(reen) B(lue). - - All values are in the range [0,1] - from http://en.wikipedia.org/wiki/HSL_and_HSV - """ - if self.model != 'HSL': return - - sextant = self.color[0]*6.0 - c = (1.0 - abs(2.0 * self.color[2] - 1.0))*self.color[1] - x = c*(1.0 - abs(sextant%2 - 1.0)) - m = self.color[2] - 0.5*c - - converted = Color('RGB',np.array([ - [c+m, x+m, m], - [x+m, c+m, m], - [m, c+m, x+m], - [m, x+m, c+m], - [x+m, m, c+m], - [c+m, m, x+m], - ][int(sextant)])) - self.model = converted.model - self.color = converted.color - - - def _RGB2HSL(self): - """ - Convert R(ed) G(reen) B(lue) to H(ue) S(aturation) L(uminance). - - All values are in the range [0,1] - from http://130.113.54.154/~monger/hsl-rgb.html - """ - if self.model != 'RGB': return - - HSL = np.zeros(3) - maxcolor = self.color.max() - mincolor = self.color.min() - HSL[2] = (maxcolor + mincolor)/2.0 - if(mincolor == maxcolor): - HSL[0] = 0.0 - HSL[1] = 0.0 - else: - if (HSL[2]<0.5): - HSL[1] = (maxcolor - mincolor)/(maxcolor + mincolor) - else: - HSL[1] = (maxcolor - mincolor)/(2.0 - maxcolor - mincolor) - if (maxcolor == self.color[0]): - HSL[0] = 0.0 + (self.color[1] - self.color[2])/(maxcolor - mincolor) - elif (maxcolor == self.color[1]): - HSL[0] = 2.0 + (self.color[2] - self.color[0])/(maxcolor - mincolor) - elif (maxcolor == self.color[2]): - HSL[0] = 4.0 + (self.color[0] - self.color[1])/(maxcolor - mincolor) - HSL[0] = HSL[0]*60.0 # scaling to 360 might be dangerous for small values - if (HSL[0] < 0.0): - HSL[0] = HSL[0] + 360.0 - HSL[1:] = np.clip(HSL[1:],0.0,1.0) - - converted = Color('HSL', HSL) - self.model = converted.model - self.color = converted.color - - - def _RGB2XYZ(self): - """ - Convert R(ed) G(reen) B(lue) to CIE XYZ. - - All values are in the range [0,1] - from http://www.cs.rit.edu/~ncs/color/t_convert.html - """ - if self.model != 'RGB': return - - XYZ = np.zeros(3) - RGB_lin = np.zeros(3) - convert = np.array([[0.412453,0.357580,0.180423], - [0.212671,0.715160,0.072169], - [0.019334,0.119193,0.950227]]) - - for i in range(3): - if (self.color[i] > 0.04045): RGB_lin[i] = ((self.color[i]+0.0555)/1.0555)**2.4 - else: RGB_lin[i] = self.color[i] /12.92 - XYZ = np.dot(convert,RGB_lin) - XYZ = np.clip(XYZ,0.0,None) - - converted = Color('XYZ', XYZ) - self.model = converted.model - self.color = converted.color - - - def _XYZ2RGB(self): - """ - Convert CIE XYZ to R(ed) G(reen) B(lue). - - All values are in the range [0,1] - from http://www.cs.rit.edu/~ncs/color/t_convert.html - """ - if self.model != 'XYZ': return - - convert = np.array([[ 3.240479,-1.537150,-0.498535], - [-0.969256, 1.875992, 0.041556], - [ 0.055648,-0.204043, 1.057311]]) - RGB_lin = np.dot(convert,self.color) - RGB = np.zeros(3) - - for i in range(3): - if (RGB_lin[i] > 0.0031308): RGB[i] = ((RGB_lin[i])**(1.0/2.4))*1.0555-0.0555 - else: RGB[i] = RGB_lin[i] *12.92 - - RGB = np.clip(RGB,0.0,1.0) - - maxVal = max(RGB) # clipping colors according to the display gamut - if (maxVal > 1.0): RGB /= maxVal - - converted = Color('RGB', RGB) - self.model = converted.model - self.color = converted.color - - - def _CIELAB2XYZ(self): - """ - Convert CIE Lab to CIE XYZ. - - All values are in the range [0,1] - from http://www.easyrgb.com/index.php?X=MATH&H=07#text7 - """ - if self.model != 'CIELAB': return - - ref_white = np.array([.95047, 1.00000, 1.08883]) # Observer = 2, Illuminant = D65 - XYZ = np.zeros(3) - - XYZ[1] = (self.color[0] + 16.0 ) / 116.0 - XYZ[0] = XYZ[1] + self.color[1]/ 500.0 - XYZ[2] = XYZ[1] - self.color[2]/ 200.0 - - for i in range(len(XYZ)): - if (XYZ[i] > 6./29. ): XYZ[i] = XYZ[i]**3. - else: XYZ[i] = 108./841. * (XYZ[i] - 4./29.) - - converted = Color('XYZ', XYZ*ref_white) - self.model = converted.model - self.color = converted.color - - - def _XYZ2CIELAB(self): - """ - Convert CIE XYZ to CIE Lab. - - All values are in the range [0,1] - from http://en.wikipedia.org/wiki/Lab_color_space, - http://www.cs.rit.edu/~ncs/color/t_convert.html - """ - if self.model != 'XYZ': return - - ref_white = np.array([.95047, 1.00000, 1.08883]) # Observer = 2, Illuminant = D65 - XYZ = self.color/ref_white - - for i in range(len(XYZ)): - if (XYZ[i] > 216./24389 ): XYZ[i] = XYZ[i]**(1.0/3.0) - else: XYZ[i] = (841./108. * XYZ[i]) + 16.0/116.0 - - converted = Color('CIELAB', np.array([ 116.0 * XYZ[1] - 16.0, - 500.0 * (XYZ[0] - XYZ[1]), - 200.0 * (XYZ[1] - XYZ[2]) ])) - self.model = converted.model - self.color = converted.color - - - def _CIELAB2MSH(self): - """ - Convert CIE Lab to Msh colorspace. - - from http://www.cs.unm.edu/~kmorel/documents/ColorMaps/DivergingColorMapWorkshop.xls - """ - if self.model != 'CIELAB': return - - Msh = np.zeros(3) - Msh[0] = np.sqrt(np.dot(self.color,self.color)) - if (Msh[0] > 0.001): - Msh[1] = np.arccos(self.color[0]/Msh[0]) - if (self.color[1] != 0.0): - Msh[2] = np.arctan2(self.color[2],self.color[1]) - - converted = Color('MSH', Msh) - self.model = converted.model - self.color = converted.color - - - def _MSH2CIELAB(self): - """ - Convert Msh colorspace to CIE Lab. - - with s,h in radians - from http://www.cs.unm.edu/~kmorel/documents/ColorMaps/DivergingColorMapWorkshop.xls - """ - if self.model != 'MSH': return - - Lab = np.zeros(3) - Lab[0] = self.color[0] * np.cos(self.color[1]) - Lab[1] = self.color[0] * np.sin(self.color[1]) * np.cos(self.color[2]) - Lab[2] = self.color[0] * np.sin(self.color[1]) * np.sin(self.color[2]) - - converted = Color('CIELAB', Lab) - self.model = converted.model - self.color = converted.color - - -class Colormap: - """Perceptually uniform diverging or sequential colormap.""" - - __slots__ = [ - 'left', - 'right', - 'interpolate', - ] - __predefined__ = { - 'gray': {'left': Color('HSL',[0,1,1]), - 'right': Color('HSL',[0,0,0.15]), - 'interpolate': 'perceptualuniform'}, - 'grey': {'left': Color('HSL',[0,1,1]), - 'right': Color('HSL',[0,0,0.15]), - 'interpolate': 'perceptualuniform'}, - 'red': {'left': Color('HSL',[0,1,0.14]), - 'right': Color('HSL',[0,0.35,0.91]), - 'interpolate': 'perceptualuniform'}, - 'green': {'left': Color('HSL',[0.33333,1,0.14]), - 'right': Color('HSL',[0.33333,0.35,0.91]), - 'interpolate': 'perceptualuniform'}, - 'blue': {'left': Color('HSL',[0.66,1,0.14]), - 'right': Color('HSL',[0.66,0.35,0.91]), - 'interpolate': 'perceptualuniform'}, - 'seaweed': {'left': Color('HSL',[0.78,1.0,0.1]), - 'right': Color('HSL',[0.40000,0.1,0.9]), - 'interpolate': 'perceptualuniform'}, - 'bluebrown': {'left': Color('HSL',[0.65,0.53,0.49]), - 'right': Color('HSL',[0.11,0.75,0.38]), - 'interpolate': 'perceptualuniform'}, - 'redgreen': {'left': Color('HSL',[0.97,0.96,0.36]), - 'right': Color('HSL',[0.33333,1.0,0.14]), - 'interpolate': 'perceptualuniform'}, - 'bluered': {'left': Color('HSL',[0.65,0.53,0.49]), - 'right': Color('HSL',[0.97,0.96,0.36]), - 'interpolate': 'perceptualuniform'}, - 'blueredrainbow':{'left': Color('HSL',[2.0/3.0,1,0.5]), - 'right': Color('HSL',[0,1,0.5]), - 'interpolate': 'linear' }, - 'orientation': {'left': Color('RGB',[0.933334,0.878432,0.878431]), - 'right': Color('RGB',[0.250980,0.007843,0.000000]), - 'interpolate': 'perceptualuniform'}, - 'strain': {'left': Color('RGB',[0.941177,0.941177,0.870588]), - 'right': Color('RGB',[0.266667,0.266667,0.000000]), - 'interpolate': 'perceptualuniform'}, - 'stress': {'left': Color('RGB',[0.878432,0.874511,0.949019]), - 'right': Color('RGB',[0.000002,0.000000,0.286275]), - 'interpolate': 'perceptualuniform'}, - } - - - def __init__(self, - left = Color('RGB',[1,1,1]), - right = Color('RGB',[0,0,0]), - interpolate = 'perceptualuniform', - predefined = None - ): - """ - Create a Colormap object. - - Parameters - ---------- - left : Color - left color (minimum value) - right : Color - right color (maximum value) - interpolate : str - interpolation scheme (either 'perceptualuniform' or 'linear') - predefined : bool - ignore other arguments and use predefined definition - - """ - if predefined is not None: - left = self.__predefined__[predefined.lower()]['left'] - right= self.__predefined__[predefined.lower()]['right'] - interpolate = self.__predefined__[predefined.lower()]['interpolate'] - - if left.__class__.__name__ != 'Color': - left = Color() - if right.__class__.__name__ != 'Color': - right = Color() - - self.left = left - self.right = right - self.interpolate = interpolate - - - def __repr__(self): - """Left and right value of colormap.""" - return 'Left: %s Right: %s'%(self.left,self.right) - - - def invert(self): - """Switch left/minimum with right/maximum.""" - (self.left, self.right) = (self.right, self.left) - return self - - - def show_predefined(self): - """Show the labels of the predefined colormaps.""" - print('\n'.join(self.__predefined__.keys())) - - def color(self,fraction = 0.5): - - def interpolate_Msh(lo, hi, frac): - - def rad_diff(a,b): - return abs(a[2]-b[2]) - - def adjust_hue(Msh_sat, Msh_unsat): - """If saturation of one of the two colors is too less than the other, hue of the less.""" - if Msh_sat[0] >= Msh_unsat[0]: - return Msh_sat[2] - else: - hSpin = Msh_sat[1]/np.sin(Msh_sat[1])*np.sqrt(Msh_unsat[0]**2.0-Msh_sat[0]**2)/Msh_sat[0] - if Msh_sat[2] < - np.pi/3.0: hSpin *= -1.0 - return Msh_sat[2] + hSpin - - Msh1 = np.array(lo[:]) - Msh2 = np.array(hi[:]) - - if (Msh1[1] > 0.05 and Msh2[1] > 0.05 and rad_diff(Msh1,Msh2) > np.pi/3.0): - M_mid = max(Msh1[0],Msh2[0],88.0) - if frac < 0.5: - Msh2 = np.array([M_mid,0.0,0.0]) - frac *= 2.0 - else: - Msh1 = np.array([M_mid,0.0,0.0]) - frac = 2.0*frac - 1.0 - if Msh1[1] < 0.05 and Msh2[1] > 0.05: Msh1[2] = adjust_hue(Msh2,Msh1) - elif Msh1[1] > 0.05 and Msh2[1] < 0.05: Msh2[2] = adjust_hue(Msh1,Msh2) - Msh = (1.0 - frac) * Msh1 + frac * Msh2 - - return Color('MSH',Msh) - - def interpolate_linear(lo, hi, frac): - """Linear interpolation between lo and hi color at given fraction; output in model of lo color.""" - interpolation = (1.0 - frac) * np.array(lo.color[:]) \ - + frac * np.array(hi.express_as(lo.model).color[:]) - - return Color(lo.model,interpolation) - - if self.interpolate == 'perceptualuniform': - return interpolate_Msh(self.left.express_as('MSH').color, - self.right.express_as('MSH').color,fraction) - elif self.interpolate == 'linear': - return interpolate_linear(self.left,self.right,fraction) - else: - raise NameError('unknown color interpolation method') - - - def export(self,name = 'uniformPerceptualColorMap',\ - format = 'paraview',\ - steps = 2,\ - crop = [-1.0,1.0], - model = 'RGB'): - """ - [RGB] colormap for use in paraview or gmsh, or as raw string, or array. - - Arguments: name, format, steps, crop. - Format is one of (paraview, gmsh, gom, raw, list). - Crop selects a (sub)range in [-1.0,1.0]. - Generates sequential map if one limiting color is either white or black, - diverging map otherwise. - """ - format = format.lower() # consistent comparison basis - frac = 0.5*(np.array(crop) + 1.0) # rescale crop range to fractions - colors = [self.color(float(i)/(steps-1)*(frac[1]-frac[0])+frac[0]).express_as(model).color for i in range(steps)] - if format == 'paraview': - colormap = [f'[\n {{\n "ColorSpace": "RGB", "Name": "{name}", "DefaultMap": true,\n "RGBPoints" : ['] \ - + [f' {i:4d},{color[0]:8.6f},{color[1]:8.6f},{color[2]:8.6f}{"," if i+1 Date: Sat, 27 Jun 2020 16:25:10 +0200 Subject: [PATCH 24/78] reverse should return DAMASK colormap --- python/damask/_colormap.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index 20b1a015f..ded6a32a0 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -123,6 +123,11 @@ class Colormap(mpl.colors.ListedColormap): elif format.lower() == 'ascii': Colormap._export_ASCII(self,f) + def reversed(self): + rev = super(Colormap,self).reversed() + return Colormap(rev.colors,rev.name) + + @staticmethod def _export_paraview(colormap,fhandle=None): From cf6322672147580f3923d60a95134189664a4f89 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 27 Jun 2020 18:55:27 +0200 Subject: [PATCH 25/78] testing all conversions --- python/tests/test_Colormap.py | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/python/tests/test_Colormap.py b/python/tests/test_Colormap.py index daeeaf775..d3cbcaacd 100644 --- a/python/tests/test_Colormap.py +++ b/python/tests/test_Colormap.py @@ -21,10 +21,14 @@ class TestColormap: print('rgb',rgb) # rgb2hsv2rgb - assert np.allclose(Colormap._hsv2rgb(Colormap._rgb2hsv(rgb)),rgb) + hsv = Colormap._rgb2hsv(rgb) + print('hsv',hsv) + assert np.allclose(Colormap._hsv2rgb(hsv),rgb) # rgb2hsl2rgb - assert np.allclose(Colormap._hsl2rgb(Colormap._rgb2hsl(rgb)),rgb) + hsl = Colormap._rgb2hsl(rgb) + print('hsl',hsl) + assert np.allclose(Colormap._hsl2rgb(hsl),rgb) # rgb2xyz2rgb xyz = Colormap._rgb2xyz(rgb) @@ -37,4 +41,21 @@ class TestColormap: assert np.allclose(Colormap._lab2xyz(lab),xyz) # lab2msh2lab - assert np.allclose(Colormap._msh2lab(Colormap._lab2msh(lab)),lab) + msh = Colormap._lab2msh(lab) + print('msh',msh) + assert np.allclose(Colormap._msh2lab(msh),lab) + + # lab2rgb2lab + assert np.allclose(Colormap._rgb2lab(Colormap._lab2rgb(lab)),lab,atol=1.e-6,rtol=0) + + # rgb2msh2rgb + assert np.allclose(Colormap._msh2rgb(Colormap._rgb2msh(rgb)),rgb,atol=1.e-6,rtol=0) + + # hsv2msh + assert np.allclose(Colormap._hsv2msh(hsv),msh,atol=1.e-6,rtol=0) + + # hsl2msh + assert np.allclose(Colormap._hsv2msh(hsv),msh,atol=1.e-6,rtol=0) + + # xyz2msh + assert np.allclose(Colormap._xyz2msh(xyz),msh,atol=1.e-6,rtol=0) From c929af12c0e33b25fa6b60fb7fd3ecee7002d6dc Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 27 Jun 2020 19:43:35 +0200 Subject: [PATCH 26/78] testing/polishing --- python/damask/_colormap.py | 93 +++++++++++++++++++++++++---------- python/tests/test_Colormap.py | 53 +++++++++++++++++++- 2 files changed, 119 insertions(+), 27 deletions(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index ded6a32a0..9b707d00a 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -17,7 +17,7 @@ class Colormap(mpl.colors.ListedColormap): @staticmethod - def from_bounds(low,high,N=256,name='DAMASK colormap',model='rgb'): + def from_bounds(low,high,name='DAMASK colormap',N=256,model='rgb'): """ Create a perceptually uniform colormap. @@ -26,30 +26,46 @@ class Colormap(mpl.colors.ListedColormap): low : numpy.ndarray of shape (3) high : numpy.ndarray of shape (3) N : integer, optional - Number of discrete color values. Defaults to 256. + The number of color quantization levels. + name : str, optional + The name of the colormap. Defaults to `DAMASK colormap`. model : str Colormodel used for low and high. """ low_,high_ = map(np.array,[low,high]) + low_high = np.vstack((low_,high)) if model.lower() == 'rgb': - # ToDo: Sanity check + if np.any(low_high<0) or np.any(low_high>1): + raise ValueError(f'RGB color out of range {low} {high}.') + low_,high_ = map(Colormap._rgb2msh,[low_,high_]) + elif model.lower() == 'hsv': - # ToDo: Sanity check + if np.any(low_high<0) or np.any(low_high[:,1:3]>1) or np.any(low_high[:,0]>360): + raise ValueError(f'HSV color out of range {low} {high}.') + low_,high_ = map(Colormap._hsv2msh,[low_,high_]) + elif model.lower() == 'hsl': - # ToDo: Sanity check + if np.any(low_high<0) or np.any(low_high[:,1:3]>1) or np.any(low_high[:,0]>360): + raise ValueError(f'HSL color out of range {low} {high}.') + low_,high_ = map(Colormap._hsl2msh,[low_,high_]) + elif model.lower() == 'xyz': - # ToDo: Sanity check + low_,high_ = map(Colormap._xyz2msh,[low_,high_]) + elif model.lower() == 'lab': - # ToDo: Sanity check + if np.any(low_high[:,0]<0): + raise ValueError(f'CIE Lab color out of range {low} {high}.') + low_,high_ = map(Colormap._lab2msh,[low_,high_]) + elif model.lower() == 'msh': - # ToDo: Sanity check pass + else: raise ValueError(f'Invalid color model: {model}.') @@ -64,14 +80,15 @@ class Colormap(mpl.colors.ListedColormap): """ Select from set of predefined colormaps. - Predefined colormaps include matplotlib-native colormaps + Predefined colormaps include native matplotlib colormaps and common DAMASK colormaps. Parameters ---------- name : str + The name of the colormap. N : int, optional - Number of discrete color values. Defaults to 256. + The number of color quantization levels. Defaults to 256. This parameter is not used for matplotlib colormaps that are of type `ListedColormap`. @@ -84,11 +101,11 @@ class Colormap(mpl.colors.ListedColormap): if isinstance(colormap,mpl.colors.LinearSegmentedColormap): return Colormap(np.array(list(map(colormap,np.linspace(0,1,N)))),name=name) else: - return Colormap(colormap.colors,name=name) + return Colormap(np.array(colormap.colors),name=name) # DAMASK presets definition = Colormap._predefined_DAMASK[name] - return Colormap.from_bounds(definition['left'],definition['right'],N,name) + return Colormap.from_bounds(definition['left'],definition['right'],name,N) @staticmethod @@ -102,6 +119,7 @@ class Colormap(mpl.colors.ListedColormap): def show(self): + """Show colormap in window.""" fig, ax = plt.subplots(figsize=(10,1)) ax.set_axis_off() im = ax.imshow(np.broadcast_to(np.linspace(0,1,640).reshape(1,-1),(64,640)),cmap=self) # noqa @@ -109,6 +127,26 @@ class Colormap(mpl.colors.ListedColormap): plt.show() + def reversed(self,name=None): + """ + Make a reversed instance of the Colormap. + + Parameters + ---------- + name : str, optional + The name for the reversed colormap. If it's None + the name will be the name of the parent colormap + "_r". + + Returns + ------- + damask.Colormap + The reversed colormap. + + """ + rev = super(Colormap,self).reversed(name) + return Colormap(rev.colors,rev.name) + + def to_file(self,fname=None,format='paraview'): if fname is not None: try: @@ -122,15 +160,16 @@ class Colormap(mpl.colors.ListedColormap): Colormap._export_paraview(self,f) elif format.lower() == 'ascii': Colormap._export_ASCII(self,f) - - def reversed(self): - rev = super(Colormap,self).reversed() - return Colormap(rev.colors,rev.name) - - + elif format.lower() == 'gom': + Colormap._export_GOM(self,f) + elif format.lower() == 'gmsh': + Colormap._export_gmsh(self,f) + else: + raise ValueError('Unknown output format: {format}.') @staticmethod def _export_paraview(colormap,fhandle=None): + """Write colormap to JSON file for Paraview.""" colors = [] for i,c in enumerate(np.round(colormap.colors,6).tolist()): colors+=[i]+c @@ -149,10 +188,11 @@ class Colormap(mpl.colors.ListedColormap): @staticmethod def _export_ASCII(colormap,fhandle=None): + """Write colormap to ASCII table.""" labels = {'R':(1,),'G':(1,),'B':(1,)} if colormap.colors.shape[1] == 4: labels['alpha']=(1,) - t = Table(colormap.colors,labels) + if fhandle is None: with open(colormap.name.replace(' ','_')+'.txt', 'w') as f: t.to_ASCII(f) @@ -162,17 +202,18 @@ class Colormap(mpl.colors.ListedColormap): @staticmethod def _export_GOM(colormap,fhandle=None): pass - # a = f'1 1 {name.replace(" ","_"} 9 {name.replace(" ","_"} ' - # f' 0 1 0 3 0 0 -1 9 \\ 0 0 0 255 255 255 0 0 255 ' - # f'30 NO_UNIT 1 1 64 64 64 255 1 0 0 0 0 0 0 3 0 ' + str(len(colors)) - # f' '.join([' 0 %s 255 1'%(' '.join([str(int(x*255.0)) for x in color])) for color in reversed(colors)])] + s =(f'1 1 {colormap.name.replace(" ","_")} 9 {colormap.name.replace(" ","_")} ' + f' 0 1 0 3 0 0 -1 9 \\ 0 0 0 255 255 255 0 0 255 ' + f'30 NO_UNIT 1 1 64 64 64 255 1 0 0 0 0 0 0 3 0 {str(len(colormap.colors))}' + ' '.join([' 0 %s 255 1'%(' '.join([str(int(x*255.0)) for x in color])) for color in reversed(colormap.colors)])) + print(s) @staticmethod def _export_gmsh(colormap,fname=None): colors = colormap.colors - colormap = ['View.ColorTable = {'] \ - + [',\n'.join(['{%s}'%(','.join([str(x*255.0) for x in color])) for color in colors])] \ - + ['}'] + colormap =('View.ColorTable = {' + ',\n'.join(['{%s}'%(','.join([str(x*255.0) for x in color])) for color in colors])+\ + '}') @staticmethod def _interpolate_msh(frac,low, high): diff --git a/python/tests/test_Colormap.py b/python/tests/test_Colormap.py index d3cbcaacd..e00367452 100644 --- a/python/tests/test_Colormap.py +++ b/python/tests/test_Colormap.py @@ -1,4 +1,7 @@ +import os + import numpy as np +import pytest from damask import Colormap @@ -15,7 +18,7 @@ class TestColormap: [1.,0.,1.], [1.,1.,1.] ]) - rgbs = np.vstack((specials,np.random.rand(10000,3))) + rgbs = np.vstack((specials,np.random.rand(100,3))) pass # class not integrated for rgb in rgbs: print('rgb',rgb) @@ -59,3 +62,51 @@ class TestColormap: # xyz2msh assert np.allclose(Colormap._xyz2msh(xyz),msh,atol=1.e-6,rtol=0) + + + @pytest.mark.parametrize('format',['ASCII','paraview','GOM','gmsh']) + @pytest.mark.parametrize('model',['rgb','hsv','hsl','xyz','lab','msh']) + def test_from_bounds(self,model,format,tmpdir): + N = np.random.randint(2,256) + c = Colormap.from_bounds(np.random.rand(3),np.random.rand(3),model=model,N=N) + c.to_file(tmpdir/'color_out',format=format) + + @pytest.mark.parametrize('format',['ASCII','paraview','GOM','gmsh']) + @pytest.mark.parametrize('name',['strain','gnuplot','Greys','PRGn','viridis']) + def test_from_predefined(self,name,format,tmpdir): + N = np.random.randint(2,256) + c = Colormap.from_predefined(name,N) + os.chdir(tmpdir) + c.to_file(format=format) + + @pytest.mark.parametrize('format,name',[('ASCII','test.txt'), + ('paraview','test.json'), + ('GOM','test.legend'), + ('gmsh','test.xxx') + ]) + def test_write_filehandle(self,format,name,tmpdir): + c = Colormap.from_predefined('Dark2') + with open(tmpdir/name,'w') as f: + c.to_file(f,format=format) + + def test_invalid_format(self): + c = Colormap.from_predefined('Dark2') + with pytest.raises(ValueError): + c.to_file(format='invalid') + + @pytest.mark.parametrize('model',['rgb','hsv','hsl','lab','invalid']) + def test_invalid_color(self,model): + with pytest.raises(ValueError): + c = Colormap.from_bounds(-2.+np.random.rand(3),np.random.rand(3),N=10,model=model) # noqa + + def test_reversed(self): + c_1 = Colormap.from_predefined('stress') + c_2 = c_1.reversed() + assert (not np.allclose(c_1.colors,c_2.colors)) and \ + np.allclose(c_1.colors,c_2.reversed().colors) + + def test_list(self): + c = Colormap.from_predefined('afmhot').reversed() + c.list_predefined() + + From d965d663195c57824e298e33f1f5fabc29d9494c Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 28 Jun 2020 09:32:10 +0200 Subject: [PATCH 27/78] [skip ci] documenting --- python/damask/_colormap.py | 57 +++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index 9b707d00a..a0c976516 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -12,6 +12,9 @@ _eps = 216./24389. _kappa = 24389./27. _ref_white = np.array([.95047, 1.00000, 1.08883]) # Observer = 2, Illuminant = D65 +# ToDo (if needed) +# - support alpha channel (paraview/ASCII/input) +# - support NaN color (paraview) class Colormap(mpl.colors.ListedColormap): @@ -19,18 +22,31 @@ class Colormap(mpl.colors.ListedColormap): @staticmethod def from_bounds(low,high,name='DAMASK colormap',N=256,model='rgb'): """ - Create a perceptually uniform colormap. + Create a perceptually uniform colormap from given bounds. + + Colors are internally stored as R(ed) G(green) B(lue) values. + The colormap can be used in matplotlib/seaborn or exported to + file for external use. Parameters ---------- low : numpy.ndarray of shape (3) + Color definition for minimum value. high : numpy.ndarray of shape (3) + Color definition for maximum value. N : integer, optional - The number of color quantization levels. + The number of color quantization levels. Defaults to 256. name : str, optional The name of the colormap. Defaults to `DAMASK colormap`. - model : str - Colormodel used for low and high. + model : {'rgb', 'hsv', 'hsl', 'xyz', 'lab', 'msh'} + Colormodel used for input color definitions. Defaults to `rgb`. + The available color models are: + - 'rgb': R(ed) G(green) B(lue). + - 'hsv': H(ue) S(aturation) V(alue). + - 'hsl': H(ue) S(aturation) L(uminance). + - 'xyz': CIE Xyz. + - 'lab': CIE Lab. + - 'msh': Msh (for perceptual uniform interpolation). """ low_,high_ = map(np.array,[low,high]) @@ -78,7 +94,7 @@ class Colormap(mpl.colors.ListedColormap): @staticmethod def from_predefined(name,N=256): """ - Select from set of predefined colormaps. + Select from a set of predefined colormaps. Predefined colormaps include native matplotlib colormaps and common DAMASK colormaps. @@ -147,7 +163,24 @@ class Colormap(mpl.colors.ListedColormap): return Colormap(rev.colors,rev.name) - def to_file(self,fname=None,format='paraview'): + def to_file(self,fname=None,format='ParaView'): + """ + Export colormap to file for use in external programs. + + Parameters + ---------- + fname : file, str, or pathlib.Path, optional. + Filename to store results. If not given, the filename will + consist of the name of the colormap and an extension that + depends on the file format. + format : {'ParaView', 'ASCII', 'GOM', 'gmsh'}, optional + File format, defaults to 'ParaView'. Available formats are: + - ParaView: JSON file, extension '.json'. + - ASCII: Plain text file, extension '.txt'. + - GOM: Aramis GOM (DIC), extension '.legend'. + - Gmsh: Gmsh FEM mesh-generator, extension '.msh'. + + """ if fname is not None: try: f = open(fname,'w') @@ -216,8 +249,18 @@ class Colormap(mpl.colors.ListedColormap): '}') @staticmethod - def _interpolate_msh(frac,low, high): + def _interpolate_msh(frac,low,high): + """ + Interpolate in Msh color space. + This interpolation gives a perceptually uniform colormap. + + References + ---------- + https://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf + https://www.kennethmoreland.com/color-maps/diverging_map.py + + """ def rad_diff(a,b): return abs(a[2]-b[2]) From 3513754647b2a2de2ef1f11413dd859c58719508 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 27 Jun 2020 20:17:48 +0200 Subject: [PATCH 28/78] low/high instead of left/right --- python/damask/_colormap.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index a0c976516..39a86cc8f 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -121,7 +121,7 @@ class Colormap(mpl.colors.ListedColormap): # DAMASK presets definition = Colormap._predefined_DAMASK[name] - return Colormap.from_bounds(definition['left'],definition['right'],name,N) + return Colormap.from_bounds(definition['low'],definition['high'],name,N) @staticmethod @@ -316,12 +316,12 @@ class Colormap(mpl.colors.ListedColormap): 'gnuplot', 'gnuplot2', 'CMRmap', 'cubehelix', 'brg', 'gist_rainbow', 'rainbow', 'jet', 'nipy_spectral', 'gist_ncar'])] - _predefined_DAMASK = {'orientation': {'low': [0.933334,0.878432,0.878431], - 'right': [0.250980,0.007843,0.000000]}, - 'strain': {'left': [0.941177,0.941177,0.870588], - 'right': [0.266667,0.266667,0.000000]}, - 'stress': {'left': [0.878432,0.874511,0.949019], - 'right': [0.000002,0.000000,0.286275]}} + _predefined_DAMASK = {'orientation': {'low': [0.933334,0.878432,0.878431], + 'high': [0.250980,0.007843,0.000000]}, + 'strain': {'low': [0.941177,0.941177,0.870588], + 'high': [0.266667,0.266667,0.000000]}, + 'stress': {'low': [0.878432,0.874511,0.949019], + 'high': [0.000002,0.000000,0.286275]}} @staticmethod def _hsv2rgb(hsv): From b78c8093756d790dfadc2aac5cddb3dc893db4cb Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 27 Jun 2020 20:36:18 +0200 Subject: [PATCH 29/78] real output --- python/damask/_colormap.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index 39a86cc8f..0d04c44b7 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -242,11 +242,16 @@ class Colormap(mpl.colors.ListedColormap): print(s) @staticmethod - def _export_gmsh(colormap,fname=None): - colors = colormap.colors - colormap =('View.ColorTable = {' - ',\n'.join(['{%s}'%(','.join([str(x*255.0) for x in color])) for color in colors])+\ - '}') + def _export_gmsh(colormap,fhandle=None): + colormap = 'View.ColorTable = {\n'\ + +'\n'.join([f'{c[0]},{c[1]},{c[2]},' for c in reversed(colormap.colors)])\ + +'}' + if fhandle is None: + with open(colormap.name.replace(' ','_')+'.txt', 'w') as f: + t.to_ASCII(f) + else: + t.to_ASCII(fhandle) + @staticmethod def _interpolate_msh(frac,low,high): From 1c03bd157f65175c4815a89277486dc97afe56a9 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 27 Jun 2020 21:26:25 +0200 Subject: [PATCH 30/78] more testing --- python/damask/_colormap.py | 31 +++++++++++++++++++------------ python/tests/test_Colormap.py | 9 +++++++-- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index 0d04c44b7..f6e6b62e7 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -234,23 +234,30 @@ class Colormap(mpl.colors.ListedColormap): @staticmethod def _export_GOM(colormap,fhandle=None): - pass - s =(f'1 1 {colormap.name.replace(" ","_")} 9 {colormap.name.replace(" ","_")} ' - f' 0 1 0 3 0 0 -1 9 \\ 0 0 0 255 255 255 0 0 255 ' - f'30 NO_UNIT 1 1 64 64 64 255 1 0 0 0 0 0 0 3 0 {str(len(colormap.colors))}' - ' '.join([' 0 %s 255 1'%(' '.join([str(int(x*255.0)) for x in color])) for color in reversed(colormap.colors)])) - print(s) + # ToDo: test in GOM + GOM_str = f'1 1 {colormap.name.replace(" ","_")} 9 {colormap.name.replace(" ","_")} ' \ + + '0 1 0 3 0 0 -1 9 \\ 0 0 0 255 255 255 0 0 255 ' \ + + f'30 NO_UNIT 1 1 64 64 64 255 1 0 0 0 0 0 0 3 0 {len(colormap.colors)}' \ + + ' '.join([f' 0 {c[0]} {c[1]} {c[2]} 255 1' for c in reversed((colormap.colors*255).astype(int))]) \ + + '\n' + if fhandle is None: + with open(colormap.name.replace(' ','_')+'.legend', 'w') as f: + f.write(GOM_str) + else: + fhandle.write(GOM_str) + @staticmethod def _export_gmsh(colormap,fhandle=None): - colormap = 'View.ColorTable = {\n'\ - +'\n'.join([f'{c[0]},{c[1]},{c[2]},' for c in reversed(colormap.colors)])\ - +'}' + # ToDo: test in gmsh + gmsh_str = 'View.ColorTable = {\n' \ + +'\n'.join([f'{c[0]},{c[1]},{c[2]},' for c in colormap.colors[:,:3]*255]) \ + +'\n}\n' if fhandle is None: - with open(colormap.name.replace(' ','_')+'.txt', 'w') as f: - t.to_ASCII(f) + with open(colormap.name.replace(' ','_')+'.msh', 'w') as f: + f.write(gmsh_str) else: - t.to_ASCII(fhandle) + fhandle.write(gmsh_str) @staticmethod diff --git a/python/tests/test_Colormap.py b/python/tests/test_Colormap.py index e00367452..dde14dcf3 100644 --- a/python/tests/test_Colormap.py +++ b/python/tests/test_Colormap.py @@ -86,10 +86,15 @@ class TestColormap: ]) def test_write_filehandle(self,format,name,tmpdir): c = Colormap.from_predefined('Dark2') - with open(tmpdir/name,'w') as f: + fname = tmpdir/name + with open(fname,'w') as f: c.to_file(f,format=format) + for i in range(10): + if fname.exists(): return + time.sleep(.5) + assert False - def test_invalid_format(self): + def test_write_invalid_format(self): c = Colormap.from_predefined('Dark2') with pytest.raises(ValueError): c.to_file(format='invalid') From e81b67e96408ad432012b803209df8d1fa2b4c56 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 28 Jun 2020 10:32:59 +0200 Subject: [PATCH 31/78] polishing --- python/damask/_colormap.py | 2 ++ python/tests/test_Colormap.py | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index f6e6b62e7..9377ca277 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -234,6 +234,7 @@ class Colormap(mpl.colors.ListedColormap): @staticmethod def _export_GOM(colormap,fhandle=None): + """Write colormap to GOM Aramis compatible format.""" # ToDo: test in GOM GOM_str = f'1 1 {colormap.name.replace(" ","_")} 9 {colormap.name.replace(" ","_")} ' \ + '0 1 0 3 0 0 -1 9 \\ 0 0 0 255 255 255 0 0 255 ' \ @@ -249,6 +250,7 @@ class Colormap(mpl.colors.ListedColormap): @staticmethod def _export_gmsh(colormap,fhandle=None): + """Write colormap to Gmsh compatible format.""" # ToDo: test in gmsh gmsh_str = 'View.ColorTable = {\n' \ +'\n'.join([f'{c[0]},{c[1]},{c[2]},' for c in colormap.colors[:,:3]*255]) \ diff --git a/python/tests/test_Colormap.py b/python/tests/test_Colormap.py index dde14dcf3..f124c704e 100644 --- a/python/tests/test_Colormap.py +++ b/python/tests/test_Colormap.py @@ -82,7 +82,7 @@ class TestColormap: @pytest.mark.parametrize('format,name',[('ASCII','test.txt'), ('paraview','test.json'), ('GOM','test.legend'), - ('gmsh','test.xxx') + ('gmsh','test.msh') ]) def test_write_filehandle(self,format,name,tmpdir): c = Colormap.from_predefined('Dark2') @@ -111,7 +111,6 @@ class TestColormap: np.allclose(c_1.colors,c_2.reversed().colors) def test_list(self): - c = Colormap.from_predefined('afmhot').reversed() - c.list_predefined() + Colormap.list_predefined() From ae4146f1c6a3a5e8e1c69c9ab94600819f5d2614 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 28 Jun 2020 11:18:22 +0200 Subject: [PATCH 32/78] report version, write out 'modern' ASCII table style --- python/damask/_colormap.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index 9377ca277..d3ed4b12e 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -6,6 +6,7 @@ import matplotlib as mpl import matplotlib.pyplot as plt from matplotlib import cm +import damask from damask import Table _eps = 216./24389. @@ -208,6 +209,7 @@ class Colormap(mpl.colors.ListedColormap): colors+=[i]+c out = [{ + 'Creator':f'damask.Colormap {damask.version}', 'ColorSpace':'RGB', 'Name':colormap.name, 'DefaultMap':True, @@ -224,13 +226,13 @@ class Colormap(mpl.colors.ListedColormap): """Write colormap to ASCII table.""" labels = {'R':(1,),'G':(1,),'B':(1,)} if colormap.colors.shape[1] == 4: labels['alpha']=(1,) - t = Table(colormap.colors,labels) + t = Table(colormap.colors,labels,f'Creator: damask.Colormap {damask.version}') if fhandle is None: with open(colormap.name.replace(' ','_')+'.txt', 'w') as f: - t.to_ASCII(f) + t.to_ASCII(f,True) else: - t.to_ASCII(fhandle) + t.to_ASCII(fhandle,True) @staticmethod def _export_GOM(colormap,fhandle=None): From 6a748d2edf8f3ffd7b4bdf8a618605c08dfa7557 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 28 Jun 2020 11:19:18 +0200 Subject: [PATCH 33/78] do not expand single strings into characters --- python/damask/_table.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/python/damask/_table.py b/python/damask/_table.py index 435b7afa9..e8d35c545 100644 --- a/python/damask/_table.py +++ b/python/damask/_table.py @@ -19,11 +19,12 @@ class Table: Data. Column labels from a pandas.DataFrame will be replaced. shapes : dict with str:tuple pairs Shapes of the columns. Example 'F':(3,3) for a deformation gradient. - comments : iterable of str, optional + comments : str or iterable of str, optional Additional, human-readable information. """ - self.comments = [] if comments is None else [c for c in comments] + comments_ = [comments] if isinstance(comments,str) else comments + self.comments = [] if comments_ is None else [c for c in comments_] self.data = pd.DataFrame(data=data) self.shapes = { k:(v,) if isinstance(v,(np.int,int)) else v for k,v in shapes.items() } self._label_condensed() From e6a87da37c8d7562aa351c2c156d830c4a08bfc3 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 28 Jun 2020 11:20:09 +0200 Subject: [PATCH 34/78] compare to reference results need to patch damask.version to be independent of version strings (in json and ASCII table) --- python/tests/reference/Colormap/binary.json | 1290 +++++++++++++++++ python/tests/reference/Colormap/binary.legend | 1 + python/tests/reference/Colormap/binary.msh | 258 ++++ python/tests/reference/Colormap/binary.txt | 258 ++++ python/tests/test_Colormap.py | 31 +- 5 files changed, 1835 insertions(+), 3 deletions(-) create mode 100644 python/tests/reference/Colormap/binary.json create mode 100644 python/tests/reference/Colormap/binary.legend create mode 100644 python/tests/reference/Colormap/binary.msh create mode 100644 python/tests/reference/Colormap/binary.txt diff --git a/python/tests/reference/Colormap/binary.json b/python/tests/reference/Colormap/binary.json new file mode 100644 index 000000000..d56f1232e --- /dev/null +++ b/python/tests/reference/Colormap/binary.json @@ -0,0 +1,1290 @@ +[ + { + "Creator": "damask.Colormap 99.99.99-9999-pytest", + "ColorSpace": "RGB", + "Name": "binary", + "DefaultMap": true, + "RGBPoints": [ + 0, + 1.0, + 1.0, + 1.0, + 1.0, + 1, + 0.996078, + 0.996078, + 0.996078, + 1.0, + 2, + 0.992157, + 0.992157, + 0.992157, + 1.0, + 3, + 0.988235, + 0.988235, + 0.988235, + 1.0, + 4, + 0.984314, + 0.984314, + 0.984314, + 1.0, + 5, + 0.980392, + 0.980392, + 0.980392, + 1.0, + 6, + 0.976471, + 0.976471, + 0.976471, + 1.0, + 7, + 0.972549, + 0.972549, + 0.972549, + 1.0, + 8, + 0.968627, + 0.968627, + 0.968627, + 1.0, + 9, + 0.964706, + 0.964706, + 0.964706, + 1.0, + 10, + 0.960784, + 0.960784, + 0.960784, + 1.0, + 11, + 0.956863, + 0.956863, + 0.956863, + 1.0, + 12, + 0.952941, + 0.952941, + 0.952941, + 1.0, + 13, + 0.94902, + 0.94902, + 0.94902, + 1.0, + 14, + 0.945098, + 0.945098, + 0.945098, + 1.0, + 15, + 0.941176, + 0.941176, + 0.941176, + 1.0, + 16, + 0.937255, + 0.937255, + 0.937255, + 1.0, + 17, + 0.933333, + 0.933333, + 0.933333, + 1.0, + 18, + 0.929412, + 0.929412, + 0.929412, + 1.0, + 19, + 0.92549, + 0.92549, + 0.92549, + 1.0, + 20, + 0.921569, + 0.921569, + 0.921569, + 1.0, + 21, + 0.917647, + 0.917647, + 0.917647, + 1.0, + 22, + 0.913725, + 0.913725, + 0.913725, + 1.0, + 23, + 0.909804, + 0.909804, + 0.909804, + 1.0, + 24, + 0.905882, + 0.905882, + 0.905882, + 1.0, + 25, + 0.901961, + 0.901961, + 0.901961, + 1.0, + 26, + 0.898039, + 0.898039, + 0.898039, + 1.0, + 27, + 0.894118, + 0.894118, + 0.894118, + 1.0, + 28, + 0.890196, + 0.890196, + 0.890196, + 1.0, + 29, + 0.886275, + 0.886275, + 0.886275, + 1.0, + 30, + 0.882353, + 0.882353, + 0.882353, + 1.0, + 31, + 0.878431, + 0.878431, + 0.878431, + 1.0, + 32, + 0.87451, + 0.87451, + 0.87451, + 1.0, + 33, + 0.870588, + 0.870588, + 0.870588, + 1.0, + 34, + 0.866667, + 0.866667, + 0.866667, + 1.0, + 35, + 0.862745, + 0.862745, + 0.862745, + 1.0, + 36, + 0.858824, + 0.858824, + 0.858824, + 1.0, + 37, + 0.854902, + 0.854902, + 0.854902, + 1.0, + 38, + 0.85098, + 0.85098, + 0.85098, + 1.0, + 39, + 0.847059, + 0.847059, + 0.847059, + 1.0, + 40, + 0.843137, + 0.843137, + 0.843137, + 1.0, + 41, + 0.839216, + 0.839216, + 0.839216, + 1.0, + 42, + 0.835294, + 0.835294, + 0.835294, + 1.0, + 43, + 0.831373, + 0.831373, + 0.831373, + 1.0, + 44, + 0.827451, + 0.827451, + 0.827451, + 1.0, + 45, + 0.823529, + 0.823529, + 0.823529, + 1.0, + 46, + 0.819608, + 0.819608, + 0.819608, + 1.0, + 47, + 0.815686, + 0.815686, + 0.815686, + 1.0, + 48, + 0.811765, + 0.811765, + 0.811765, + 1.0, + 49, + 0.807843, + 0.807843, + 0.807843, + 1.0, + 50, + 0.803922, + 0.803922, + 0.803922, + 1.0, + 51, + 0.8, + 0.8, + 0.8, + 1.0, + 52, + 0.796078, + 0.796078, + 0.796078, + 1.0, + 53, + 0.792157, + 0.792157, + 0.792157, + 1.0, + 54, + 0.788235, + 0.788235, + 0.788235, + 1.0, + 55, + 0.784314, + 0.784314, + 0.784314, + 1.0, + 56, + 0.780392, + 0.780392, + 0.780392, + 1.0, + 57, + 0.776471, + 0.776471, + 0.776471, + 1.0, + 58, + 0.772549, + 0.772549, + 0.772549, + 1.0, + 59, + 0.768627, + 0.768627, + 0.768627, + 1.0, + 60, + 0.764706, + 0.764706, + 0.764706, + 1.0, + 61, + 0.760784, + 0.760784, + 0.760784, + 1.0, + 62, + 0.756863, + 0.756863, + 0.756863, + 1.0, + 63, + 0.752941, + 0.752941, + 0.752941, + 1.0, + 64, + 0.74902, + 0.74902, + 0.74902, + 1.0, + 65, + 0.745098, + 0.745098, + 0.745098, + 1.0, + 66, + 0.741176, + 0.741176, + 0.741176, + 1.0, + 67, + 0.737255, + 0.737255, + 0.737255, + 1.0, + 68, + 0.733333, + 0.733333, + 0.733333, + 1.0, + 69, + 0.729412, + 0.729412, + 0.729412, + 1.0, + 70, + 0.72549, + 0.72549, + 0.72549, + 1.0, + 71, + 0.721569, + 0.721569, + 0.721569, + 1.0, + 72, + 0.717647, + 0.717647, + 0.717647, + 1.0, + 73, + 0.713725, + 0.713725, + 0.713725, + 1.0, + 74, + 0.709804, + 0.709804, + 0.709804, + 1.0, + 75, + 0.705882, + 0.705882, + 0.705882, + 1.0, + 76, + 0.701961, + 0.701961, + 0.701961, + 1.0, + 77, + 0.698039, + 0.698039, + 0.698039, + 1.0, + 78, + 0.694118, + 0.694118, + 0.694118, + 1.0, + 79, + 0.690196, + 0.690196, + 0.690196, + 1.0, + 80, + 0.686275, + 0.686275, + 0.686275, + 1.0, + 81, + 0.682353, + 0.682353, + 0.682353, + 1.0, + 82, + 0.678431, + 0.678431, + 0.678431, + 1.0, + 83, + 0.67451, + 0.67451, + 0.67451, + 1.0, + 84, + 0.670588, + 0.670588, + 0.670588, + 1.0, + 85, + 0.666667, + 0.666667, + 0.666667, + 1.0, + 86, + 0.662745, + 0.662745, + 0.662745, + 1.0, + 87, + 0.658824, + 0.658824, + 0.658824, + 1.0, + 88, + 0.654902, + 0.654902, + 0.654902, + 1.0, + 89, + 0.65098, + 0.65098, + 0.65098, + 1.0, + 90, + 0.647059, + 0.647059, + 0.647059, + 1.0, + 91, + 0.643137, + 0.643137, + 0.643137, + 1.0, + 92, + 0.639216, + 0.639216, + 0.639216, + 1.0, + 93, + 0.635294, + 0.635294, + 0.635294, + 1.0, + 94, + 0.631373, + 0.631373, + 0.631373, + 1.0, + 95, + 0.627451, + 0.627451, + 0.627451, + 1.0, + 96, + 0.623529, + 0.623529, + 0.623529, + 1.0, + 97, + 0.619608, + 0.619608, + 0.619608, + 1.0, + 98, + 0.615686, + 0.615686, + 0.615686, + 1.0, + 99, + 0.611765, + 0.611765, + 0.611765, + 1.0, + 100, + 0.607843, + 0.607843, + 0.607843, + 1.0, + 101, + 0.603922, + 0.603922, + 0.603922, + 1.0, + 102, + 0.6, + 0.6, + 0.6, + 1.0, + 103, + 0.596078, + 0.596078, + 0.596078, + 1.0, + 104, + 0.592157, + 0.592157, + 0.592157, + 1.0, + 105, + 0.588235, + 0.588235, + 0.588235, + 1.0, + 106, + 0.584314, + 0.584314, + 0.584314, + 1.0, + 107, + 0.580392, + 0.580392, + 0.580392, + 1.0, + 108, + 0.576471, + 0.576471, + 0.576471, + 1.0, + 109, + 0.572549, + 0.572549, + 0.572549, + 1.0, + 110, + 0.568627, + 0.568627, + 0.568627, + 1.0, + 111, + 0.564706, + 0.564706, + 0.564706, + 1.0, + 112, + 0.560784, + 0.560784, + 0.560784, + 1.0, + 113, + 0.556863, + 0.556863, + 0.556863, + 1.0, + 114, + 0.552941, + 0.552941, + 0.552941, + 1.0, + 115, + 0.54902, + 0.54902, + 0.54902, + 1.0, + 116, + 0.545098, + 0.545098, + 0.545098, + 1.0, + 117, + 0.541176, + 0.541176, + 0.541176, + 1.0, + 118, + 0.537255, + 0.537255, + 0.537255, + 1.0, + 119, + 0.533333, + 0.533333, + 0.533333, + 1.0, + 120, + 0.529412, + 0.529412, + 0.529412, + 1.0, + 121, + 0.52549, + 0.52549, + 0.52549, + 1.0, + 122, + 0.521569, + 0.521569, + 0.521569, + 1.0, + 123, + 0.517647, + 0.517647, + 0.517647, + 1.0, + 124, + 0.513725, + 0.513725, + 0.513725, + 1.0, + 125, + 0.509804, + 0.509804, + 0.509804, + 1.0, + 126, + 0.505882, + 0.505882, + 0.505882, + 1.0, + 127, + 0.501961, + 0.501961, + 0.501961, + 1.0, + 128, + 0.498039, + 0.498039, + 0.498039, + 1.0, + 129, + 0.494118, + 0.494118, + 0.494118, + 1.0, + 130, + 0.490196, + 0.490196, + 0.490196, + 1.0, + 131, + 0.486275, + 0.486275, + 0.486275, + 1.0, + 132, + 0.482353, + 0.482353, + 0.482353, + 1.0, + 133, + 0.478431, + 0.478431, + 0.478431, + 1.0, + 134, + 0.47451, + 0.47451, + 0.47451, + 1.0, + 135, + 0.470588, + 0.470588, + 0.470588, + 1.0, + 136, + 0.466667, + 0.466667, + 0.466667, + 1.0, + 137, + 0.462745, + 0.462745, + 0.462745, + 1.0, + 138, + 0.458824, + 0.458824, + 0.458824, + 1.0, + 139, + 0.454902, + 0.454902, + 0.454902, + 1.0, + 140, + 0.45098, + 0.45098, + 0.45098, + 1.0, + 141, + 0.447059, + 0.447059, + 0.447059, + 1.0, + 142, + 0.443137, + 0.443137, + 0.443137, + 1.0, + 143, + 0.439216, + 0.439216, + 0.439216, + 1.0, + 144, + 0.435294, + 0.435294, + 0.435294, + 1.0, + 145, + 0.431373, + 0.431373, + 0.431373, + 1.0, + 146, + 0.427451, + 0.427451, + 0.427451, + 1.0, + 147, + 0.423529, + 0.423529, + 0.423529, + 1.0, + 148, + 0.419608, + 0.419608, + 0.419608, + 1.0, + 149, + 0.415686, + 0.415686, + 0.415686, + 1.0, + 150, + 0.411765, + 0.411765, + 0.411765, + 1.0, + 151, + 0.407843, + 0.407843, + 0.407843, + 1.0, + 152, + 0.403922, + 0.403922, + 0.403922, + 1.0, + 153, + 0.4, + 0.4, + 0.4, + 1.0, + 154, + 0.396078, + 0.396078, + 0.396078, + 1.0, + 155, + 0.392157, + 0.392157, + 0.392157, + 1.0, + 156, + 0.388235, + 0.388235, + 0.388235, + 1.0, + 157, + 0.384314, + 0.384314, + 0.384314, + 1.0, + 158, + 0.380392, + 0.380392, + 0.380392, + 1.0, + 159, + 0.376471, + 0.376471, + 0.376471, + 1.0, + 160, + 0.372549, + 0.372549, + 0.372549, + 1.0, + 161, + 0.368627, + 0.368627, + 0.368627, + 1.0, + 162, + 0.364706, + 0.364706, + 0.364706, + 1.0, + 163, + 0.360784, + 0.360784, + 0.360784, + 1.0, + 164, + 0.356863, + 0.356863, + 0.356863, + 1.0, + 165, + 0.352941, + 0.352941, + 0.352941, + 1.0, + 166, + 0.34902, + 0.34902, + 0.34902, + 1.0, + 167, + 0.345098, + 0.345098, + 0.345098, + 1.0, + 168, + 0.341176, + 0.341176, + 0.341176, + 1.0, + 169, + 0.337255, + 0.337255, + 0.337255, + 1.0, + 170, + 0.333333, + 0.333333, + 0.333333, + 1.0, + 171, + 0.329412, + 0.329412, + 0.329412, + 1.0, + 172, + 0.32549, + 0.32549, + 0.32549, + 1.0, + 173, + 0.321569, + 0.321569, + 0.321569, + 1.0, + 174, + 0.317647, + 0.317647, + 0.317647, + 1.0, + 175, + 0.313725, + 0.313725, + 0.313725, + 1.0, + 176, + 0.309804, + 0.309804, + 0.309804, + 1.0, + 177, + 0.305882, + 0.305882, + 0.305882, + 1.0, + 178, + 0.301961, + 0.301961, + 0.301961, + 1.0, + 179, + 0.298039, + 0.298039, + 0.298039, + 1.0, + 180, + 0.294118, + 0.294118, + 0.294118, + 1.0, + 181, + 0.290196, + 0.290196, + 0.290196, + 1.0, + 182, + 0.286275, + 0.286275, + 0.286275, + 1.0, + 183, + 0.282353, + 0.282353, + 0.282353, + 1.0, + 184, + 0.278431, + 0.278431, + 0.278431, + 1.0, + 185, + 0.27451, + 0.27451, + 0.27451, + 1.0, + 186, + 0.270588, + 0.270588, + 0.270588, + 1.0, + 187, + 0.266667, + 0.266667, + 0.266667, + 1.0, + 188, + 0.262745, + 0.262745, + 0.262745, + 1.0, + 189, + 0.258824, + 0.258824, + 0.258824, + 1.0, + 190, + 0.254902, + 0.254902, + 0.254902, + 1.0, + 191, + 0.25098, + 0.25098, + 0.25098, + 1.0, + 192, + 0.247059, + 0.247059, + 0.247059, + 1.0, + 193, + 0.243137, + 0.243137, + 0.243137, + 1.0, + 194, + 0.239216, + 0.239216, + 0.239216, + 1.0, + 195, + 0.235294, + 0.235294, + 0.235294, + 1.0, + 196, + 0.231373, + 0.231373, + 0.231373, + 1.0, + 197, + 0.227451, + 0.227451, + 0.227451, + 1.0, + 198, + 0.223529, + 0.223529, + 0.223529, + 1.0, + 199, + 0.219608, + 0.219608, + 0.219608, + 1.0, + 200, + 0.215686, + 0.215686, + 0.215686, + 1.0, + 201, + 0.211765, + 0.211765, + 0.211765, + 1.0, + 202, + 0.207843, + 0.207843, + 0.207843, + 1.0, + 203, + 0.203922, + 0.203922, + 0.203922, + 1.0, + 204, + 0.2, + 0.2, + 0.2, + 1.0, + 205, + 0.196078, + 0.196078, + 0.196078, + 1.0, + 206, + 0.192157, + 0.192157, + 0.192157, + 1.0, + 207, + 0.188235, + 0.188235, + 0.188235, + 1.0, + 208, + 0.184314, + 0.184314, + 0.184314, + 1.0, + 209, + 0.180392, + 0.180392, + 0.180392, + 1.0, + 210, + 0.176471, + 0.176471, + 0.176471, + 1.0, + 211, + 0.172549, + 0.172549, + 0.172549, + 1.0, + 212, + 0.168627, + 0.168627, + 0.168627, + 1.0, + 213, + 0.164706, + 0.164706, + 0.164706, + 1.0, + 214, + 0.160784, + 0.160784, + 0.160784, + 1.0, + 215, + 0.156863, + 0.156863, + 0.156863, + 1.0, + 216, + 0.152941, + 0.152941, + 0.152941, + 1.0, + 217, + 0.14902, + 0.14902, + 0.14902, + 1.0, + 218, + 0.145098, + 0.145098, + 0.145098, + 1.0, + 219, + 0.141176, + 0.141176, + 0.141176, + 1.0, + 220, + 0.137255, + 0.137255, + 0.137255, + 1.0, + 221, + 0.133333, + 0.133333, + 0.133333, + 1.0, + 222, + 0.129412, + 0.129412, + 0.129412, + 1.0, + 223, + 0.12549, + 0.12549, + 0.12549, + 1.0, + 224, + 0.121569, + 0.121569, + 0.121569, + 1.0, + 225, + 0.117647, + 0.117647, + 0.117647, + 1.0, + 226, + 0.113725, + 0.113725, + 0.113725, + 1.0, + 227, + 0.109804, + 0.109804, + 0.109804, + 1.0, + 228, + 0.105882, + 0.105882, + 0.105882, + 1.0, + 229, + 0.101961, + 0.101961, + 0.101961, + 1.0, + 230, + 0.098039, + 0.098039, + 0.098039, + 1.0, + 231, + 0.094118, + 0.094118, + 0.094118, + 1.0, + 232, + 0.090196, + 0.090196, + 0.090196, + 1.0, + 233, + 0.086275, + 0.086275, + 0.086275, + 1.0, + 234, + 0.082353, + 0.082353, + 0.082353, + 1.0, + 235, + 0.078431, + 0.078431, + 0.078431, + 1.0, + 236, + 0.07451, + 0.07451, + 0.07451, + 1.0, + 237, + 0.070588, + 0.070588, + 0.070588, + 1.0, + 238, + 0.066667, + 0.066667, + 0.066667, + 1.0, + 239, + 0.062745, + 0.062745, + 0.062745, + 1.0, + 240, + 0.058824, + 0.058824, + 0.058824, + 1.0, + 241, + 0.054902, + 0.054902, + 0.054902, + 1.0, + 242, + 0.05098, + 0.05098, + 0.05098, + 1.0, + 243, + 0.047059, + 0.047059, + 0.047059, + 1.0, + 244, + 0.043137, + 0.043137, + 0.043137, + 1.0, + 245, + 0.039216, + 0.039216, + 0.039216, + 1.0, + 246, + 0.035294, + 0.035294, + 0.035294, + 1.0, + 247, + 0.031373, + 0.031373, + 0.031373, + 1.0, + 248, + 0.027451, + 0.027451, + 0.027451, + 1.0, + 249, + 0.023529, + 0.023529, + 0.023529, + 1.0, + 250, + 0.019608, + 0.019608, + 0.019608, + 1.0, + 251, + 0.015686, + 0.015686, + 0.015686, + 1.0, + 252, + 0.011765, + 0.011765, + 0.011765, + 1.0, + 253, + 0.007843, + 0.007843, + 0.007843, + 1.0, + 254, + 0.003922, + 0.003922, + 0.003922, + 1.0, + 255, + 0.0, + 0.0, + 0.0, + 1.0 + ] + } +] \ No newline at end of file diff --git a/python/tests/reference/Colormap/binary.legend b/python/tests/reference/Colormap/binary.legend new file mode 100644 index 000000000..ba417facc --- /dev/null +++ b/python/tests/reference/Colormap/binary.legend @@ -0,0 +1 @@ +1 1 binary 9 binary 0 1 0 3 0 0 -1 9 \ 0 0 0 255 255 255 0 0 255 30 NO_UNIT 1 1 64 64 64 255 1 0 0 0 0 0 0 3 0 256 0 0 0 0 255 1 0 0 0 0 255 1 0 1 1 1 255 1 0 2 2 2 255 1 0 4 4 4 255 1 0 5 5 5 255 1 0 6 6 6 255 1 0 7 7 7 255 1 0 8 8 8 255 1 0 8 8 8 255 1 0 9 9 9 255 1 0 11 11 11 255 1 0 12 12 12 255 1 0 13 13 13 255 1 0 14 14 14 255 1 0 15 15 15 255 1 0 16 16 16 255 1 0 16 16 16 255 1 0 17 17 17 255 1 0 18 18 18 255 1 0 20 20 20 255 1 0 21 21 21 255 1 0 22 22 22 255 1 0 23 23 23 255 1 0 24 24 24 255 1 0 24 24 24 255 1 0 25 25 25 255 1 0 27 27 27 255 1 0 28 28 28 255 1 0 29 29 29 255 1 0 30 30 30 255 1 0 31 31 31 255 1 0 32 32 32 255 1 0 32 32 32 255 1 0 33 33 33 255 1 0 34 34 34 255 1 0 36 36 36 255 1 0 37 37 37 255 1 0 38 38 38 255 1 0 39 39 39 255 1 0 40 40 40 255 1 0 40 40 40 255 1 0 41 41 41 255 1 0 43 43 43 255 1 0 44 44 44 255 1 0 45 45 45 255 1 0 46 46 46 255 1 0 47 47 47 255 1 0 48 48 48 255 1 0 48 48 48 255 1 0 49 49 49 255 1 0 50 50 50 255 1 0 52 52 52 255 1 0 53 53 53 255 1 0 54 54 54 255 1 0 55 55 55 255 1 0 56 56 56 255 1 0 56 56 56 255 1 0 57 57 57 255 1 0 59 59 59 255 1 0 60 60 60 255 1 0 61 61 61 255 1 0 62 62 62 255 1 0 63 63 63 255 1 0 64 64 64 255 1 0 65 65 65 255 1 0 65 65 65 255 1 0 66 66 66 255 1 0 68 68 68 255 1 0 69 69 69 255 1 0 70 70 70 255 1 0 71 71 71 255 1 0 72 72 72 255 1 0 73 73 73 255 1 0 73 73 73 255 1 0 75 75 75 255 1 0 76 76 76 255 1 0 77 77 77 255 1 0 78 78 78 255 1 0 79 79 79 255 1 0 80 80 80 255 1 0 81 81 81 255 1 0 81 81 81 255 1 0 82 82 82 255 1 0 84 84 84 255 1 0 85 85 85 255 1 0 86 86 86 255 1 0 87 87 87 255 1 0 88 88 88 255 1 0 89 89 89 255 1 0 89 89 89 255 1 0 91 91 91 255 1 0 92 92 92 255 1 0 93 93 93 255 1 0 94 94 94 255 1 0 95 95 95 255 1 0 96 96 96 255 1 0 97 97 97 255 1 0 97 97 97 255 1 0 98 98 98 255 1 0 100 100 100 255 1 0 101 101 101 255 1 0 102 102 102 255 1 0 103 103 103 255 1 0 104 104 104 255 1 0 105 105 105 255 1 0 105 105 105 255 1 0 107 107 107 255 1 0 108 108 108 255 1 0 109 109 109 255 1 0 110 110 110 255 1 0 111 111 111 255 1 0 112 112 112 255 1 0 113 113 113 255 1 0 113 113 113 255 1 0 114 114 114 255 1 0 116 116 116 255 1 0 117 117 117 255 1 0 118 118 118 255 1 0 119 119 119 255 1 0 120 120 120 255 1 0 121 121 121 255 1 0 121 121 121 255 1 0 123 123 123 255 1 0 124 124 124 255 1 0 125 125 125 255 1 0 126 126 126 255 1 0 127 127 127 255 1 0 128 128 128 255 1 0 129 129 129 255 1 0 130 130 130 255 1 0 131 131 131 255 1 0 131 131 131 255 1 0 133 133 133 255 1 0 134 134 134 255 1 0 135 135 135 255 1 0 136 136 136 255 1 0 137 137 137 255 1 0 138 138 138 255 1 0 139 139 139 255 1 0 140 140 140 255 1 0 141 141 141 255 1 0 142 142 142 255 1 0 143 143 143 255 1 0 144 144 144 255 1 0 145 145 145 255 1 0 146 146 146 255 1 0 147 147 147 255 1 0 147 147 147 255 1 0 149 149 149 255 1 0 150 150 150 255 1 0 151 151 151 255 1 0 152 152 152 255 1 0 153 153 153 255 1 0 154 154 154 255 1 0 155 155 155 255 1 0 156 156 156 255 1 0 157 157 157 255 1 0 158 158 158 255 1 0 159 159 159 255 1 0 160 160 160 255 1 0 161 161 161 255 1 0 162 162 162 255 1 0 163 163 163 255 1 0 163 163 163 255 1 0 165 165 165 255 1 0 166 166 166 255 1 0 167 167 167 255 1 0 168 168 168 255 1 0 169 169 169 255 1 0 170 170 170 255 1 0 171 171 171 255 1 0 172 172 172 255 1 0 173 173 173 255 1 0 174 174 174 255 1 0 175 175 175 255 1 0 176 176 176 255 1 0 177 177 177 255 1 0 178 178 178 255 1 0 179 179 179 255 1 0 179 179 179 255 1 0 181 181 181 255 1 0 182 182 182 255 1 0 183 183 183 255 1 0 184 184 184 255 1 0 185 185 185 255 1 0 186 186 186 255 1 0 187 187 187 255 1 0 188 188 188 255 1 0 189 189 189 255 1 0 190 190 190 255 1 0 191 191 191 255 1 0 192 192 192 255 1 0 193 193 193 255 1 0 194 194 194 255 1 0 195 195 195 255 1 0 195 195 195 255 1 0 197 197 197 255 1 0 198 198 198 255 1 0 199 199 199 255 1 0 200 200 200 255 1 0 201 201 201 255 1 0 202 202 202 255 1 0 203 203 203 255 1 0 204 204 204 255 1 0 205 205 205 255 1 0 206 206 206 255 1 0 207 207 207 255 1 0 208 208 208 255 1 0 209 209 209 255 1 0 210 210 210 255 1 0 211 211 211 255 1 0 211 211 211 255 1 0 213 213 213 255 1 0 214 214 214 255 1 0 215 215 215 255 1 0 216 216 216 255 1 0 217 217 217 255 1 0 218 218 218 255 1 0 219 219 219 255 1 0 220 220 220 255 1 0 221 221 221 255 1 0 222 222 222 255 1 0 223 223 223 255 1 0 224 224 224 255 1 0 225 225 225 255 1 0 226 226 226 255 1 0 227 227 227 255 1 0 228 228 228 255 1 0 229 229 229 255 1 0 230 230 230 255 1 0 231 231 231 255 1 0 232 232 232 255 1 0 233 233 233 255 1 0 234 234 234 255 1 0 235 235 235 255 1 0 236 236 236 255 1 0 237 237 237 255 1 0 238 238 238 255 1 0 239 239 239 255 1 0 240 240 240 255 1 0 241 241 241 255 1 0 242 242 242 255 1 0 243 243 243 255 1 0 244 244 244 255 1 0 245 245 245 255 1 0 246 246 246 255 1 0 247 247 247 255 1 0 248 248 248 255 1 0 249 249 249 255 1 0 250 250 250 255 1 0 251 251 251 255 1 0 252 252 252 255 1 0 253 253 253 255 1 0 254 254 254 255 1 0 255 255 255 255 1 diff --git a/python/tests/reference/Colormap/binary.msh b/python/tests/reference/Colormap/binary.msh new file mode 100644 index 000000000..66b1cf863 --- /dev/null +++ b/python/tests/reference/Colormap/binary.msh @@ -0,0 +1,258 @@ +View.ColorTable = { +255.0,255.0,255.0, +254.0,254.0,254.0, +253.0,253.0,253.0, +252.0,252.0,252.0, +251.0,251.0,251.0, +250.0,250.0,250.0, +249.0,249.0,249.0, +248.0,248.0,248.0, +247.0,247.0,247.0, +246.0,246.0,246.0, +245.0,245.0,245.0, +244.0,244.0,244.0, +243.0,243.0,243.0, +242.0,242.0,242.0, +241.0,241.0,241.0, +240.0,240.0,240.0, +239.0,239.0,239.0, +238.0,238.0,238.0, +237.0,237.0,237.0, +236.0,236.0,236.0, +235.00000000000003,235.00000000000003,235.00000000000003, +234.0,234.0,234.0, +233.0,233.0,233.0, +232.0,232.0,232.0, +231.0,231.0,231.0, +230.0,230.0,230.0, +229.0,229.0,229.0, +228.0,228.0,228.0, +227.0,227.0,227.0, +226.0,226.0,226.0, +225.0,225.0,225.0, +224.0,224.0,224.0, +223.0,223.0,223.0, +222.0,222.0,222.0, +221.0,221.0,221.0, +220.0,220.0,220.0, +219.00000000000003,219.00000000000003,219.00000000000003, +218.00000000000003,218.00000000000003,218.00000000000003, +217.0,217.0,217.0, +216.0,216.0,216.0, +215.0,215.0,215.0, +214.0,214.0,214.0, +213.0,213.0,213.0, +211.99999999999997,211.99999999999997,211.99999999999997, +211.0,211.0,211.0, +210.0,210.0,210.0, +209.0,209.0,209.0, +208.0,208.0,208.0, +207.0,207.0,207.0, +206.0,206.0,206.0, +205.0,205.0,205.0, +204.0,204.0,204.0, +203.00000000000003,203.00000000000003,203.00000000000003, +202.00000000000003,202.00000000000003,202.00000000000003, +201.0,201.0,201.0, +200.0,200.0,200.0, +199.0,199.0,199.0, +198.0,198.0,198.0, +197.0,197.0,197.0, +195.99999999999997,195.99999999999997,195.99999999999997, +195.0,195.0,195.0, +194.0,194.0,194.0, +193.0,193.0,193.0, +192.0,192.0,192.0, +191.0,191.0,191.0, +190.0,190.0,190.0, +189.0,189.0,189.0, +188.0,188.0,188.0, +187.00000000000003,187.00000000000003,187.00000000000003, +186.00000000000003,186.00000000000003,186.00000000000003, +185.0,185.0,185.0, +184.0,184.0,184.0, +183.0,183.0,183.0, +182.0,182.0,182.0, +181.0,181.0,181.0, +179.99999999999997,179.99999999999997,179.99999999999997, +179.0,179.0,179.0, +178.0,178.0,178.0, +177.0,177.0,177.0, +176.0,176.0,176.0, +175.0,175.0,175.0, +174.0,174.0,174.0, +173.0,173.0,173.0, +172.0,172.0,172.0, +171.00000000000003,171.00000000000003,171.00000000000003, +170.00000000000003,170.00000000000003,170.00000000000003, +169.0,169.0,169.0, +168.0,168.0,168.0, +167.0,167.0,167.0, +166.0,166.0,166.0, +165.0,165.0,165.0, +163.99999999999997,163.99999999999997,163.99999999999997, +163.0,163.0,163.0, +162.0,162.0,162.0, +161.0,161.0,161.0, +160.0,160.0,160.0, +159.0,159.0,159.0, +158.0,158.0,158.0, +157.0,157.0,157.0, +156.0,156.0,156.0, +155.00000000000003,155.00000000000003,155.00000000000003, +154.00000000000003,154.00000000000003,154.00000000000003, +153.0,153.0,153.0, +152.0,152.0,152.0, +151.0,151.0,151.0, +150.0,150.0,150.0, +149.0,149.0,149.0, +147.99999999999997,147.99999999999997,147.99999999999997, +147.0,147.0,147.0, +146.0,146.0,146.0, +145.0,145.0,145.0, +144.0,144.0,144.0, +143.0,143.0,143.0, +142.0,142.0,142.0, +141.0,141.0,141.0, +140.0,140.0,140.0, +139.00000000000003,139.00000000000003,139.00000000000003, +138.00000000000003,138.00000000000003,138.00000000000003, +137.0,137.0,137.0, +136.0,136.0,136.0, +135.0,135.0,135.0, +134.0,134.0,134.0, +133.0,133.0,133.0, +131.99999999999997,131.99999999999997,131.99999999999997, +131.0,131.0,131.0, +130.0,130.0,130.0, +129.0,129.0,129.0, +128.0,128.0,128.0, +127.0,127.0,127.0, +126.0,126.0,126.0, +125.00000000000001,125.00000000000001,125.00000000000001, +124.00000000000001,124.00000000000001,124.00000000000001, +123.00000000000001,123.00000000000001,123.00000000000001, +121.99999999999999,121.99999999999999,121.99999999999999, +121.0,121.0,121.0, +120.0,120.0,120.0, +119.0,119.0,119.0, +118.0,118.0,118.0, +117.00000000000001,117.00000000000001,117.00000000000001, +116.00000000000001,116.00000000000001,116.00000000000001, +114.99999999999999,114.99999999999999,114.99999999999999, +113.99999999999999,113.99999999999999,113.99999999999999, +113.0,113.0,113.0, +112.0,112.0,112.0, +111.0,111.0,111.0, +110.0,110.0,110.0, +109.00000000000001,109.00000000000001,109.00000000000001, +108.00000000000001,108.00000000000001,108.00000000000001, +107.00000000000001,107.00000000000001,107.00000000000001, +105.99999999999999,105.99999999999999,105.99999999999999, +105.0,105.0,105.0, +104.0,104.0,104.0, +103.0,103.0,103.0, +102.0,102.0,102.0, +101.00000000000001,101.00000000000001,101.00000000000001, +100.00000000000001,100.00000000000001,100.00000000000001, +98.99999999999999,98.99999999999999,98.99999999999999, +97.99999999999999,97.99999999999999,97.99999999999999, +97.0,97.0,97.0, +96.0,96.0,96.0, +95.0,95.0,95.0, +94.0,94.0,94.0, +93.00000000000001,93.00000000000001,93.00000000000001, +92.00000000000001,92.00000000000001,92.00000000000001, +91.00000000000001,91.00000000000001,91.00000000000001, +89.99999999999999,89.99999999999999,89.99999999999999, +89.0,89.0,89.0, +88.0,88.0,88.0, +87.0,87.0,87.0, +86.0,86.0,86.0, +85.00000000000001,85.00000000000001,85.00000000000001, +84.00000000000001,84.00000000000001,84.00000000000001, +82.99999999999999,82.99999999999999,82.99999999999999, +81.99999999999999,81.99999999999999,81.99999999999999, +81.0,81.0,81.0, +80.0,80.0,80.0, +79.0,79.0,79.0, +78.0,78.0,78.0, +77.00000000000001,77.00000000000001,77.00000000000001, +76.00000000000001,76.00000000000001,76.00000000000001, +75.00000000000001,75.00000000000001,75.00000000000001, +73.99999999999999,73.99999999999999,73.99999999999999, +73.0,73.0,73.0, +72.0,72.0,72.0, +71.0,71.0,71.0, +70.0,70.0,70.0, +69.00000000000001,69.00000000000001,69.00000000000001, +68.00000000000001,68.00000000000001,68.00000000000001, +66.99999999999999,66.99999999999999,66.99999999999999, +65.99999999999999,65.99999999999999,65.99999999999999, +65.0,65.0,65.0, +64.0,64.0,64.0, +63.0,63.0,63.0, +62.00000000000001,62.00000000000001,62.00000000000001, +61.00000000000001,61.00000000000001,61.00000000000001, +60.000000000000014,60.000000000000014,60.000000000000014, +59.000000000000014,59.000000000000014,59.000000000000014, +57.99999999999999,57.99999999999999,57.99999999999999, +56.99999999999999,56.99999999999999,56.99999999999999, +56.0,56.0,56.0, +55.0,55.0,55.0, +54.00000000000001,54.00000000000001,54.00000000000001, +53.00000000000001,53.00000000000001,53.00000000000001, +52.000000000000014,52.000000000000014,52.000000000000014, +50.999999999999986,50.999999999999986,50.999999999999986, +49.99999999999999,49.99999999999999,49.99999999999999, +48.99999999999999,48.99999999999999,48.99999999999999, +48.0,48.0,48.0, +47.0,47.0,47.0, +46.00000000000001,46.00000000000001,46.00000000000001, +45.00000000000001,45.00000000000001,45.00000000000001, +44.000000000000014,44.000000000000014,44.000000000000014, +43.000000000000014,43.000000000000014,43.000000000000014, +41.99999999999999,41.99999999999999,41.99999999999999, +40.99999999999999,40.99999999999999,40.99999999999999, +40.0,40.0,40.0, +39.0,39.0,39.0, +38.00000000000001,38.00000000000001,38.00000000000001, +37.00000000000001,37.00000000000001,37.00000000000001, +36.000000000000014,36.000000000000014,36.000000000000014, +34.999999999999986,34.999999999999986,34.999999999999986, +33.99999999999999,33.99999999999999,33.99999999999999, +32.99999999999999,32.99999999999999,32.99999999999999, +32.0,32.0,32.0, +31.000000000000004,31.000000000000004,31.000000000000004, +30.000000000000007,30.000000000000007,30.000000000000007, +29.00000000000001,29.00000000000001,29.00000000000001, +28.000000000000014,28.000000000000014,28.000000000000014, +27.000000000000018,27.000000000000018,27.000000000000018, +25.999999999999993,25.999999999999993,25.999999999999993, +24.999999999999996,24.999999999999996,24.999999999999996, +24.0,24.0,24.0, +23.000000000000004,23.000000000000004,23.000000000000004, +22.000000000000007,22.000000000000007,22.000000000000007, +21.00000000000001,21.00000000000001,21.00000000000001, +20.000000000000014,20.000000000000014,20.000000000000014, +18.99999999999999,18.99999999999999,18.99999999999999, +17.999999999999993,17.999999999999993,17.999999999999993, +16.999999999999996,16.999999999999996,16.999999999999996, +16.0,16.0,16.0, +15.000000000000004,15.000000000000004,15.000000000000004, +14.000000000000007,14.000000000000007,14.000000000000007, +13.00000000000001,13.00000000000001,13.00000000000001, +12.000000000000014,12.000000000000014,12.000000000000014, +11.000000000000018,11.000000000000018,11.000000000000018, +9.999999999999993,9.999999999999993,9.999999999999993, +8.999999999999996,8.999999999999996,8.999999999999996, +8.0,8.0,8.0, +7.0000000000000036,7.0000000000000036,7.0000000000000036, +6.000000000000007,6.000000000000007,6.000000000000007, +5.000000000000011,5.000000000000011,5.000000000000011, +4.000000000000014,4.000000000000014,4.000000000000014, +2.9999999999999893,2.9999999999999893,2.9999999999999893, +1.999999999999993,1.999999999999993,1.999999999999993, +0.9999999999999964,0.9999999999999964,0.9999999999999964, +0.0,0.0,0.0, +} diff --git a/python/tests/reference/Colormap/binary.txt b/python/tests/reference/Colormap/binary.txt new file mode 100644 index 000000000..65e5e3d58 --- /dev/null +++ b/python/tests/reference/Colormap/binary.txt @@ -0,0 +1,258 @@ +# Creator: damask.Colormap 99.99.99-9999-pytest +R G B alpha +1.0 1.0 1.0 1.0 +0.996078431372549 0.996078431372549 0.996078431372549 1.0 +0.9921568627450981 0.9921568627450981 0.9921568627450981 1.0 +0.9882352941176471 0.9882352941176471 0.9882352941176471 1.0 +0.9843137254901961 0.9843137254901961 0.9843137254901961 1.0 +0.9803921568627451 0.9803921568627451 0.9803921568627451 1.0 +0.9764705882352941 0.9764705882352941 0.9764705882352941 1.0 +0.9725490196078431 0.9725490196078431 0.9725490196078431 1.0 +0.9686274509803922 0.9686274509803922 0.9686274509803922 1.0 +0.9647058823529412 0.9647058823529412 0.9647058823529412 1.0 +0.9607843137254902 0.9607843137254902 0.9607843137254902 1.0 +0.9568627450980393 0.9568627450980393 0.9568627450980393 1.0 +0.9529411764705882 0.9529411764705882 0.9529411764705882 1.0 +0.9490196078431372 0.9490196078431372 0.9490196078431372 1.0 +0.9450980392156862 0.9450980392156862 0.9450980392156862 1.0 +0.9411764705882353 0.9411764705882353 0.9411764705882353 1.0 +0.9372549019607843 0.9372549019607843 0.9372549019607843 1.0 +0.9333333333333333 0.9333333333333333 0.9333333333333333 1.0 +0.9294117647058824 0.9294117647058824 0.9294117647058824 1.0 +0.9254901960784314 0.9254901960784314 0.9254901960784314 1.0 +0.9215686274509804 0.9215686274509804 0.9215686274509804 1.0 +0.9176470588235294 0.9176470588235294 0.9176470588235294 1.0 +0.9137254901960784 0.9137254901960784 0.9137254901960784 1.0 +0.9098039215686274 0.9098039215686274 0.9098039215686274 1.0 +0.9058823529411765 0.9058823529411765 0.9058823529411765 1.0 +0.9019607843137255 0.9019607843137255 0.9019607843137255 1.0 +0.8980392156862745 0.8980392156862745 0.8980392156862745 1.0 +0.8941176470588236 0.8941176470588236 0.8941176470588236 1.0 +0.8901960784313725 0.8901960784313725 0.8901960784313725 1.0 +0.8862745098039215 0.8862745098039215 0.8862745098039215 1.0 +0.8823529411764706 0.8823529411764706 0.8823529411764706 1.0 +0.8784313725490196 0.8784313725490196 0.8784313725490196 1.0 +0.8745098039215686 0.8745098039215686 0.8745098039215686 1.0 +0.8705882352941177 0.8705882352941177 0.8705882352941177 1.0 +0.8666666666666667 0.8666666666666667 0.8666666666666667 1.0 +0.8627450980392157 0.8627450980392157 0.8627450980392157 1.0 +0.8588235294117648 0.8588235294117648 0.8588235294117648 1.0 +0.8549019607843138 0.8549019607843138 0.8549019607843138 1.0 +0.8509803921568627 0.8509803921568627 0.8509803921568627 1.0 +0.8470588235294118 0.8470588235294118 0.8470588235294118 1.0 +0.8431372549019608 0.8431372549019608 0.8431372549019608 1.0 +0.8392156862745098 0.8392156862745098 0.8392156862745098 1.0 +0.8352941176470589 0.8352941176470589 0.8352941176470589 1.0 +0.8313725490196078 0.8313725490196078 0.8313725490196078 1.0 +0.8274509803921568 0.8274509803921568 0.8274509803921568 1.0 +0.8235294117647058 0.8235294117647058 0.8235294117647058 1.0 +0.8196078431372549 0.8196078431372549 0.8196078431372549 1.0 +0.8156862745098039 0.8156862745098039 0.8156862745098039 1.0 +0.8117647058823529 0.8117647058823529 0.8117647058823529 1.0 +0.807843137254902 0.807843137254902 0.807843137254902 1.0 +0.803921568627451 0.803921568627451 0.803921568627451 1.0 +0.8 0.8 0.8 1.0 +0.7960784313725491 0.7960784313725491 0.7960784313725491 1.0 +0.7921568627450981 0.7921568627450981 0.7921568627450981 1.0 +0.788235294117647 0.788235294117647 0.788235294117647 1.0 +0.7843137254901961 0.7843137254901961 0.7843137254901961 1.0 +0.7803921568627451 0.7803921568627451 0.7803921568627451 1.0 +0.7764705882352941 0.7764705882352941 0.7764705882352941 1.0 +0.7725490196078432 0.7725490196078432 0.7725490196078432 1.0 +0.7686274509803921 0.7686274509803921 0.7686274509803921 1.0 +0.7647058823529411 0.7647058823529411 0.7647058823529411 1.0 +0.7607843137254902 0.7607843137254902 0.7607843137254902 1.0 +0.7568627450980392 0.7568627450980392 0.7568627450980392 1.0 +0.7529411764705882 0.7529411764705882 0.7529411764705882 1.0 +0.7490196078431373 0.7490196078431373 0.7490196078431373 1.0 +0.7450980392156863 0.7450980392156863 0.7450980392156863 1.0 +0.7411764705882353 0.7411764705882353 0.7411764705882353 1.0 +0.7372549019607844 0.7372549019607844 0.7372549019607844 1.0 +0.7333333333333334 0.7333333333333334 0.7333333333333334 1.0 +0.7294117647058824 0.7294117647058824 0.7294117647058824 1.0 +0.7254901960784313 0.7254901960784313 0.7254901960784313 1.0 +0.7215686274509804 0.7215686274509804 0.7215686274509804 1.0 +0.7176470588235294 0.7176470588235294 0.7176470588235294 1.0 +0.7137254901960784 0.7137254901960784 0.7137254901960784 1.0 +0.7098039215686275 0.7098039215686275 0.7098039215686275 1.0 +0.7058823529411764 0.7058823529411764 0.7058823529411764 1.0 +0.7019607843137254 0.7019607843137254 0.7019607843137254 1.0 +0.6980392156862745 0.6980392156862745 0.6980392156862745 1.0 +0.6941176470588235 0.6941176470588235 0.6941176470588235 1.0 +0.6901960784313725 0.6901960784313725 0.6901960784313725 1.0 +0.6862745098039216 0.6862745098039216 0.6862745098039216 1.0 +0.6823529411764706 0.6823529411764706 0.6823529411764706 1.0 +0.6784313725490196 0.6784313725490196 0.6784313725490196 1.0 +0.6745098039215687 0.6745098039215687 0.6745098039215687 1.0 +0.6705882352941177 0.6705882352941177 0.6705882352941177 1.0 +0.6666666666666667 0.6666666666666667 0.6666666666666667 1.0 +0.6627450980392157 0.6627450980392157 0.6627450980392157 1.0 +0.6588235294117647 0.6588235294117647 0.6588235294117647 1.0 +0.6549019607843137 0.6549019607843137 0.6549019607843137 1.0 +0.6509803921568628 0.6509803921568628 0.6509803921568628 1.0 +0.6470588235294118 0.6470588235294118 0.6470588235294118 1.0 +0.6431372549019607 0.6431372549019607 0.6431372549019607 1.0 +0.6392156862745098 0.6392156862745098 0.6392156862745098 1.0 +0.6352941176470588 0.6352941176470588 0.6352941176470588 1.0 +0.6313725490196078 0.6313725490196078 0.6313725490196078 1.0 +0.6274509803921569 0.6274509803921569 0.6274509803921569 1.0 +0.6235294117647059 0.6235294117647059 0.6235294117647059 1.0 +0.6196078431372549 0.6196078431372549 0.6196078431372549 1.0 +0.615686274509804 0.615686274509804 0.615686274509804 1.0 +0.611764705882353 0.611764705882353 0.611764705882353 1.0 +0.607843137254902 0.607843137254902 0.607843137254902 1.0 +0.603921568627451 0.603921568627451 0.603921568627451 1.0 +0.6 0.6 0.6 1.0 +0.596078431372549 0.596078431372549 0.596078431372549 1.0 +0.592156862745098 0.592156862745098 0.592156862745098 1.0 +0.5882352941176471 0.5882352941176471 0.5882352941176471 1.0 +0.5843137254901961 0.5843137254901961 0.5843137254901961 1.0 +0.580392156862745 0.580392156862745 0.580392156862745 1.0 +0.5764705882352941 0.5764705882352941 0.5764705882352941 1.0 +0.5725490196078431 0.5725490196078431 0.5725490196078431 1.0 +0.5686274509803921 0.5686274509803921 0.5686274509803921 1.0 +0.5647058823529412 0.5647058823529412 0.5647058823529412 1.0 +0.5607843137254902 0.5607843137254902 0.5607843137254902 1.0 +0.5568627450980392 0.5568627450980392 0.5568627450980392 1.0 +0.5529411764705883 0.5529411764705883 0.5529411764705883 1.0 +0.5490196078431373 0.5490196078431373 0.5490196078431373 1.0 +0.5450980392156863 0.5450980392156863 0.5450980392156863 1.0 +0.5411764705882354 0.5411764705882354 0.5411764705882354 1.0 +0.5372549019607843 0.5372549019607843 0.5372549019607843 1.0 +0.5333333333333333 0.5333333333333333 0.5333333333333333 1.0 +0.5294117647058824 0.5294117647058824 0.5294117647058824 1.0 +0.5254901960784314 0.5254901960784314 0.5254901960784314 1.0 +0.5215686274509804 0.5215686274509804 0.5215686274509804 1.0 +0.5176470588235293 0.5176470588235293 0.5176470588235293 1.0 +0.5137254901960784 0.5137254901960784 0.5137254901960784 1.0 +0.5098039215686274 0.5098039215686274 0.5098039215686274 1.0 +0.5058823529411764 0.5058823529411764 0.5058823529411764 1.0 +0.5019607843137255 0.5019607843137255 0.5019607843137255 1.0 +0.4980392156862745 0.4980392156862745 0.4980392156862745 1.0 +0.49411764705882355 0.49411764705882355 0.49411764705882355 1.0 +0.4901960784313726 0.4901960784313726 0.4901960784313726 1.0 +0.4862745098039216 0.4862745098039216 0.4862745098039216 1.0 +0.48235294117647065 0.48235294117647065 0.48235294117647065 1.0 +0.4784313725490196 0.4784313725490196 0.4784313725490196 1.0 +0.4745098039215686 0.4745098039215686 0.4745098039215686 1.0 +0.47058823529411764 0.47058823529411764 0.47058823529411764 1.0 +0.4666666666666667 0.4666666666666667 0.4666666666666667 1.0 +0.4627450980392157 0.4627450980392157 0.4627450980392157 1.0 +0.45882352941176474 0.45882352941176474 0.45882352941176474 1.0 +0.4549019607843138 0.4549019607843138 0.4549019607843138 1.0 +0.4509803921568627 0.4509803921568627 0.4509803921568627 1.0 +0.44705882352941173 0.44705882352941173 0.44705882352941173 1.0 +0.44313725490196076 0.44313725490196076 0.44313725490196076 1.0 +0.4392156862745098 0.4392156862745098 0.4392156862745098 1.0 +0.43529411764705883 0.43529411764705883 0.43529411764705883 1.0 +0.43137254901960786 0.43137254901960786 0.43137254901960786 1.0 +0.4274509803921569 0.4274509803921569 0.4274509803921569 1.0 +0.42352941176470593 0.42352941176470593 0.42352941176470593 1.0 +0.41960784313725497 0.41960784313725497 0.41960784313725497 1.0 +0.4156862745098039 0.4156862745098039 0.4156862745098039 1.0 +0.4117647058823529 0.4117647058823529 0.4117647058823529 1.0 +0.40784313725490196 0.40784313725490196 0.40784313725490196 1.0 +0.403921568627451 0.403921568627451 0.403921568627451 1.0 +0.4 0.4 0.4 1.0 +0.39607843137254906 0.39607843137254906 0.39607843137254906 1.0 +0.3921568627450981 0.3921568627450981 0.3921568627450981 1.0 +0.388235294117647 0.388235294117647 0.388235294117647 1.0 +0.38431372549019605 0.38431372549019605 0.38431372549019605 1.0 +0.3803921568627451 0.3803921568627451 0.3803921568627451 1.0 +0.3764705882352941 0.3764705882352941 0.3764705882352941 1.0 +0.37254901960784315 0.37254901960784315 0.37254901960784315 1.0 +0.3686274509803922 0.3686274509803922 0.3686274509803922 1.0 +0.3647058823529412 0.3647058823529412 0.3647058823529412 1.0 +0.36078431372549025 0.36078431372549025 0.36078431372549025 1.0 +0.3568627450980393 0.3568627450980393 0.3568627450980393 1.0 +0.3529411764705882 0.3529411764705882 0.3529411764705882 1.0 +0.34901960784313724 0.34901960784313724 0.34901960784313724 1.0 +0.34509803921568627 0.34509803921568627 0.34509803921568627 1.0 +0.3411764705882353 0.3411764705882353 0.3411764705882353 1.0 +0.33725490196078434 0.33725490196078434 0.33725490196078434 1.0 +0.33333333333333337 0.33333333333333337 0.33333333333333337 1.0 +0.3294117647058824 0.3294117647058824 0.3294117647058824 1.0 +0.3254901960784313 0.3254901960784313 0.3254901960784313 1.0 +0.32156862745098036 0.32156862745098036 0.32156862745098036 1.0 +0.3176470588235294 0.3176470588235294 0.3176470588235294 1.0 +0.3137254901960784 0.3137254901960784 0.3137254901960784 1.0 +0.30980392156862746 0.30980392156862746 0.30980392156862746 1.0 +0.3058823529411765 0.3058823529411765 0.3058823529411765 1.0 +0.3019607843137255 0.3019607843137255 0.3019607843137255 1.0 +0.29803921568627456 0.29803921568627456 0.29803921568627456 1.0 +0.2941176470588236 0.2941176470588236 0.2941176470588236 1.0 +0.2901960784313725 0.2901960784313725 0.2901960784313725 1.0 +0.28627450980392155 0.28627450980392155 0.28627450980392155 1.0 +0.2823529411764706 0.2823529411764706 0.2823529411764706 1.0 +0.2784313725490196 0.2784313725490196 0.2784313725490196 1.0 +0.27450980392156865 0.27450980392156865 0.27450980392156865 1.0 +0.2705882352941177 0.2705882352941177 0.2705882352941177 1.0 +0.2666666666666667 0.2666666666666667 0.2666666666666667 1.0 +0.26274509803921564 0.26274509803921564 0.26274509803921564 1.0 +0.2588235294117647 0.2588235294117647 0.2588235294117647 1.0 +0.2549019607843137 0.2549019607843137 0.2549019607843137 1.0 +0.25098039215686274 0.25098039215686274 0.25098039215686274 1.0 +0.24705882352941178 0.24705882352941178 0.24705882352941178 1.0 +0.2431372549019608 0.2431372549019608 0.2431372549019608 1.0 +0.23921568627450984 0.23921568627450984 0.23921568627450984 1.0 +0.23529411764705888 0.23529411764705888 0.23529411764705888 1.0 +0.2313725490196079 0.2313725490196079 0.2313725490196079 1.0 +0.22745098039215683 0.22745098039215683 0.22745098039215683 1.0 +0.22352941176470587 0.22352941176470587 0.22352941176470587 1.0 +0.2196078431372549 0.2196078431372549 0.2196078431372549 1.0 +0.21568627450980393 0.21568627450980393 0.21568627450980393 1.0 +0.21176470588235297 0.21176470588235297 0.21176470588235297 1.0 +0.207843137254902 0.207843137254902 0.207843137254902 1.0 +0.20392156862745103 0.20392156862745103 0.20392156862745103 1.0 +0.19999999999999996 0.19999999999999996 0.19999999999999996 1.0 +0.196078431372549 0.196078431372549 0.196078431372549 1.0 +0.19215686274509802 0.19215686274509802 0.19215686274509802 1.0 +0.18823529411764706 0.18823529411764706 0.18823529411764706 1.0 +0.1843137254901961 0.1843137254901961 0.1843137254901961 1.0 +0.18039215686274512 0.18039215686274512 0.18039215686274512 1.0 +0.17647058823529416 0.17647058823529416 0.17647058823529416 1.0 +0.1725490196078432 0.1725490196078432 0.1725490196078432 1.0 +0.16862745098039222 0.16862745098039222 0.16862745098039222 1.0 +0.16470588235294115 0.16470588235294115 0.16470588235294115 1.0 +0.16078431372549018 0.16078431372549018 0.16078431372549018 1.0 +0.1568627450980392 0.1568627450980392 0.1568627450980392 1.0 +0.15294117647058825 0.15294117647058825 0.15294117647058825 1.0 +0.14901960784313728 0.14901960784313728 0.14901960784313728 1.0 +0.14509803921568631 0.14509803921568631 0.14509803921568631 1.0 +0.14117647058823535 0.14117647058823535 0.14117647058823535 1.0 +0.13725490196078427 0.13725490196078427 0.13725490196078427 1.0 +0.1333333333333333 0.1333333333333333 0.1333333333333333 1.0 +0.12941176470588234 0.12941176470588234 0.12941176470588234 1.0 +0.12549019607843137 0.12549019607843137 0.12549019607843137 1.0 +0.1215686274509804 0.1215686274509804 0.1215686274509804 1.0 +0.11764705882352944 0.11764705882352944 0.11764705882352944 1.0 +0.11372549019607847 0.11372549019607847 0.11372549019607847 1.0 +0.1098039215686275 0.1098039215686275 0.1098039215686275 1.0 +0.10588235294117654 0.10588235294117654 0.10588235294117654 1.0 +0.10196078431372546 0.10196078431372546 0.10196078431372546 1.0 +0.0980392156862745 0.0980392156862745 0.0980392156862745 1.0 +0.09411764705882353 0.09411764705882353 0.09411764705882353 1.0 +0.09019607843137256 0.09019607843137256 0.09019607843137256 1.0 +0.0862745098039216 0.0862745098039216 0.0862745098039216 1.0 +0.08235294117647063 0.08235294117647063 0.08235294117647063 1.0 +0.07843137254901966 0.07843137254901966 0.07843137254901966 1.0 +0.07450980392156858 0.07450980392156858 0.07450980392156858 1.0 +0.07058823529411762 0.07058823529411762 0.07058823529411762 1.0 +0.06666666666666665 0.06666666666666665 0.06666666666666665 1.0 +0.06274509803921569 0.06274509803921569 0.06274509803921569 1.0 +0.05882352941176472 0.05882352941176472 0.05882352941176472 1.0 +0.05490196078431375 0.05490196078431375 0.05490196078431375 1.0 +0.050980392156862786 0.050980392156862786 0.050980392156862786 1.0 +0.04705882352941182 0.04705882352941182 0.04705882352941182 1.0 +0.04313725490196085 0.04313725490196085 0.04313725490196085 1.0 +0.039215686274509776 0.039215686274509776 0.039215686274509776 1.0 +0.03529411764705881 0.03529411764705881 0.03529411764705881 1.0 +0.03137254901960784 0.03137254901960784 0.03137254901960784 1.0 +0.027450980392156876 0.027450980392156876 0.027450980392156876 1.0 +0.02352941176470591 0.02352941176470591 0.02352941176470591 1.0 +0.019607843137254943 0.019607843137254943 0.019607843137254943 1.0 +0.015686274509803977 0.015686274509803977 0.015686274509803977 1.0 +0.0117647058823529 0.0117647058823529 0.0117647058823529 1.0 +0.007843137254901933 0.007843137254901933 0.007843137254901933 1.0 +0.0039215686274509665 0.0039215686274509665 0.0039215686274509665 1.0 +0.0 0.0 0.0 1.0 diff --git a/python/tests/test_Colormap.py b/python/tests/test_Colormap.py index f124c704e..b95d23a86 100644 --- a/python/tests/test_Colormap.py +++ b/python/tests/test_Colormap.py @@ -1,10 +1,18 @@ import os +import filecmp +import time import numpy as np import pytest +import damask from damask import Colormap +@pytest.fixture +def reference_dir(reference_dir_base): + """Directory containing reference results.""" + return reference_dir_base/'Colormap' + class TestColormap: def test_conversion(self): @@ -64,14 +72,14 @@ class TestColormap: assert np.allclose(Colormap._xyz2msh(xyz),msh,atol=1.e-6,rtol=0) - @pytest.mark.parametrize('format',['ASCII','paraview','GOM','gmsh']) + @pytest.mark.parametrize('format',['ASCII','paraview','GOM','Gmsh']) @pytest.mark.parametrize('model',['rgb','hsv','hsl','xyz','lab','msh']) def test_from_bounds(self,model,format,tmpdir): N = np.random.randint(2,256) c = Colormap.from_bounds(np.random.rand(3),np.random.rand(3),model=model,N=N) c.to_file(tmpdir/'color_out',format=format) - @pytest.mark.parametrize('format',['ASCII','paraview','GOM','gmsh']) + @pytest.mark.parametrize('format',['ASCII','paraview','GOM','Gmsh']) @pytest.mark.parametrize('name',['strain','gnuplot','Greys','PRGn','viridis']) def test_from_predefined(self,name,format,tmpdir): N = np.random.randint(2,256) @@ -82,7 +90,7 @@ class TestColormap: @pytest.mark.parametrize('format,name',[('ASCII','test.txt'), ('paraview','test.json'), ('GOM','test.legend'), - ('gmsh','test.msh') + ('Gmsh','test.msh') ]) def test_write_filehandle(self,format,name,tmpdir): c = Colormap.from_predefined('Dark2') @@ -113,4 +121,21 @@ class TestColormap: def test_list(self): Colormap.list_predefined() + @pytest.mark.parametrize('format,ext',[('ASCII','.txt'), + ('paraview','.json'), + ('GOM','.legend'), + ('Gmsh','.msh') + ]) + def test_compare_reference(self,format,ext,tmpdir,reference_dir,update,monkeypatch): + monkeypatch.setattr(damask, 'version', '99.99.99-9999-pytest') + name = 'binary' + c = Colormap.from_predefined(name) + if update: + os.chdir(reference_dir) + c.to_file(format=format) + else: + os.chdir(tmpdir) + c.to_file(format=format) + time.sleep(.5) + assert filecmp.cmp(tmpdir/(name+ext),reference_dir/(name+ext)) From b3f5ee022ace521fde51c1e35104dfafcd10402c Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 28 Jun 2020 11:40:19 +0200 Subject: [PATCH 35/78] unified style --- python/damask/_colormap.py | 6 +++--- python/damask/_result.py | 4 ++-- python/damask/_vtk.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index d3ed4b12e..f354af0a3 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -7,7 +7,7 @@ import matplotlib.pyplot as plt from matplotlib import cm import damask -from damask import Table +from . import Table _eps = 216./24389. _kappa = 24389./27. @@ -209,7 +209,7 @@ class Colormap(mpl.colors.ListedColormap): colors+=[i]+c out = [{ - 'Creator':f'damask.Colormap {damask.version}', + 'Creator':f'damask.Colormap v{damask.version}', 'ColorSpace':'RGB', 'Name':colormap.name, 'DefaultMap':True, @@ -226,7 +226,7 @@ class Colormap(mpl.colors.ListedColormap): """Write colormap to ASCII table.""" labels = {'R':(1,),'G':(1,),'B':(1,)} if colormap.colors.shape[1] == 4: labels['alpha']=(1,) - t = Table(colormap.colors,labels,f'Creator: damask.Colormap {damask.version}') + t = Table(colormap.colors,labels,f'Creator: damask.Colormap v{damask.version}') if fhandle is None: with open(colormap.name.replace(' ','_')+'.txt', 'w') as f: diff --git a/python/damask/_result.py b/python/damask/_result.py index 354c1c3f7..472355ae2 100644 --- a/python/damask/_result.py +++ b/python/damask/_result.py @@ -12,6 +12,7 @@ from functools import partial import h5py import numpy as np +import damask from . import VTK from . import Table from . import Rotation @@ -20,7 +21,6 @@ from . import Environment from . import grid_filters from . import mechanics from . import util -from . import version class Result: @@ -1090,7 +1090,7 @@ class Result: for l,v in result[1]['meta'].items(): dataset.attrs[l]=v.encode() - creator = f"damask.Result.{dataset.attrs['Creator'].decode()} v{version}" + creator = f"damask.Result.{dataset.attrs['Creator'].decode()} v{damask.version}" dataset.attrs['Creator'] = creator.encode() except (OSError,RuntimeError) as err: diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index f4855820e..0b9fde387 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -6,9 +6,9 @@ import vtk from vtk.util.numpy_support import numpy_to_vtk as np_to_vtk from vtk.util.numpy_support import numpy_to_vtkIdTypeArray as np_to_vtkIdTypeArray +import damask from . import Table from . import Environment -from . import version class VTK: @@ -214,7 +214,7 @@ class VTK: def __repr__(self): """ASCII representation of the VTK data.""" writer = vtk.vtkDataSetWriter() - writer.SetHeader(f'# DAMASK.VTK v{version}') + writer.SetHeader(f'# damask.VTK v{damask.version}') writer.WriteToOutputStringOn() writer.SetInputData(self.geom) writer.Write() From 1cfa6d44d9fb2332de356a32b05b1b9e9a7580d1 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 28 Jun 2020 11:50:28 +0200 Subject: [PATCH 36/78] dummy version could be useful for other tests note that monkey patching requires direct access to damask.version in the respective modules. 'from xx import yy' creates a copy (at least for the version string). --- python/tests/conftest.py | 5 +++++ python/tests/test_Colormap.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/python/tests/conftest.py b/python/tests/conftest.py index 411c07a8c..a7bc59f98 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -2,6 +2,11 @@ import os import pytest +# Use to monkeypatch damask.version (for comparsion to reference files that contain version information) +def pytest_configure(): + pytest.dummy_version = '99.99.99-9999-pytest' + + def pytest_addoption(parser): parser.addoption("--update", action="store_true", diff --git a/python/tests/test_Colormap.py b/python/tests/test_Colormap.py index b95d23a86..8d3d51018 100644 --- a/python/tests/test_Colormap.py +++ b/python/tests/test_Colormap.py @@ -127,7 +127,7 @@ class TestColormap: ('Gmsh','.msh') ]) def test_compare_reference(self,format,ext,tmpdir,reference_dir,update,monkeypatch): - monkeypatch.setattr(damask, 'version', '99.99.99-9999-pytest') + monkeypatch.setattr(damask, 'version', pytest.dummy_version) name = 'binary' c = Colormap.from_predefined(name) if update: From 352c4e95f1a8ce6cb6321aa10a324613e80544f9 Mon Sep 17 00:00:00 2001 From: "f.basile" Date: Sun, 28 Jun 2020 19:03:06 +0200 Subject: [PATCH 37/78] more vectorized --- python/damask/_orientation.py | 62 ++++++++++++++------------ python/tests/test_ori_vec.py | 83 +++++++++++++++++++++++++---------- 2 files changed, 95 insertions(+), 50 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 0e7b68954..e36713ee9 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -77,27 +77,19 @@ class Orientation: # ToDo: make subclass of lattice and Rotation # self-->other: True, self<--other: False def inFZ_vec(self): - """ - Check if orientations falls into Fundamental Zone. - - self.rotation.as_Rodrigues() working fine - self.rotation.as_Rodrigues(vector=True) doesn't work for several rotations - i apply dirty fix - - """ + """Check if orientations fall into Fundamental Zone.""" if not self.rotation.shape: return self.lattice.symmetry.inFZ(self.rotation.as_Rodrigues(vector=True)) else: return [self.lattice.symmetry.inFZ(\ - Rotation._qu2ro(self.rotation.as_quaternion())[l][...,:3]\ - *Rotation._qu2ro(self.rotation.as_quaternion())[l][...,3])\ - for l in range(self.rotation.shape[0])] + self.rotation.as_Rodrigues(vector=True)[l]) for l in range(self.rotation.shape[0])] + def inFZ(self): return self.lattice.symmetry.inFZ(self.rotation.as_Rodrigues(vector=True)) @property - def equivalent(self): + def equivalent_vec(self): """ Return orientations which are symmetrically equivalent. @@ -105,12 +97,12 @@ class Orientation: # ToDo: make subclass of lattice and Rotation is added to the left of the rotation array. """ - s = self.lattice.symmetry.symmetry_operations - s = s.reshape(s.shape[:1]+(1,)*len(self.rotation.shape)+(4,)) + s = self.lattice.symmetry.symmetry_operations #24 lines (sym) x 4 columns (quat) + s = s.reshape(s.shape[:1]+(1,)*len(self.rotation.shape)+(4,)) #reshape zo (24,1,4) s = Rotation(np.broadcast_to(s,s.shape[:1]+self.rotation.quaternion.shape)) - r = np.broadcast_to(self.rotation.quaternion,s.shape[:1]+self.rotation.quaternion.shape) - r = Rotation(r) + r = np.broadcast_to(self.rotation.quaternion,s.shape[:1]+self.rotation.quaternion.shape) #(24,NumRots,4) + r = Rotation(r) #(24, NumRot) return self.__class__(s@r,self.lattice) @@ -127,14 +119,17 @@ class Orientation: # ToDo: make subclass of lattice and Rotation def relatedOrientations_vec(self,model): """List of orientations related by the given orientation relationship.""" - r = self.lattice.relationOperations(model) - if not self.rotation.shape: - return [self.__class__(o*self.rotation,r['lattice']) for o in r['rotations']] - else: - return np.reshape(\ - [self.__class__(o*Rotation.from_quaternion(self.rotation.as_quaternion()[l])\ - ,r['lattice']) for o in r['rotations'] for l in range(self.rotation.shape[0])] - ,(len(r['rotations']),self.rotation.shape[0])) + h = self.lattice.relationOperations(model) + rot= h['rotations'] + op=np.array([o.as_quaternion() for o in rot]) + + s = op.reshape(op.shape[:1]+(1,)*len(self.rotation.shape)+(4,)) + s = Rotation(np.broadcast_to(s,s.shape[:1]+self.rotation.quaternion.shape)) + + r = np.broadcast_to(self.rotation.quaternion,s.shape[:1]+self.rotation.quaternion.shape) + r = Rotation(r) + + return self.__class__(s@r,h['lattice']) def relatedOrientations(self,model): @@ -142,6 +137,19 @@ class Orientation: # ToDo: make subclass of lattice and Rotation r = self.lattice.relationOperations(model) return [self.__class__(o*self.rotation,r['lattice']) for o in r['rotations']] + @property + def reduced_vec(self): + """Transform orientation to fall into fundamental zone according to symmetry.""" + equi=self.equivalent_vec.rotation #24 x rot x 3(rodrigues vector) + r= 1 if not self.rotation.shape else equi.shape[1] #number of rotations + quat=np.empty( [r , 4]) + for rot in range(r): + for sym in range(equi.shape[0]): + if self.lattice.symmetry.inFZ(equi.as_Rodrigues(vector=True)[sym,rot]) == True: + quat[rot]=equi.as_quaternion()[sym,rot] + return self.__class__(quat,self.lattice) + + def reduced(self): """Transform orientation to fall into fundamental zone according to symmetry.""" @@ -178,9 +186,9 @@ class Orientation: # ToDo: make subclass of lattice and Rotation return color - def IPF_color(self,axis): - """TSL color of inverse pole figure for given axis.""" - eq = self.equivalent + def IPFcolor_vec(self,axis): + """TSL color of inverse pole figure for given axis. Not for hex or triclinic lattices""" + eq = self.equivalent_vec pole = eq.rotation @ np.broadcast_to(axis/np.linalg.norm(axis),eq.rotation.shape+(3,)) in_SST, color = self.lattice.symmetry.in_SST(pole,color=True) diff --git a/python/tests/test_ori_vec.py b/python/tests/test_ori_vec.py index a13ad3a03..daae6c197 100644 --- a/python/tests/test_ori_vec.py +++ b/python/tests/test_ori_vec.py @@ -5,32 +5,36 @@ from damask import Rotation from damask import Orientation from damask import Lattice -rot0= Rotation.from_random() -rot1= Rotation.from_random() -rot2= Rotation.from_random() -rot3= Rotation.from_random() +rot0= Rotation.from_random() ; +rot1= Rotation.from_random() ; +rot2= Rotation.from_random() ; +rot3= Rotation.from_random() ; + +#disorientation + +#fromaverage +#average class TestOrientation_vec: - @pytest.mark.xfail + #@pytest.mark.xfail @pytest.mark.parametrize('lattice',Lattice.lattices) - def test_equivalentOrientations_vec(self,lattice): + def test_equivalent_vec(self,lattice): ori0=Orientation(rot0,lattice) ori1=Orientation(rot1,lattice) ori2=Orientation(rot2,lattice) ori3=Orientation(rot3,lattice) quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),rot2.as_quaternion(),rot3.as_quaternion()]) - rot_vec=Rotation.from_quaternion(quat) - ori_vec=Orientation(rot_vec,lattice) + ori_vec=Orientation(quat,lattice) for s in range(len(ori_vec.lattice.symmetry.symmetryOperations())): - assert all(ori_vec.equivalent_vec()[s,0].rotation.as_Eulers() == \ + assert all(ori_vec.equivalent_vec.rotation.as_Eulers()[s,0] == \ ori0.equivalentOrientations()[s].rotation.as_Eulers()) - assert all(ori_vec.equivalent_vec()[s,1].rotation.as_quaternion() == \ + assert all(ori_vec.equivalent_vec.rotation.as_quaternion()[s,1] == \ ori1.equivalentOrientations()[s].rotation.as_quaternion()) - assert all(ori_vec.equivalent_vec()[s,2].rotation.as_Rodrigues() == \ + assert all(ori_vec.equivalent_vec.rotation.as_Rodrigues()[s,2] == \ ori2.equivalentOrientations()[s].rotation.as_Rodrigues()) - assert all(ori_vec.equivalent_vec()[s,3].rotation.as_cubochoric() == \ + assert all(ori_vec.equivalent_vec.rotation.as_cubochoric()[s,3] == \ ori3.equivalentOrientations()[s].rotation.as_cubochoric()) @pytest.mark.parametrize('lattice',Lattice.lattices) @@ -39,14 +43,11 @@ class TestOrientation_vec: ori1=Orientation(rot1,lattice) ori2=Orientation(rot2,lattice) ori3=Orientation(rot3,lattice) - #ensure 1 of them is in FZ - ori4=ori0.reduced() - rot4=ori4.rotation + ori4=ori0.reduced() ; rot4=ori4.rotation #ensure 1 of them is in FZ quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),\ rot2.as_quaternion(),rot3.as_quaternion(), rot4.as_quaternion()]) - rot_vec=Rotation.from_quaternion(quat) - ori_vec=Orientation(rot_vec,lattice) + ori_vec=Orientation(quat,lattice) assert ori_vec.inFZ_vec()[0] == ori0.inFZ() assert ori_vec.inFZ_vec()[1] == ori1.inFZ() @@ -64,16 +65,52 @@ class TestOrientation_vec: ori3=Orientation(rot3,lattice) quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),rot2.as_quaternion(),rot3.as_quaternion()]) - rot_vec=Rotation.from_quaternion(quat) - ori_vec=Orientation(rot_vec,lattice) + ori_vec=Orientation(quat,lattice) + for s in range(len(ori1.lattice.relationOperations(model)['rotations'])): - assert all(ori_vec.relatedOrientations_vec(model)[s,0].rotation.as_Eulers() == \ + assert all(ori_vec.relatedOrientations_vec(model).rotation.as_Eulers()[s,0] == \ ori0.relatedOrientations(model)[s].rotation.as_Eulers()) - assert all(ori_vec.relatedOrientations_vec(model)[s,1].rotation.as_quaternion() == \ + assert all(ori_vec.relatedOrientations_vec(model).rotation.as_quaternion()[s,1] == \ ori1.relatedOrientations(model)[s].rotation.as_quaternion()) - assert all(ori_vec.relatedOrientations_vec(model)[s,2].rotation.as_Rodrigues() == \ + assert all(ori_vec.relatedOrientations_vec(model).rotation.as_Rodrigues()[s,2] == \ ori2.relatedOrientations(model)[s].rotation.as_Rodrigues()) - assert all(ori_vec.relatedOrientations_vec(model)[s,3].rotation.as_cubochoric() == \ + assert all(ori_vec.relatedOrientations_vec(model).rotation.as_cubochoric()[s,3] == \ ori3.relatedOrientations(model)[s].rotation.as_cubochoric()) + @pytest.mark.parametrize('lattice',Lattice.lattices) + def test_reduced_vec(self,lattice): + ori0=Orientation(rot0,lattice) + ori1=Orientation(rot1,lattice) + ori2=Orientation(rot2,lattice) + ori3=Orientation(rot3,lattice) + #ensure 1 of them is in FZ + ori4=ori0.reduced() + rot4=ori4.rotation + + quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),\ + rot2.as_quaternion(),rot3.as_quaternion(), rot4.as_quaternion()]) + ori_vec=Orientation(quat,lattice) + + assert all(ori_vec.reduced_vec.rotation.as_Eulers()[0] == ori0.reduced().rotation.as_Eulers() ) + assert all(ori_vec.reduced_vec.rotation.as_quaternion()[1] == ori1.reduced().rotation.as_quaternion() ) + assert all(ori_vec.reduced_vec.rotation.as_Rodrigues()[2] == ori2.reduced().rotation.as_Rodrigues() ) + assert all(ori_vec.reduced_vec.rotation.as_cubochoric()[3] == ori3.reduced().rotation.as_cubochoric() ) + assert all(ori_vec.reduced_vec.rotation.as_axis_angle()[4] == ori4.reduced().rotation.as_axis_angle() ) + + + @pytest.mark.parametrize('lattice',['bcc','fcc','bct']) + def test_IPFcolor_vec(self,lattice): + ori0=Orientation(rot0,lattice) + ori1=Orientation(rot1,lattice) + ori2=Orientation(rot2,lattice) + ori3=Orientation(rot3,lattice) + + quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),\ + rot2.as_quaternion(),rot3.as_quaternion()]) + ori_vec=Orientation(quat,lattice) + + assert np.allclose( ori_vec.IPFcolor_vec(np.array([0,0,1]))[0],ori0.IPFcolor(np.array([0,0,1]))) + assert np.allclose( ori_vec.IPFcolor_vec(np.array([0,2,1]))[1],ori1.IPFcolor(np.array([0,2,1]))) + assert np.allclose( ori_vec.IPFcolor_vec(np.array([0,3,1]))[2],ori2.IPFcolor(np.array([0,3,1]))) + assert np.allclose( ori_vec.IPFcolor_vec(np.array([4,0,1]))[3],ori3.IPFcolor(np.array([4,0,1]))) From 8484d2e6ccb0a6f85fbb1049010d66f8025f21d2 Mon Sep 17 00:00:00 2001 From: "f.basile" Date: Sun, 28 Jun 2020 19:05:10 +0200 Subject: [PATCH 38/78] fix github stuff --- python/damask/_orientation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index e36713ee9..c0952c5fd 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -145,7 +145,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation quat=np.empty( [r , 4]) for rot in range(r): for sym in range(equi.shape[0]): - if self.lattice.symmetry.inFZ(equi.as_Rodrigues(vector=True)[sym,rot]) == True: + if self.lattice.symmetry.inFZ(equi.as_Rodrigues(vector=True)[sym,rot]) is True: quat[rot]=equi.as_quaternion()[sym,rot] return self.__class__(quat,self.lattice) @@ -187,7 +187,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation def IPFcolor_vec(self,axis): - """TSL color of inverse pole figure for given axis. Not for hex or triclinic lattices""" + """TSL color of inverse pole figure for given axis. Not for hex or triclinic lattices.""" eq = self.equivalent_vec pole = eq.rotation @ np.broadcast_to(axis/np.linalg.norm(axis),eq.rotation.shape+(3,)) in_SST, color = self.lattice.symmetry.in_SST(pole,color=True) From c7eb56a63d10ff11d7f9621fa0cb028e2fafe70c Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 28 Jun 2020 19:25:58 +0200 Subject: [PATCH 39/78] single precision reference files --- python/tests/reference/VTK/polyData.vtp | 6 +++--- python/tests/reference/VTK/rectilinearGrid.vtr | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/python/tests/reference/VTK/polyData.vtp b/python/tests/reference/VTK/polyData.vtp index e93e90f58..6ed05f67f 100644 --- a/python/tests/reference/VTK/polyData.vtp +++ b/python/tests/reference/VTK/polyData.vtp @@ -3,11 +3,11 @@ - - AQAAAACAAADwAAAAagAAAA==eF5jYMAGPuyXOV4IRHvsIfQZe8u+xxZ9j1/sh/Eh9B37IjDj4f5QMLhqD6Gf2odB+Pth6iD0G3sFiLn7ofqg+j/CxOH6IfRX+xCouRYQ+6H0D/sCqH6YuRD6D1wd1B9QmsEBxgcAJsNfhw== + + AQAAAACAAAB4AAAAVgAAAA==eF5jYICBhv2WfY9tLfuS7Ypk3PeDaCDf7okF3/7Vq1bZrV6lZQ+k94HEgHL2QHovUM7+iUUfiG0LlQdhkH77Ipnj9iB5qFp7kBjQDiBmcADRANsaLXM= - 0.7453559925 + 0.74535601471 2.4494897428 diff --git a/python/tests/reference/VTK/rectilinearGrid.vtr b/python/tests/reference/VTK/rectilinearGrid.vtr index 3e965bd2e..8d39f6e16 100644 --- a/python/tests/reference/VTK/rectilinearGrid.vtr +++ b/python/tests/reference/VTK/rectilinearGrid.vtr @@ -3,27 +3,27 @@ - - AQAAAACAAACAHwAAywMAAA==eF51mTGKVUEQRX88izByB8byY0MDlyG4CTfgFswMDWQigw8/Ejr40E7woGkYXqLJW4LDb8qmz71VDDjvWMGhijvd0KeTr8dXn/++f/x59rwIf3j6+untw1PS34S/udez8KgP97r+///w8bwIDx/f34SHD3nU4DXxIS/CVx/2N+GrTxWfUV18PC/C132xvwlf9zV51PDck/mQF+HrfNjfhK/z2cXn273+iI/nRXj4+P4mPHzI1zqSfZEX4eu+2N+Er/uanPXl9buXn+9n5n3lRTjzvvY34cx78Pgee7ye6eN5Ec6804ecefc+NfEhL8KZd+9TE58qPqO6+HhehDPv9CFn3v189mQ+5EU48+7ns4sPefhE7ujjeRHOvNOHnHmnz6gj2Rd5Ec6804ecefc+kbtLkvcLfCb3eb/AZ3Kf90uS9+njOfN+SfI+fch93ulTEx9y5p0+7Gfe6VPFZ1QXH8+Zd+6L/cw799XFZ3ju4uM58875sJ9553x28VlzN308Z94vSd6nD7nPO/d1iI/nzDv3xX7mnfs6Ep/Tafvx8eXnl+R95UU48772N+HMe/D4Hnu8nunjeRHOvNOHnHn3PjXxIS/CmXfvUxOfKj6juvh4XoQz7/QhZ979fPZkPuRFOPPu57OLD3n4RO7o43kRzrzTh5x5p8+oI9kXeRHOvNOHnHn3PnHO3iTvK+f5fkvO9xt8Jmfeg8f32GOcs9PHc57vN8k7fciZd+9TEx9ynu/0YT/Pd/pU8RnVxcdznu/cF/t5vnNfXXyG5y4+nvN853zYz/Od89nFZz1np4/nPN9vknf6kDPv9Bl1iI/nPN+5L/bzfOe+jsTndLr/Gdh+S95XXoQz72t/E868B4/vscfrmT6eF+HMO33ImXfvUxMf8iKcefc+NfGp4jOqi4/nRTjzTh9y5t3PZ0/mQ16EM+9+Prv4kIdP5I4+nhfhzDt9yJl3+ow6kn2RF+HMO33ImXfvE/fqTfK+ct7nt+Q+v8FncuY9eHyPPca9evp4zvv8JnmnDznz7n1q4kPO+zx92M/7PH2q+Izq4uM57/PcF/t5n+e+uvgMz118POd9nvNhP+/znM8uPpE7+njO+/wmeacPOfNOn1GH+HjO+zz3xX7e57mvI/GJ6pL3lfM9rkve1/4mnHkPHr+NPca72PTxnO9xXfK+9jfhzLv3qYkPOd/j6MP+Jpx5p8/6zX2R8z2O+2J/E868r//yPY7zIed7HOfD/iaceadP5I4+nvM9rkve6UPOvNNn1CE+nvM9jvtifxPOvAf/B5cwXsg= + + AQAAAACAAADADwAAFQMAAA==eF5Vl7GtFEEQBTeG8wgAhxxux8bEOIsYwDu/M0A6D0JofMAmjU0BF5+d7anXjzP+L6NV4l3pj8S29efL77/35ucO//nwS3zeiL99fTM2+3zPd/u6uTc/d3h67EY8PXB50jxpnjRPmifNk/Kcn4Gn+do18NiNeO3StvPfJk/ztUseuxGvXfI8Hg95mp87PD12I54euD5hu0IeuHbpRly74r9mb9+/7nQvru6T6b5uxHSfPH/PdniaqzseuxHTvT1pnjRPmifNk+ZJec7PsF3Ddg3bxY2Y7rZLnubqbrvkgemOZ7bD01zd8diNmO69K2xXyAPTvXeF7QrzzHa3vbtPpvtt7+7Xjbi73/b1/ex4muleHrsRd3c8aZ40T5onzZPmSXm2q512Dds1bBc34u6uXfI001275IG7e3mqXXma6V4euxF3d3aF7Qp54O7OrrBdYZ5t+/npo7oXV/fJdF83YrpPXt/Pjqe5uuOxGzHd25PmSfOkedI8aZ6U5/wM2zVs17Bd3Ijpbrvkaa7utksemO54Zjs8zdUdj92I6d67wnaFPDDde1fYrjDP9Vare7Heeft7f6n7ZHvn7e/9pe54YHvn1R0PXJ40T5onzZPmSfOkPFu91epuu4bt4kZs77z9vWuXPLC98+puu+RZb7W644HtnVd3PHDNCtsV8sD2zqt77wrzbNvn44e6F1f3yXRfN2K6T17fz46nubrjsRsx3duT5knzpHnSPGmelOf8DNs1bNewXdyI6W675Gmu7rZLHpjueGY7PM3VHY/diOneu8J2hTww3XtX2K4wz3yrD3Uv5p0/7J0/1H1yv/OHuuNp5p0/7J0/1B0PXJ40T5onzZPmSfOkPNv1VmvXsF3DdnEj7ndeu+Rp5p3XLnngfucPdcfTzDt/2Dt/qDseuGaF7Qp54H7n2RW2K8xT3xHdi5/67ui+bsR0nzx/zHZ4mqs7HrsR0709aZ40T5onzZPmSXnWb3YNPDDd8cB0t13yNFd32yUPTHc8sx2eZv0/btAdD0z33hW2K+SB6d67wnYV/wMyiXC6 0 - 1.268857754 + 1.2688577653 - - AQAAAACAAACwEwAAZQIAAA==eF511r2KFEEYRmHjjY2N9Ao0lQ79yQy8jBVjc29gL0Ezhc1maDBfU0FB3NVgNyqQuged1leZ56sqBpo5HRx4+wS13nn989l6vjzfzm45u/vk9+/NcvL17cuHJx8Lf3D/cD4XfvPq9vmj68vCH19vLwpf/3pvbedT8crjlccrj1ce77vtXBavPF55vPJ45fG+3/hN8crjlccrj1d+vHOb7NyKV368cyte+XrUVZ901YtXftxVL175Ss/f96dX+9MPpedwew6353B7DrdnvXJ71iu3Z73pTa/cnvXK7VlvetMrt2e9cnse79wmO7fildvzeOdWvOlt3FUvXrk9j7vqE+9uWQ/46qL0HG7P4fYcbs/h9qxXbs965fasN73plduzXrk9601veuX2rFduz+Od22TnVrxyex7v3Io3vY276sUrt+dxV33i3f37/vYcbs/h9hxuz+H2rFduz3rl9pynPYfbs165PeuV27NeuT3rlduz3j//22TnVrxyew6353B7DrdnvXJ71iu353uHa8jZl9JzuD2H23O4PYfbs165PeuV27Pe9KZXbs965fasN73plduzXrk9j3duk51b8crtebxzK970Nu6qF6/cnsdd9Yl3tzzdLtbfSs/h9hxuz+H2HG7PeuX2rFduz3rTm165PeuV27Pe9KZXbs965fY83rlNdm7FK7fn8c6teNPbuKtevHJ7HnfVJ97d8uJwDdn/KD2H23O4PYfbc7g965Xbs165PetNb3rl9qxXbs9605teuT3rldvzeOc22bkVr9yexzu34k1v46568crtedzVf/4LDINsdg== + + AQAAAACAAADYCQAA8QEAAA==eF5N0CGOFlEQAOHVK0nmBnAFgnn/PLsSsZd4CSQIPDfYZN2iUbMePI4DEMR/BbgDNJ2ve1yZSir18P3jeD6O8eruxfj99s0Ff356Kh63v4o/jNsdP/xzb24+Xbg4XBwuDheHe3//s1wcLg4Xh4vT3fZ2k9NNTjc53eRsnuXibJ7l4mye5T4fq1ycr1a5OF+tk3uMb++u9TnY52Cfg30O9pmLfeZin7nxjYt95mKf2932dpN9bjfZ526e5WKfu3mWi33uV6tc7HO/Wif3GO+vry8+B/sc7HOwz8E+c7HPXOwzN75xsc9c7HO7295uss/tJvvczbNc7HM3z3Kxz/1qlYt97lfr5B7/f/kc7HOwz8E+B/vMxT5zsc/c+MbFPnOxz+1ue7vJPreb7HM3z3Kxz908y8U+96tVLva5X62Te4y7xy/1OdjnYJ+DfQ72mYt95mKfufGNi33mYp/b3fZ2k31uN9nnbp7lYp+7eZaLfe5Xq1zsc79aJ/cYjy9/1Odgn4N9DvY52Gcu9pmLfebGNy72mYt9bnfb2032ud1kn7t5lot97uZZLva5X61ysc/9ap3cY1y//qnPwT4H+xzsc7DPXOwzF/vMjW9c7DMX+9zutreb7HO7yT538ywX+9zNs1zsc79a5WKf+1XyX6K10A4= - 0.10871961483 + 0.10871961652 - 1.1607924027 + 1.1607924233 From a99f0164380a45699be7b8af6d9b3e1448560c5e Mon Sep 17 00:00:00 2001 From: "f.basile" Date: Sun, 28 Jun 2020 19:29:52 +0200 Subject: [PATCH 40/78] problem with if value is true / if value == True --- python/damask/_orientation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index c0952c5fd..adfa839d1 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -145,7 +145,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation quat=np.empty( [r , 4]) for rot in range(r): for sym in range(equi.shape[0]): - if self.lattice.symmetry.inFZ(equi.as_Rodrigues(vector=True)[sym,rot]) is True: + if self.lattice.symmetry.inFZ(equi.as_Rodrigues(vector=True)[sym,rot]) == True: quat[rot]=equi.as_quaternion()[sym,rot] return self.__class__(quat,self.lattice) From a657125840cb63c0f8e7614f04332dc5d7515c11 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 28 Jun 2020 19:30:23 +0200 Subject: [PATCH 41/78] standard version string --- python/tests/reference/Colormap/binary.json | 2 +- python/tests/reference/Colormap/binary.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/tests/reference/Colormap/binary.json b/python/tests/reference/Colormap/binary.json index d56f1232e..c71d3649b 100644 --- a/python/tests/reference/Colormap/binary.json +++ b/python/tests/reference/Colormap/binary.json @@ -1,6 +1,6 @@ [ { - "Creator": "damask.Colormap 99.99.99-9999-pytest", + "Creator": "damask.Colormap v99.99.99-9999-pytest", "ColorSpace": "RGB", "Name": "binary", "DefaultMap": true, diff --git a/python/tests/reference/Colormap/binary.txt b/python/tests/reference/Colormap/binary.txt index 65e5e3d58..817350aee 100644 --- a/python/tests/reference/Colormap/binary.txt +++ b/python/tests/reference/Colormap/binary.txt @@ -1,4 +1,4 @@ -# Creator: damask.Colormap 99.99.99-9999-pytest +# Creator: damask.Colormap v99.99.99-9999-pytest R G B alpha 1.0 1.0 1.0 1.0 0.996078431372549 0.996078431372549 0.996078431372549 1.0 From 4875191ffdda6bca74505d1aefb7e91bfb603ef8 Mon Sep 17 00:00:00 2001 From: "f.basile" Date: Sun, 28 Jun 2020 19:32:22 +0200 Subject: [PATCH 42/78] change if statement so github doesnt complain --- python/damask/_orientation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index adfa839d1..cc26acafc 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -145,8 +145,9 @@ class Orientation: # ToDo: make subclass of lattice and Rotation quat=np.empty( [r , 4]) for rot in range(r): for sym in range(equi.shape[0]): - if self.lattice.symmetry.inFZ(equi.as_Rodrigues(vector=True)[sym,rot]) == True: + if self.lattice.symmetry.inFZ(equi.as_Rodrigues(vector=True)[sym,rot]): quat[rot]=equi.as_quaternion()[sym,rot] + break return self.__class__(quat,self.lattice) From 2d0c680dafb4a1f131e2c885f86c2a91ab00eea3 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 29 Jun 2020 07:30:42 +0200 Subject: [PATCH 43/78] useful information --- python/damask/_colormap.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index f354af0a3..d60f7f6c6 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -127,7 +127,19 @@ class Colormap(mpl.colors.ListedColormap): @staticmethod def list_predefined(): - """List predefined colormaps by category.""" + """ + List predefined colormaps by category. + + References + ---------- + .. [1] DAMASK colormap theory + https://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf + .. [2] DAMASK colormaps first use + https://doi.org/10.1016/j.ijplas.2012.09.012 + .. [3] Matplotlib colormaps overview + https://matplotlib.org/tutorials/colors/colormaps.html + + """ print('DAMASK colormaps') print(' '+', '.join(Colormap._predefined_DAMASK.keys())) for cat in Colormap._predefined_mpl: From d06daec4cb5578f744d83fe91d7e31933947f6f3 Mon Sep 17 00:00:00 2001 From: "f.basile" Date: Mon, 29 Jun 2020 18:25:45 +0200 Subject: [PATCH 44/78] reducec vectorized is improved --- python/damask/_orientation.py | 28 ++++++++++++++++++---------- python/tests/test_ori_vec.py | 8 ++++---- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index cc26acafc..2b6f1f701 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -140,15 +140,23 @@ class Orientation: # ToDo: make subclass of lattice and Rotation @property def reduced_vec(self): """Transform orientation to fall into fundamental zone according to symmetry.""" - equi=self.equivalent_vec.rotation #24 x rot x 3(rodrigues vector) - r= 1 if not self.rotation.shape else equi.shape[1] #number of rotations - quat=np.empty( [r , 4]) - for rot in range(r): - for sym in range(equi.shape[0]): - if self.lattice.symmetry.inFZ(equi.as_Rodrigues(vector=True)[sym,rot]): - quat[rot]=equi.as_quaternion()[sym,rot] - break - return self.__class__(quat,self.lattice) + equi= self.equivalent_vec.rotation #equivalent orientations + r= 1 if not self.rotation.shape else equi.shape[1] #number of rotations + num_equi=equi.shape[0] #number of equivalente orientations + quat= np.reshape( equi.as_quaternion(), (r*num_equi,4) ,order='F') #equivalents are listed in intiuitive order + boolean=Orientation(quat, self.lattice).inFZ_vec() #check which ones are in FZ + if sum(boolean) == r: + return self.__class__(quat[boolean],self.lattice) + + else: + print('More than 1 equivalent orientation has been found for an orientation') + index=np.empty(r, dtype=int) + for l,h in enumerate(range(0,r*num_equi, num_equi)): + index[l]=np.where(boolean[h:h+num_equi])[0][0] + (l*num_equi) #get first index that is true then go check to next orientation + + return self.__class__(quat[index],self.lattice) + + @@ -187,7 +195,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation return color - def IPFcolor_vec(self,axis): + def IPF_color(self,axis): """TSL color of inverse pole figure for given axis. Not for hex or triclinic lattices.""" eq = self.equivalent_vec pole = eq.rotation @ np.broadcast_to(axis/np.linalg.norm(axis),eq.rotation.shape+(3,)) diff --git a/python/tests/test_ori_vec.py b/python/tests/test_ori_vec.py index daae6c197..6bbe37f63 100644 --- a/python/tests/test_ori_vec.py +++ b/python/tests/test_ori_vec.py @@ -110,7 +110,7 @@ class TestOrientation_vec: rot2.as_quaternion(),rot3.as_quaternion()]) ori_vec=Orientation(quat,lattice) - assert np.allclose( ori_vec.IPFcolor_vec(np.array([0,0,1]))[0],ori0.IPFcolor(np.array([0,0,1]))) - assert np.allclose( ori_vec.IPFcolor_vec(np.array([0,2,1]))[1],ori1.IPFcolor(np.array([0,2,1]))) - assert np.allclose( ori_vec.IPFcolor_vec(np.array([0,3,1]))[2],ori2.IPFcolor(np.array([0,3,1]))) - assert np.allclose( ori_vec.IPFcolor_vec(np.array([4,0,1]))[3],ori3.IPFcolor(np.array([4,0,1]))) + assert np.allclose( ori_vec.IPF_color(np.array([0,0,1]))[0],ori0.IPFcolor(np.array([0,0,1]))) + assert np.allclose( ori_vec.IPF_color(np.array([0,2,1]))[1],ori1.IPFcolor(np.array([0,2,1]))) + assert np.allclose( ori_vec.IPF_color(np.array([0,3,1]))[2],ori2.IPFcolor(np.array([0,3,1]))) + assert np.allclose( ori_vec.IPF_color(np.array([4,0,1]))[3],ori3.IPFcolor(np.array([4,0,1]))) From 97ca1b1a9bc6b58a3994052caf85e72ea901fd70 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Mon, 29 Jun 2020 21:50:33 -0400 Subject: [PATCH 45/78] try wxPython before tkinter, recent macOS otherwise fails --- python/damask/_environment.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/python/damask/_environment.py b/python/damask/_environment.py index 7d12050aa..73fe6c035 100644 --- a/python/damask/_environment.py +++ b/python/damask/_environment.py @@ -1,19 +1,25 @@ import os from pathlib import Path -import tkinter class Environment: def __init__(self): """Read and provide values of DAMASK configuration.""" + self.screen_width = 1024 + self.screen_height = 768 try: - tk = tkinter.Tk() - self.screen_width = tk.winfo_screenwidth() - self.screen_height = tk.winfo_screenheight() - tk.destroy() - except tkinter.TclError: - self.screen_width = 1024 - self.screen_height = 768 + import wx + app = wx.App(False) + self.screenwidth, self.screenheight = wx.GetDisplaySize() + except ImportError: + try: + import tkinter + tk = tkinter.Tk() + self.screen_width = tk.winfo_screenwidth() + self.screen_height = tk.winfo_screenheight() + tk.destroy() + except: + pass @property def options(self): From 39aac76859a39bcbf11e5a626cdd01c58c0fb155 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Mon, 29 Jun 2020 21:58:41 -0400 Subject: [PATCH 46/78] __add__, from_range, fixed missing np.array(colors), show peppered... --- python/damask/_colormap.py | 57 +++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index d60f7f6c6..442ee1b63 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -19,11 +19,15 @@ _ref_white = np.array([.95047, 1.00000, 1.08883]) class Colormap(mpl.colors.ListedColormap): + def __add__(self,other): + """Concatenate colormaps.""" + return Colormap(np.vstack((self.colors,other.colors)), + f'{self.name}+{other.name}') @staticmethod - def from_bounds(low,high,name='DAMASK colormap',N=256,model='rgb'): + def from_range(low,high,name='DAMASK colormap',N=256,model='rgb'): """ - Create a perceptually uniform colormap from given bounds. + Create a perceptually uniform colormap between given (inclusive) bounds. Colors are internally stored as R(ed) G(green) B(lue) values. The colormap can be used in matplotlib/seaborn or exported to @@ -50,35 +54,34 @@ class Colormap(mpl.colors.ListedColormap): - 'msh': Msh (for perceptual uniform interpolation). """ - low_,high_ = map(np.array,[low,high]) - low_high = np.vstack((low_,high)) + low_high = np.vstack((low,high)) if model.lower() == 'rgb': if np.any(low_high<0) or np.any(low_high>1): - raise ValueError(f'RGB color out of range {low} {high}.') + raise ValueError(f'RGB color {low} | {high} are out of range.') - low_,high_ = map(Colormap._rgb2msh,[low_,high_]) + low_,high_ = map(Colormap._rgb2msh,low_high) elif model.lower() == 'hsv': if np.any(low_high<0) or np.any(low_high[:,1:3]>1) or np.any(low_high[:,0]>360): - raise ValueError(f'HSV color out of range {low} {high}.') + raise ValueError(f'HSV color {low} | {high} are out of range.') - low_,high_ = map(Colormap._hsv2msh,[low_,high_]) + low_,high_ = map(Colormap._hsv2msh,low_high) elif model.lower() == 'hsl': if np.any(low_high<0) or np.any(low_high[:,1:3]>1) or np.any(low_high[:,0]>360): - raise ValueError(f'HSL color out of range {low} {high}.') + raise ValueError(f'HSL color {low} | {high} are out of range.') - low_,high_ = map(Colormap._hsl2msh,[low_,high_]) + low_,high_ = map(Colormap._hsl2msh,low_high) elif model.lower() == 'xyz': - low_,high_ = map(Colormap._xyz2msh,[low_,high_]) + low_,high_ = map(Colormap._xyz2msh,low_high) elif model.lower() == 'lab': if np.any(low_high[:,0]<0): - raise ValueError(f'CIE Lab color out of range {low} {high}.') + raise ValueError(f'CIE Lab color {low} | {high} are out of range.') - low_,high_ = map(Colormap._lab2msh,[low_,high_]) + low_,high_ = map(Colormap._lab2msh,low_high) elif model.lower() == 'msh': pass @@ -122,7 +125,7 @@ class Colormap(mpl.colors.ListedColormap): # DAMASK presets definition = Colormap._predefined_DAMASK[name] - return Colormap.from_bounds(definition['low'],definition['high'],name,N) + return Colormap.from_range(definition['low'],definition['high'],name,N) @staticmethod @@ -147,24 +150,27 @@ class Colormap(mpl.colors.ListedColormap): print(' '+', '.join(cat[1])) - def show(self): - """Show colormap in window.""" - fig, ax = plt.subplots(figsize=(10,1)) - ax.set_axis_off() - im = ax.imshow(np.broadcast_to(np.linspace(0,1,640).reshape(1,-1),(64,640)),cmap=self) # noqa - fig.canvas.set_window_title(self.name) + def show(self,aspect=10,vertical=False): + """Show colormap as matplotlib figure.""" + fig = plt.figure(figsize=(5/aspect,5) if vertical else (5,5/aspect)) + ax1 = fig.add_axes([0, 0, 1, 1]) + ax1.set_axis_off() + ax1.imshow(np.linspace(1 if vertical else 0, + 0 if vertical else 1, + self.N).reshape((-1,1) if vertical else (1,-1)), + aspect='auto', cmap=self, interpolation='nearest') plt.show() def reversed(self,name=None): """ - Make a reversed instance of the Colormap. + Make a reversed instance of the colormap. Parameters ---------- name : str, optional - The name for the reversed colormap. If it's None - the name will be the name of the parent colormap + "_r". + The name for the reversed colormap. + A name of None will be replaced by the name of the parent colormap + "_r". Returns ------- @@ -173,7 +179,7 @@ class Colormap(mpl.colors.ListedColormap): """ rev = super(Colormap,self).reversed(name) - return Colormap(rev.colors,rev.name) + return Colormap(np.array(rev.colors),rev.name[:-4] if rev.name.endswith('_r_r') else rev.name) def to_file(self,fname=None,format='ParaView'): @@ -236,8 +242,7 @@ class Colormap(mpl.colors.ListedColormap): @staticmethod def _export_ASCII(colormap,fhandle=None): """Write colormap to ASCII table.""" - labels = {'R':(1,),'G':(1,),'B':(1,)} - if colormap.colors.shape[1] == 4: labels['alpha']=(1,) + labels = {'RGBA':4} if colormap.colors.shape[1] == 4 else {'RGB': 3} t = Table(colormap.colors,labels,f'Creator: damask.Colormap v{damask.version}') if fhandle is None: From d3b3d628b290bcd33211146abf4657dbf089496d Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Mon, 29 Jun 2020 22:06:40 -0400 Subject: [PATCH 47/78] PEP conformity... --- python/damask/_environment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/damask/_environment.py b/python/damask/_environment.py index 73fe6c035..29c0f45c7 100644 --- a/python/damask/_environment.py +++ b/python/damask/_environment.py @@ -9,7 +9,7 @@ class Environment: self.screen_height = 768 try: import wx - app = wx.App(False) + _ = wx.App(False) # noqa self.screenwidth, self.screenheight = wx.GetDisplaySize() except ImportError: try: @@ -18,7 +18,7 @@ class Environment: self.screen_width = tk.winfo_screenwidth() self.screen_height = tk.winfo_screenheight() tk.destroy() - except: + except Exception as e: pass @property From decbe8074a8eef7c3a86217874ddd8f7c39f68e9 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Mon, 29 Jun 2020 22:22:21 -0400 Subject: [PATCH 48/78] not using unassigned variable --- python/damask/_colormap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index 442ee1b63..ebfd241d0 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -89,7 +89,7 @@ class Colormap(mpl.colors.ListedColormap): else: raise ValueError(f'Invalid color model: {model}.') - msh = map(functools.partial(Colormap._interpolate_msh,low=low_,high=high_),np.linspace(0,1,N)) + msh = map(functools.partial(Colormap._interpolate_msh,low=low_high[0],high=low_high[1]),np.linspace(0,1,N)) rgb = np.array(list(map(Colormap._msh2rgb,msh))) return Colormap(rgb,name=name) From 8dc87023d11f27818cfe97dbc737ff6cc3d44fff Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Mon, 29 Jun 2020 22:23:24 -0400 Subject: [PATCH 49/78] test from_range; update ASCII colormap labels (i_RGBA) --- python/tests/reference/Colormap/binary.txt | 2 +- python/tests/test_Colormap.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/tests/reference/Colormap/binary.txt b/python/tests/reference/Colormap/binary.txt index 817350aee..976c0202a 100644 --- a/python/tests/reference/Colormap/binary.txt +++ b/python/tests/reference/Colormap/binary.txt @@ -1,5 +1,5 @@ # Creator: damask.Colormap v99.99.99-9999-pytest -R G B alpha +1_RGBA 2_RGBA 3_RGBA 4_RGBA 1.0 1.0 1.0 1.0 0.996078431372549 0.996078431372549 0.996078431372549 1.0 0.9921568627450981 0.9921568627450981 0.9921568627450981 1.0 diff --git a/python/tests/test_Colormap.py b/python/tests/test_Colormap.py index 8d3d51018..fcc2eca81 100644 --- a/python/tests/test_Colormap.py +++ b/python/tests/test_Colormap.py @@ -74,9 +74,9 @@ class TestColormap: @pytest.mark.parametrize('format',['ASCII','paraview','GOM','Gmsh']) @pytest.mark.parametrize('model',['rgb','hsv','hsl','xyz','lab','msh']) - def test_from_bounds(self,model,format,tmpdir): + def test_from_range(self,model,format,tmpdir): N = np.random.randint(2,256) - c = Colormap.from_bounds(np.random.rand(3),np.random.rand(3),model=model,N=N) + c = Colormap.from_range(np.random.rand(3),np.random.rand(3),model=model,N=N) c.to_file(tmpdir/'color_out',format=format) @pytest.mark.parametrize('format',['ASCII','paraview','GOM','Gmsh']) @@ -110,7 +110,7 @@ class TestColormap: @pytest.mark.parametrize('model',['rgb','hsv','hsl','lab','invalid']) def test_invalid_color(self,model): with pytest.raises(ValueError): - c = Colormap.from_bounds(-2.+np.random.rand(3),np.random.rand(3),N=10,model=model) # noqa + c = Colormap.from_range(-2.+np.random.rand(3),np.random.rand(3),N=10,model=model) # noqa def test_reversed(self): c_1 = Colormap.from_predefined('stress') From c8adfae0fa20baaff0f009be59530c13c45ae341 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 30 Jun 2020 07:16:49 +0200 Subject: [PATCH 50/78] bugfix: wrong variables used --- python/damask/_colormap.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index ebfd241d0..964f22d85 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -84,12 +84,12 @@ class Colormap(mpl.colors.ListedColormap): low_,high_ = map(Colormap._lab2msh,low_high) elif model.lower() == 'msh': - pass + low_,high_ = low_high[0],low_high[1] else: raise ValueError(f'Invalid color model: {model}.') - msh = map(functools.partial(Colormap._interpolate_msh,low=low_high[0],high=low_high[1]),np.linspace(0,1,N)) + msh = map(functools.partial(Colormap._interpolate_msh,low=low_,high=high_),np.linspace(0,1,N)) rgb = np.array(list(map(Colormap._msh2rgb,msh))) return Colormap(rgb,name=name) From b8b34080fed48d5ba4e95dc783b624da214fa7f4 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 30 Jun 2020 12:16:47 +0200 Subject: [PATCH 51/78] enable array like slicing/iteration --- python/damask/_rotation.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 694384de2..a8d8a7928 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -52,6 +52,8 @@ class Rotation: Unit quaternion that follows the conventions. Use .from_quaternion to perform a sanity check. """ + if quaternion.shape[-1] != 4: + raise ValueError('Not a quaternion') self.quaternion = quaternion.copy() @@ -60,6 +62,7 @@ class Rotation: return self.quaternion.shape[:-1] + # ToDo: Check difference __copy__ vs __deepcopy__ def __copy__(self): """Copy.""" return self.__class__(self.quaternion) @@ -70,7 +73,7 @@ class Rotation: def __repr__(self): """Orientation displayed as unit quaternion, rotation matrix, and Bunge-Euler angles.""" if self.quaternion.shape != (4,): - return str(self.quaternion) # ToDo: could be nicer ... + return 'Quaternions:\n'+str(self.quaternion) # ToDo: could be nicer ... return '\n'.join([ 'Quaternion: (real={:.3f}, imag=<{:+.3f}, {:+.3f}, {:+.3f}>)'.format(*(self.quaternion)), 'Matrix:\n{}'.format(self.as_matrix()), @@ -78,6 +81,20 @@ class Rotation: ]) + def __len__(self): + return 0 if self.shape == () else len(self.shape) + + + def __getitem__(self,item): + if isinstance(item,tuple) and len(item) >= len(self): + raise IndexError('Too many indices') + return self.__class__(self.quaternion[item]) + + + def __len__(self): + return 0 if self.shape == () else self.shape[0] + + def __matmul__(self, other): """ Rotation of vector, second or fourth order tensor, or rotation object. From ce7018164fd615e09095df5c49e75b53fb29c5cf Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 30 Jun 2020 12:55:08 +0200 Subject: [PATCH 52/78] WIP: more reasonable naming --- python/damask/_lattice.py | 75 ++++++++++++++++++------------------ python/tests/test_Lattice.py | 22 +++++------ 2 files changed, 49 insertions(+), 48 deletions(-) diff --git a/python/damask/_lattice.py b/python/damask/_lattice.py index 251821868..22f1de320 100644 --- a/python/damask/_lattice.py +++ b/python/damask/_lattice.py @@ -5,7 +5,7 @@ from . import Rotation class Symmetry: """ - Symmetry operations for lattice systems. + Symmetry-related operations for crystal systems. References ---------- @@ -13,34 +13,35 @@ class Symmetry: """ - lattices = [None,'orthorhombic','tetragonal','hexagonal','cubic'] + crystal_systems = [None,'orthorhombic','tetragonal','hexagonal','cubic'] - def __init__(self, symmetry = None): + def __init__(self, system = None): """ Symmetry Definition. Parameters ---------- - symmetry : str, optional - label of the crystal system + system : {None,'orthorhombic','tetragonal','hexagonal','cubic'}, optional + Name of the crystal system. Defaults to 'None'. """ - if symmetry is not None and symmetry.lower() not in Symmetry.lattices: - raise KeyError('Symmetry/crystal system "{}" is unknown'.format(symmetry)) + if system is not None and system.lower() not in self.crystal_systems: + raise KeyError(f'Crystal system "{system}" is unknown') - self.lattice = symmetry.lower() if isinstance(symmetry,str) else symmetry + self.system = system.lower() if isinstance(system,str) else system + self.lattice = self.system # for compatibility def __copy__(self): """Copy.""" - return self.__class__(self.lattice) + return self.__class__(self.system) copy = __copy__ def __repr__(self): """Readable string.""" - return '{}'.format(self.lattice) + return '{}'.format(self.system) def __eq__(self, other): @@ -53,7 +54,7 @@ class Symmetry: Symmetry to check for equality. """ - return self.lattice == other.lattice + return self.system == other.system def __neq__(self, other): """ @@ -77,13 +78,13 @@ class Symmetry: Symmetry to check for for order. """ - myOrder = Symmetry.lattices.index(self.lattice) - otherOrder = Symmetry.lattices.index(other.lattice) + myOrder = self.crystal_systems.index(self.system) + otherOrder = self.crystal_systems.index(other.system) return (myOrder > otherOrder) - (myOrder < otherOrder) def symmetryOperations(self,members=[]): """List (or single element) of symmetry operations as rotations.""" - if self.lattice == 'cubic': + if self.system == 'cubic': symQuats = [ [ 1.0, 0.0, 0.0, 0.0 ], [ 0.0, 1.0, 0.0, 0.0 ], @@ -110,7 +111,7 @@ class Symmetry: [-0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0, 0.0 ], [-0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0, 0.0 ], ] - elif self.lattice == 'hexagonal': + elif self.system == 'hexagonal': symQuats = [ [ 1.0, 0.0, 0.0, 0.0 ], [-0.5*np.sqrt(3), 0.0, 0.0, -0.5 ], @@ -125,7 +126,7 @@ class Symmetry: [ 0.0, -0.5, -0.5*np.sqrt(3), 0.0 ], [ 0.0, 0.5*np.sqrt(3), 0.5, 0.0 ], ] - elif self.lattice == 'tetragonal': + elif self.system == 'tetragonal': symQuats = [ [ 1.0, 0.0, 0.0, 0.0 ], [ 0.0, 1.0, 0.0, 0.0 ], @@ -136,7 +137,7 @@ class Symmetry: [ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], [-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], ] - elif self.lattice == 'orthorhombic': + elif self.system == 'orthorhombic': symQuats = [ [ 1.0,0.0,0.0,0.0 ], [ 0.0,1.0,0.0,0.0 ], @@ -160,7 +161,7 @@ class Symmetry: @property def symmetry_operations(self): """Symmetry operations as Rotations.""" - if self.lattice == 'cubic': + if self.system == 'cubic': symQuats = [ [ 1.0, 0.0, 0.0, 0.0 ], [ 0.0, 1.0, 0.0, 0.0 ], @@ -187,7 +188,7 @@ class Symmetry: [-0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0, 0.0 ], [-0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0, 0.0 ], ] - elif self.lattice == 'hexagonal': + elif self.system == 'hexagonal': symQuats = [ [ 1.0, 0.0, 0.0, 0.0 ], [-0.5*np.sqrt(3), 0.0, 0.0, -0.5 ], @@ -202,7 +203,7 @@ class Symmetry: [ 0.0, -0.5, -0.5*np.sqrt(3), 0.0 ], [ 0.0, 0.5*np.sqrt(3), 0.5, 0.0 ], ] - elif self.lattice == 'tetragonal': + elif self.system == 'tetragonal': symQuats = [ [ 1.0, 0.0, 0.0, 0.0 ], [ 0.0, 1.0, 0.0, 0.0 ], @@ -213,7 +214,7 @@ class Symmetry: [ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], [-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], ] - elif self.lattice == 'orthorhombic': + elif self.system == 'orthorhombic': symQuats = [ [ 1.0,0.0,0.0,0.0 ], [ 0.0,1.0,0.0,0.0 ], @@ -240,21 +241,21 @@ class Symmetry: Rabs = abs(rodrigues) - if self.lattice == 'cubic': + if self.system == 'cubic': return np.sqrt(2.0)-1.0 >= Rabs[0] \ and np.sqrt(2.0)-1.0 >= Rabs[1] \ and np.sqrt(2.0)-1.0 >= Rabs[2] \ and 1.0 >= Rabs[0] + Rabs[1] + Rabs[2] - elif self.lattice == 'hexagonal': + elif self.system == 'hexagonal': return 1.0 >= Rabs[0] and 1.0 >= Rabs[1] and 1.0 >= Rabs[2] \ and 2.0 >= np.sqrt(3)*Rabs[0] + Rabs[1] \ and 2.0 >= np.sqrt(3)*Rabs[1] + Rabs[0] \ and 2.0 >= np.sqrt(3) + Rabs[2] - elif self.lattice == 'tetragonal': + elif self.system == 'tetragonal': return 1.0 >= Rabs[0] and 1.0 >= Rabs[1] \ and np.sqrt(2.0) >= Rabs[0] + Rabs[1] \ and np.sqrt(2.0) >= Rabs[2] + 1.0 - elif self.lattice == 'orthorhombic': + elif self.system == 'orthorhombic': return 1.0 >= Rabs[0] and 1.0 >= Rabs[1] and 1.0 >= Rabs[2] else: return True @@ -275,13 +276,13 @@ class Symmetry: R = rodrigues epsilon = 0.0 - if self.lattice == 'cubic': + if self.system == 'cubic': return R[0] >= R[1]+epsilon and R[1] >= R[2]+epsilon and R[2] >= epsilon - elif self.lattice == 'hexagonal': + elif self.system == 'hexagonal': return R[0] >= np.sqrt(3)*(R[1]-epsilon) and R[1] >= epsilon and R[2] >= epsilon - elif self.lattice == 'tetragonal': + elif self.system == 'tetragonal': return R[0] >= R[1]-epsilon and R[1] >= epsilon and R[2] >= epsilon - elif self.lattice == 'orthorhombic': + elif self.system == 'orthorhombic': return R[0] >= epsilon and R[1] >= epsilon and R[2] >= epsilon else: return True @@ -313,7 +314,7 @@ class Symmetry: ... } """ - if self.lattice == 'cubic': + if self.system == 'cubic': basis = {'improper':np.array([ [-1. , 0. , 1. ], [ np.sqrt(2.) , -np.sqrt(2.) , 0. ], [ 0. , np.sqrt(3.) , 0. ] ]), @@ -321,7 +322,7 @@ class Symmetry: [-np.sqrt(2.) , np.sqrt(2.) , 0. ], [ np.sqrt(3.) , 0. , 0. ] ]), } - elif self.lattice == 'hexagonal': + elif self.system == 'hexagonal': basis = {'improper':np.array([ [ 0. , 0. , 1. ], [ 1. , -np.sqrt(3.) , 0. ], [ 0. , 2. , 0. ] ]), @@ -329,7 +330,7 @@ class Symmetry: [-1. , np.sqrt(3.) , 0. ], [ np.sqrt(3.) , -1. , 0. ] ]), } - elif self.lattice == 'tetragonal': + elif self.system == 'tetragonal': basis = {'improper':np.array([ [ 0. , 0. , 1. ], [ 1. , -1. , 0. ], [ 0. , np.sqrt(2.) , 0. ] ]), @@ -337,7 +338,7 @@ class Symmetry: [-1. , 1. , 0. ], [ np.sqrt(2.) , 0. , 0. ] ]), } - elif self.lattice == 'orthorhombic': + elif self.system == 'orthorhombic': basis = {'improper':np.array([ [ 0., 0., 1.], [ 1., 0., 0.], [ 0., 1., 0.] ]), @@ -401,7 +402,7 @@ class Symmetry: ... } """ - if self.lattice == 'cubic': + if self.system == 'cubic': basis = {'improper':np.array([ [-1. , 0. , 1. ], [ np.sqrt(2.) , -np.sqrt(2.) , 0. ], [ 0. , np.sqrt(3.) , 0. ] ]), @@ -409,7 +410,7 @@ class Symmetry: [-np.sqrt(2.) , np.sqrt(2.) , 0. ], [ np.sqrt(3.) , 0. , 0. ] ]), } - elif self.lattice == 'hexagonal': + elif self.system == 'hexagonal': basis = {'improper':np.array([ [ 0. , 0. , 1. ], [ 1. , -np.sqrt(3.) , 0. ], [ 0. , 2. , 0. ] ]), @@ -417,7 +418,7 @@ class Symmetry: [-1. , np.sqrt(3.) , 0. ], [ np.sqrt(3.) , -1. , 0. ] ]), } - elif self.lattice == 'tetragonal': + elif self.system == 'tetragonal': basis = {'improper':np.array([ [ 0. , 0. , 1. ], [ 1. , -1. , 0. ], [ 0. , np.sqrt(2.) , 0. ] ]), @@ -425,7 +426,7 @@ class Symmetry: [-1. , 1. , 0. ], [ np.sqrt(2.) , 0. , 0. ] ]), } - elif self.lattice == 'orthorhombic': + elif self.system == 'orthorhombic': basis = {'improper':np.array([ [ 0., 0., 1.], [ 1., 0., 0.], [ 0., 1., 0.] ]), diff --git a/python/tests/test_Lattice.py b/python/tests/test_Lattice.py index fb8ce8ea5..3d9c35706 100644 --- a/python/tests/test_Lattice.py +++ b/python/tests/test_Lattice.py @@ -13,26 +13,26 @@ class TestSymmetry: s = Symmetry(invalid_symmetry) # noqa def test_equal(self): - symmetry = random.choice(Symmetry.lattices) + symmetry = random.choice(Symmetry.crystal_systems) print(symmetry) assert Symmetry(symmetry) == Symmetry(symmetry) def test_not_equal(self): - symmetries = random.sample(Symmetry.lattices,k=2) + symmetries = random.sample(Symmetry.crystal_systems,k=2) assert Symmetry(symmetries[0]) != Symmetry(symmetries[1]) - @pytest.mark.parametrize('lattice',Symmetry.lattices) - def test_inFZ(self,lattice): - assert Symmetry(lattice).inFZ(np.zeros(3)) + @pytest.mark.parametrize('system',Symmetry.crystal_systems) + def test_inFZ(self,system): + assert Symmetry(system).inFZ(np.zeros(3)) - @pytest.mark.parametrize('lattice',Symmetry.lattices) - def test_inDisorientationSST(self,lattice): - assert Symmetry(lattice).inDisorientationSST(np.zeros(3)) + @pytest.mark.parametrize('system',Symmetry.crystal_systems) + def test_inDisorientationSST(self,system): + assert Symmetry(system).inDisorientationSST(np.zeros(3)) - @pytest.mark.parametrize('lattice',Symmetry.lattices) + @pytest.mark.parametrize('system',Symmetry.crystal_systems) @pytest.mark.parametrize('proper',[True,False]) - def test_inSST(self,lattice,proper): - assert Symmetry(lattice).inSST(np.zeros(3),proper) + def test_inSST(self,system,proper): + assert Symmetry(system).inSST(np.zeros(3),proper) @pytest.mark.parametrize('function',['inFZ','inDisorientationSST']) def test_invalid_argument(self,function): From 9d94b521ad5feba47ec78fa2816a3ec5f5b27524 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 30 Jun 2020 13:31:58 +0200 Subject: [PATCH 53/78] polishing --- python/damask/_orientation.py | 12 ++++++------ python/damask/_rotation.py | 3 ++- python/tests/test_ori_vec.py | 8 ++++---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 2b6f1f701..6333bbfa0 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -125,10 +125,10 @@ class Orientation: # ToDo: make subclass of lattice and Rotation s = op.reshape(op.shape[:1]+(1,)*len(self.rotation.shape)+(4,)) s = Rotation(np.broadcast_to(s,s.shape[:1]+self.rotation.quaternion.shape)) - + r = np.broadcast_to(self.rotation.quaternion,s.shape[:1]+self.rotation.quaternion.shape) r = Rotation(r) - + return self.__class__(s@r,h['lattice']) @@ -142,7 +142,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation """Transform orientation to fall into fundamental zone according to symmetry.""" equi= self.equivalent_vec.rotation #equivalent orientations r= 1 if not self.rotation.shape else equi.shape[1] #number of rotations - num_equi=equi.shape[0] #number of equivalente orientations + num_equi=equi.shape[0] #number of equivalente orientations quat= np.reshape( equi.as_quaternion(), (r*num_equi,4) ,order='F') #equivalents are listed in intiuitive order boolean=Orientation(quat, self.lattice).inFZ_vec() #check which ones are in FZ if sum(boolean) == r: @@ -150,12 +150,12 @@ class Orientation: # ToDo: make subclass of lattice and Rotation else: print('More than 1 equivalent orientation has been found for an orientation') - index=np.empty(r, dtype=int) + index=np.empty(r, dtype=int) for l,h in enumerate(range(0,r*num_equi, num_equi)): index[l]=np.where(boolean[h:h+num_equi])[0][0] + (l*num_equi) #get first index that is true then go check to next orientation - + return self.__class__(quat[index],self.lattice) - + diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index a8d8a7928..686b144ae 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -267,7 +267,8 @@ class Rotation: """ ro = Rotation._qu2ro(self.quaternion) - return ro[...,:3]*ro[...,3:] if vector else ro + with np.errstate(invalid='ignore'): + return ro[...,:3]*ro[...,3:] if vector else ro def as_homochoric(self): """Homochoric vector: (h_1, h_2, h_3).""" diff --git a/python/tests/test_ori_vec.py b/python/tests/test_ori_vec.py index 6bbe37f63..ffe2923ff 100644 --- a/python/tests/test_ori_vec.py +++ b/python/tests/test_ori_vec.py @@ -5,10 +5,10 @@ from damask import Rotation from damask import Orientation from damask import Lattice -rot0= Rotation.from_random() ; -rot1= Rotation.from_random() ; -rot2= Rotation.from_random() ; -rot3= Rotation.from_random() ; +rot0= Rotation.from_random() +rot1= Rotation.from_random() +rot2= Rotation.from_random() +rot3= Rotation.from_random() #disorientation From c86e3e292c2b189eceb1cf49ce1dd8af7a9542ee Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 30 Jun 2020 13:55:09 +0200 Subject: [PATCH 54/78] WIP: cleaning namespace --- python/damask/_lattice.py | 17 +++++++++++------ python/damask/_orientation.py | 31 ++++++++++++++++++------------- python/tests/test_ori_vec.py | 8 ++++---- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/python/damask/_lattice.py b/python/damask/_lattice.py index 22f1de320..ee718adcd 100644 --- a/python/damask/_lattice.py +++ b/python/damask/_lattice.py @@ -479,11 +479,11 @@ class Lattice: # ToDo: Make a subclass of Symmetry! """ lattices = { - 'triclinic':{'symmetry':None}, - 'bct':{'symmetry':'tetragonal'}, - 'hex':{'symmetry':'hexagonal'}, - 'fcc':{'symmetry':'cubic','c/a':1.0}, - 'bcc':{'symmetry':'cubic','c/a':1.0}, + 'triclinic':{'system':None}, + 'bct': {'system':'tetragonal'}, + 'hex': {'system':'hexagonal'}, + 'fcc': {'system':'cubic','c/a':1.0}, + 'bcc': {'system':'cubic','c/a':1.0}, } @@ -498,7 +498,12 @@ class Lattice: # ToDo: Make a subclass of Symmetry! """ self.lattice = lattice - self.symmetry = Symmetry(self.lattices[lattice]['symmetry']) + self.symmetry = Symmetry(self.lattices[lattice]['system']) + + # transition to subclass + self.system = self.symmetry.system + self.in_SST = self.symmetry.in_SST + self.inFZ = self.symmetry.inFZ def __repr__(self): diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 6333bbfa0..753da4d4e 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -39,6 +39,11 @@ class Orientation: # ToDo: make subclass of lattice and Rotation else: self.rotation = Rotation.from_quaternion(rotation) # assume quaternion + def __getitem__(self,item): + if isinstance(item,tuple) and len(item) >= len(self): + raise IndexError('Too many indices') + return self.__class__(self.rotation[item],self.lattice) + def disorientation(self, other, @@ -66,7 +71,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation r = b*aInv for k in range(2): r.inverse() - breaker = self.lattice.symmetry.inFZ(r.as_Rodrigues(vector=True)) \ + breaker = self.lattice.inFZ(r.as_Rodrigues(vector=True)) \ and (not SST or other.lattice.symmetry.inDisorientationSST(r.as_Rodrigues(vector=True))) if breaker: break if breaker: break @@ -79,17 +84,17 @@ class Orientation: # ToDo: make subclass of lattice and Rotation def inFZ_vec(self): """Check if orientations fall into Fundamental Zone.""" if not self.rotation.shape: - return self.lattice.symmetry.inFZ(self.rotation.as_Rodrigues(vector=True)) + return self.lattice.inFZ(self.rotation.as_Rodrigues(vector=True)) else: - return [self.lattice.symmetry.inFZ(\ + return [self.lattice.inFZ(\ self.rotation.as_Rodrigues(vector=True)[l]) for l in range(self.rotation.shape[0])] def inFZ(self): - return self.lattice.symmetry.inFZ(self.rotation.as_Rodrigues(vector=True)) + return self.lattice.inFZ(self.rotation.as_Rodrigues(vector=True)) @property - def equivalent_vec(self): + def equivalent(self): """ Return orientations which are symmetrically equivalent. @@ -97,12 +102,12 @@ class Orientation: # ToDo: make subclass of lattice and Rotation is added to the left of the rotation array. """ - s = self.lattice.symmetry.symmetry_operations #24 lines (sym) x 4 columns (quat) - s = s.reshape(s.shape[:1]+(1,)*len(self.rotation.shape)+(4,)) #reshape zo (24,1,4) + s = self.lattice.symmetry.symmetry_operations + s = s.reshape(s.shape[:1]+(1,)*len(self.rotation.shape)+(4,)) s = Rotation(np.broadcast_to(s,s.shape[:1]+self.rotation.quaternion.shape)) - r = np.broadcast_to(self.rotation.quaternion,s.shape[:1]+self.rotation.quaternion.shape) #(24,NumRots,4) - r = Rotation(r) #(24, NumRot) + r = np.broadcast_to(self.rotation.quaternion,s.shape[:1]+self.rotation.quaternion.shape) + r = Rotation(r) return self.__class__(s@r,self.lattice) @@ -140,7 +145,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation @property def reduced_vec(self): """Transform orientation to fall into fundamental zone according to symmetry.""" - equi= self.equivalent_vec.rotation #equivalent orientations + equi= self.equivalent.rotation #equivalent orientations r= 1 if not self.rotation.shape else equi.shape[1] #number of rotations num_equi=equi.shape[0] #number of equivalente orientations quat= np.reshape( equi.as_quaternion(), (r*num_equi,4) ,order='F') #equivalents are listed in intiuitive order @@ -163,7 +168,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation def reduced(self): """Transform orientation to fall into fundamental zone according to symmetry.""" for me in self.equivalentOrientations(): - if self.lattice.symmetry.inFZ(me.rotation.as_Rodrigues(vector=True)): break + if self.lattice.inFZ(me.rotation.as_Rodrigues(vector=True)): break return self.__class__(me.rotation,self.lattice) @@ -197,9 +202,9 @@ class Orientation: # ToDo: make subclass of lattice and Rotation def IPF_color(self,axis): """TSL color of inverse pole figure for given axis. Not for hex or triclinic lattices.""" - eq = self.equivalent_vec + eq = self.equivalent pole = eq.rotation @ np.broadcast_to(axis/np.linalg.norm(axis),eq.rotation.shape+(3,)) - in_SST, color = self.lattice.symmetry.in_SST(pole,color=True) + in_SST, color = self.lattice.in_SST(pole,color=True) # ignore duplicates (occur for highly symmetric orientations) found = np.zeros_like(in_SST[1],dtype=bool) diff --git a/python/tests/test_ori_vec.py b/python/tests/test_ori_vec.py index ffe2923ff..ed64306c4 100644 --- a/python/tests/test_ori_vec.py +++ b/python/tests/test_ori_vec.py @@ -28,13 +28,13 @@ class TestOrientation_vec: ori_vec=Orientation(quat,lattice) for s in range(len(ori_vec.lattice.symmetry.symmetryOperations())): - assert all(ori_vec.equivalent_vec.rotation.as_Eulers()[s,0] == \ + assert all(ori_vec.equivalent.rotation.as_Eulers()[s,0] == \ ori0.equivalentOrientations()[s].rotation.as_Eulers()) - assert all(ori_vec.equivalent_vec.rotation.as_quaternion()[s,1] == \ + assert all(ori_vec.equivalent.rotation.as_quaternion()[s,1] == \ ori1.equivalentOrientations()[s].rotation.as_quaternion()) - assert all(ori_vec.equivalent_vec.rotation.as_Rodrigues()[s,2] == \ + assert all(ori_vec.equivalent.rotation.as_Rodrigues()[s,2] == \ ori2.equivalentOrientations()[s].rotation.as_Rodrigues()) - assert all(ori_vec.equivalent_vec.rotation.as_cubochoric()[s,3] == \ + assert all(ori_vec.equivalent.rotation.as_cubochoric()[s,3] == \ ori3.equivalentOrientations()[s].rotation.as_cubochoric()) @pytest.mark.parametrize('lattice',Lattice.lattices) From be21d1289d2d419c8132c9ee9a1320aaf590e94f Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 30 Jun 2020 16:33:04 +0200 Subject: [PATCH 55/78] using slicing method --- python/damask/_orientation.py | 2 +- python/tests/test_Orientation.py | 2 +- python/tests/test_ori_vec.py | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 753da4d4e..7d0f11c49 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -192,7 +192,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation """TSL color of inverse pole figure for given axis.""" color = np.zeros(3,'d') - for o in self.equivalentOrientations(): + for o in self.equivalent: pole = o.rotation@axis # align crystal direction to axis inSST,color = self.lattice.symmetry.inSST(pole,color=True) if inSST: break diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index a8b8afdac..277fa2a4b 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -34,7 +34,7 @@ class TestOrientation: for rot in [Rotation.from_random() for r in range(n//100)]: R = damask.Orientation(rot,lattice) color = R.IPFcolor(direction) - for equivalent in R.equivalentOrientations(): + for equivalent in R.equivalent: assert np.allclose(color,R.IPFcolor(direction)) @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) diff --git a/python/tests/test_ori_vec.py b/python/tests/test_ori_vec.py index ed64306c4..ff5fe80bc 100644 --- a/python/tests/test_ori_vec.py +++ b/python/tests/test_ori_vec.py @@ -29,13 +29,13 @@ class TestOrientation_vec: for s in range(len(ori_vec.lattice.symmetry.symmetryOperations())): assert all(ori_vec.equivalent.rotation.as_Eulers()[s,0] == \ - ori0.equivalentOrientations()[s].rotation.as_Eulers()) + ori0.equivalent[s].rotation.as_Eulers()) assert all(ori_vec.equivalent.rotation.as_quaternion()[s,1] == \ - ori1.equivalentOrientations()[s].rotation.as_quaternion()) + ori1.equivalent[s].rotation.as_quaternion()) assert all(ori_vec.equivalent.rotation.as_Rodrigues()[s,2] == \ - ori2.equivalentOrientations()[s].rotation.as_Rodrigues()) + ori2.equivalent[s].rotation.as_Rodrigues()) assert all(ori_vec.equivalent.rotation.as_cubochoric()[s,3] == \ - ori3.equivalentOrientations()[s].rotation.as_cubochoric()) + ori3.equivalent[s].rotation.as_cubochoric()) @pytest.mark.parametrize('lattice',Lattice.lattices) def test_inFZ_vec(self,lattice): From 3d6afff27a8a66cdd1c1a0b0a3921a8acb519c24 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 30 Jun 2020 17:30:29 +0200 Subject: [PATCH 56/78] clearer name --- python/tests/test_Rotation.py | 102 +++++++++++++++++----------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 5a1cd145d..b087cc774 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -6,15 +6,15 @@ import numpy as np from damask import Rotation from damask import _rotation - - -n = 1100 +n = 1000 atol=1.e-4 -scatter=1.e-2 + @pytest.fixture -def default(): +def set_of_rotations(): """A set of n random rotations.""" + n = 1100 + scatter=1.e-2 specials = np.array([ [1.0, 0.0, 0.0, 0.0], #---------------------- @@ -567,9 +567,9 @@ class TestRotation: (Rotation._qu2ro,Rotation._ro2qu), (Rotation._qu2ho,Rotation._ho2qu), (Rotation._qu2cu,Rotation._cu2qu)]) - def test_quaternion_internal(self,default,forward,backward): + def test_quaternion_internal(self,set_of_rotations,forward,backward): """Ensure invariance of conversion from quaternion and back.""" - for rot in default: + for rot in set_of_rotations: m = rot.as_quaternion() o = backward(forward(m)) ok = np.allclose(m,o,atol=atol) @@ -584,9 +584,9 @@ class TestRotation: (Rotation._om2ro,Rotation._ro2om), (Rotation._om2ho,Rotation._ho2om), (Rotation._om2cu,Rotation._cu2om)]) - def test_matrix_internal(self,default,forward,backward): + def test_matrix_internal(self,set_of_rotations,forward,backward): """Ensure invariance of conversion from rotation matrix and back.""" - for rot in default: + for rot in set_of_rotations: m = rot.as_matrix() o = backward(forward(m)) ok = np.allclose(m,o,atol=atol) @@ -599,9 +599,9 @@ class TestRotation: (Rotation._eu2ro,Rotation._ro2eu), (Rotation._eu2ho,Rotation._ho2eu), (Rotation._eu2cu,Rotation._cu2eu)]) - def test_Eulers_internal(self,default,forward,backward): + def test_Eulers_internal(self,set_of_rotations,forward,backward): """Ensure invariance of conversion from Euler angles and back.""" - for rot in default: + for rot in set_of_rotations: m = rot.as_Eulers() o = backward(forward(m)) u = np.array([np.pi*2,np.pi,np.pi*2]) @@ -619,9 +619,9 @@ class TestRotation: (Rotation._ax2ro,Rotation._ro2ax), (Rotation._ax2ho,Rotation._ho2ax), (Rotation._ax2cu,Rotation._cu2ax)]) - def test_axis_angle_internal(self,default,forward,backward): + def test_axis_angle_internal(self,set_of_rotations,forward,backward): """Ensure invariance of conversion from axis angle angles pair and back.""" - for rot in default: + for rot in set_of_rotations: m = rot.as_axis_angle() o = backward(forward(m)) ok = np.allclose(m,o,atol=atol) @@ -636,10 +636,10 @@ class TestRotation: (Rotation._ro2ax,Rotation._ax2ro), (Rotation._ro2ho,Rotation._ho2ro), (Rotation._ro2cu,Rotation._cu2ro)]) - def test_Rodrigues_internal(self,default,forward,backward): + def test_Rodrigues_internal(self,set_of_rotations,forward,backward): """Ensure invariance of conversion from Rodrigues-Frank vector and back.""" cutoff = np.tan(np.pi*.5*(1.-1e-4)) - for rot in default: + for rot in set_of_rotations: m = rot.as_Rodrigues() o = backward(forward(m)) ok = np.allclose(np.clip(m,None,cutoff),np.clip(o,None,cutoff),atol=atol) @@ -653,9 +653,9 @@ class TestRotation: (Rotation._ho2ax,Rotation._ax2ho), (Rotation._ho2ro,Rotation._ro2ho), (Rotation._ho2cu,Rotation._cu2ho)]) - def test_homochoric_internal(self,default,forward,backward): + def test_homochoric_internal(self,set_of_rotations,forward,backward): """Ensure invariance of conversion from homochoric vector and back.""" - for rot in default: + for rot in set_of_rotations: m = rot.as_homochoric() o = backward(forward(m)) ok = np.allclose(m,o,atol=atol) @@ -668,9 +668,9 @@ class TestRotation: (Rotation._cu2ax,Rotation._ax2cu), (Rotation._cu2ro,Rotation._ro2cu), (Rotation._cu2ho,Rotation._ho2cu)]) - def test_cubochoric_internal(self,default,forward,backward): + def test_cubochoric_internal(self,set_of_rotations,forward,backward): """Ensure invariance of conversion from cubochoric vector and back.""" - for rot in default: + for rot in set_of_rotations: m = rot.as_cubochoric() o = backward(forward(m)) ok = np.allclose(m,o,atol=atol) @@ -684,9 +684,9 @@ class TestRotation: (Rotation._qu2ax,qu2ax), (Rotation._qu2ro,qu2ro), (Rotation._qu2ho,qu2ho)]) - def test_quaternion_vectorization(self,default,vectorized,single): + def test_quaternion_vectorization(self,set_of_rotations,vectorized,single): """Check vectorized implementation for quaternion against single point calculation.""" - qu = np.array([rot.as_quaternion() for rot in default]) + qu = np.array([rot.as_quaternion() for rot in set_of_rotations]) vectorized(qu.reshape(qu.shape[0]//2,-1,4)) co = vectorized(qu) for q,c in zip(qu,co): @@ -697,9 +697,9 @@ class TestRotation: @pytest.mark.parametrize('vectorized, single',[(Rotation._om2qu,om2qu), (Rotation._om2eu,om2eu), (Rotation._om2ax,om2ax)]) - def test_matrix_vectorization(self,default,vectorized,single): + def test_matrix_vectorization(self,set_of_rotations,vectorized,single): """Check vectorized implementation for rotation matrix against single point calculation.""" - om = np.array([rot.as_matrix() for rot in default]) + om = np.array([rot.as_matrix() for rot in set_of_rotations]) vectorized(om.reshape(om.shape[0]//2,-1,3,3)) co = vectorized(om) for o,c in zip(om,co): @@ -710,9 +710,9 @@ class TestRotation: (Rotation._eu2om,eu2om), (Rotation._eu2ax,eu2ax), (Rotation._eu2ro,eu2ro)]) - def test_Eulers_vectorization(self,default,vectorized,single): + def test_Eulers_vectorization(self,set_of_rotations,vectorized,single): """Check vectorized implementation for Euler angles against single point calculation.""" - eu = np.array([rot.as_Eulers() for rot in default]) + eu = np.array([rot.as_Eulers() for rot in set_of_rotations]) vectorized(eu.reshape(eu.shape[0]//2,-1,3)) co = vectorized(eu) for e,c in zip(eu,co): @@ -723,9 +723,9 @@ class TestRotation: (Rotation._ax2om,ax2om), (Rotation._ax2ro,ax2ro), (Rotation._ax2ho,ax2ho)]) - def test_axis_angle_vectorization(self,default,vectorized,single): + def test_axis_angle_vectorization(self,set_of_rotations,vectorized,single): """Check vectorized implementation for axis angle pair against single point calculation.""" - ax = np.array([rot.as_axis_angle() for rot in default]) + ax = np.array([rot.as_axis_angle() for rot in set_of_rotations]) vectorized(ax.reshape(ax.shape[0]//2,-1,4)) co = vectorized(ax) for a,c in zip(ax,co): @@ -735,9 +735,9 @@ class TestRotation: @pytest.mark.parametrize('vectorized, single',[(Rotation._ro2ax,ro2ax), (Rotation._ro2ho,ro2ho)]) - def test_Rodrigues_vectorization(self,default,vectorized,single): + def test_Rodrigues_vectorization(self,set_of_rotations,vectorized,single): """Check vectorized implementation for Rodrigues-Frank vector against single point calculation.""" - ro = np.array([rot.as_Rodrigues() for rot in default]) + ro = np.array([rot.as_Rodrigues() for rot in set_of_rotations]) vectorized(ro.reshape(ro.shape[0]//2,-1,4)) co = vectorized(ro) for r,c in zip(ro,co): @@ -746,9 +746,9 @@ class TestRotation: @pytest.mark.parametrize('vectorized, single',[(Rotation._ho2ax,ho2ax), (Rotation._ho2cu,ho2cu)]) - def test_homochoric_vectorization(self,default,vectorized,single): + def test_homochoric_vectorization(self,set_of_rotations,vectorized,single): """Check vectorized implementation for homochoric vector against single point calculation.""" - ho = np.array([rot.as_homochoric() for rot in default]) + ho = np.array([rot.as_homochoric() for rot in set_of_rotations]) vectorized(ho.reshape(ho.shape[0]//2,-1,3)) co = vectorized(ho) for h,c in zip(ho,co): @@ -756,9 +756,9 @@ class TestRotation: assert np.allclose(single(h),c) and np.allclose(single(h),vectorized(h)) @pytest.mark.parametrize('vectorized, single',[(Rotation._cu2ho,cu2ho)]) - def test_cubochoric_vectorization(self,default,vectorized,single): + def test_cubochoric_vectorization(self,set_of_rotations,vectorized,single): """Check vectorized implementation for cubochoric vector against single point calculation.""" - cu = np.array([rot.as_cubochoric() for rot in default]) + cu = np.array([rot.as_cubochoric() for rot in set_of_rotations]) vectorized(cu.reshape(cu.shape[0]//2,-1,3)) co = vectorized(cu) for u,c in zip(cu,co): @@ -766,8 +766,8 @@ class TestRotation: assert np.allclose(single(u),c) and np.allclose(single(u),vectorized(u)) @pytest.mark.parametrize('degrees',[True,False]) - def test_Eulers(self,default,degrees): - for rot in default: + def test_Eulers(self,set_of_rotations,degrees): + for rot in set_of_rotations: m = rot.as_quaternion() o = Rotation.from_Eulers(rot.as_Eulers(degrees),degrees).as_quaternion() ok = np.allclose(m,o,atol=atol) @@ -779,9 +779,9 @@ class TestRotation: @pytest.mark.parametrize('P',[1,-1]) @pytest.mark.parametrize('normalise',[True,False]) @pytest.mark.parametrize('degrees',[True,False]) - def test_axis_angle(self,default,degrees,normalise,P): + def test_axis_angle(self,set_of_rotations,degrees,normalise,P): c = np.array([P*-1,P*-1,P*-1,1.]) - for rot in default: + for rot in set_of_rotations: m = rot.as_Eulers() o = Rotation.from_axis_angle(rot.as_axis_angle(degrees)*c,degrees,normalise,P).as_Eulers() u = np.array([np.pi*2,np.pi,np.pi*2]) @@ -793,8 +793,8 @@ class TestRotation: print(m,o,rot.as_quaternion()) 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: + def test_matrix(self,set_of_rotations): + for rot in set_of_rotations: m = rot.as_axis_angle() o = Rotation.from_axis_angle(rot.as_axis_angle()).as_axis_angle() ok = np.allclose(m,o,atol=atol) @@ -805,9 +805,9 @@ class TestRotation: @pytest.mark.parametrize('P',[1,-1]) @pytest.mark.parametrize('normalise',[True,False]) - def test_Rodrigues(self,default,normalise,P): + def test_Rodrigues(self,set_of_rotations,normalise,P): c = np.array([P*-1,P*-1,P*-1,1.]) - for rot in default: + for rot in set_of_rotations: m = rot.as_matrix() o = Rotation.from_Rodrigues(rot.as_Rodrigues()*c,normalise,P).as_matrix() ok = np.allclose(m,o,atol=atol) @@ -815,9 +815,9 @@ class TestRotation: assert ok and np.isclose(np.linalg.det(o),1.0) @pytest.mark.parametrize('P',[1,-1]) - def test_homochoric(self,default,P): + def test_homochoric(self,set_of_rotations,P): cutoff = np.tan(np.pi*.5*(1.-1e-4)) - for rot in default: + for rot in set_of_rotations: m = rot.as_Rodrigues() o = Rotation.from_homochoric(rot.as_homochoric()*P*-1,P).as_Rodrigues() ok = np.allclose(np.clip(m,None,cutoff),np.clip(o,None,cutoff),atol=atol) @@ -826,8 +826,8 @@ class TestRotation: assert ok and np.isclose(np.linalg.norm(o[:3]),1.0) @pytest.mark.parametrize('P',[1,-1]) - def test_cubochoric(self,default,P): - for rot in default: + def test_cubochoric(self,set_of_rotations,P): + for rot in set_of_rotations: m = rot.as_homochoric() o = Rotation.from_cubochoric(rot.as_cubochoric()*P*-1,P).as_homochoric() ok = np.allclose(m,o,atol=atol) @@ -836,9 +836,9 @@ class TestRotation: @pytest.mark.parametrize('P',[1,-1]) @pytest.mark.parametrize('accept_homomorph',[True,False]) - def test_quaternion(self,default,P,accept_homomorph): + def test_quaternion(self,set_of_rotations,P,accept_homomorph): c = np.array([1,P*-1,P*-1,P*-1]) * (-1 if accept_homomorph else 1) - for rot in default: + for rot in set_of_rotations: m = rot.as_cubochoric() o = Rotation.from_quaternion(rot.as_quaternion()*c,accept_homomorph,P).as_cubochoric() ok = np.allclose(m,o,atol=atol) @@ -848,8 +848,8 @@ class TestRotation: assert ok and o.max() < np.pi**(2./3.)*0.5+1.e-9 @pytest.mark.parametrize('reciprocal',[True,False]) - def test_basis(self,default,reciprocal): - for rot in default: + def test_basis(self,set_of_rotations,reciprocal): + for rot in set_of_rotations: om = rot.as_matrix() + 0.1*np.eye(3) rot = Rotation.from_basis(om,False,reciprocal=reciprocal) assert np.isclose(np.linalg.det(rot.as_matrix()),1.0) @@ -909,8 +909,8 @@ class TestRotation: @pytest.mark.parametrize('data',[np.random.rand(5,3), np.random.rand(5,3,3), np.random.rand(5,3,3,3,3)]) - def test_rotate_vectorization(self,default,data): - for rot in default: + def test_rotate_vectorization(self,set_of_rotations,data): + for rot in set_of_rotations: v = rot.broadcast_to((5,)) @ data for i in range(data.shape[0]): print(i-data[i]) From 6e27a140f66796e47072897c9d0659323127342f Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 30 Jun 2020 17:35:52 +0200 Subject: [PATCH 57/78] better split --- python/tests/conftest.py | 79 +++++++++++++++++++++++++++++++++++ python/tests/test_Rotation.py | 77 ---------------------------------- 2 files changed, 79 insertions(+), 77 deletions(-) diff --git a/python/tests/conftest.py b/python/tests/conftest.py index 411c07a8c..78ebea2a5 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -1,7 +1,10 @@ import os +import numpy as np import pytest +from damask import Rotation + def pytest_addoption(parser): parser.addoption("--update", action="store_true", @@ -16,3 +19,79 @@ def update(request): def reference_dir_base(): """Directory containing reference results.""" return os.path.join(os.path.dirname(__file__),'reference') + +@pytest.fixture +def set_of_rotations(): + """A set of n random rotations.""" + n = 1100 + scatter=1.e-2 + specials = np.array([ + [1.0, 0.0, 0.0, 0.0], + #---------------------- + [0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + [0.0,-1.0, 0.0, 0.0], + [0.0, 0.0,-1.0, 0.0], + [0.0, 0.0, 0.0,-1.0], + #---------------------- + [1.0, 1.0, 0.0, 0.0], + [1.0, 0.0, 1.0, 0.0], + [1.0, 0.0, 0.0, 1.0], + [0.0, 1.0, 1.0, 0.0], + [0.0, 1.0, 0.0, 1.0], + [0.0, 0.0, 1.0, 1.0], + #---------------------- + [1.0,-1.0, 0.0, 0.0], + [1.0, 0.0,-1.0, 0.0], + [1.0, 0.0, 0.0,-1.0], + [0.0, 1.0,-1.0, 0.0], + [0.0, 1.0, 0.0,-1.0], + [0.0, 0.0, 1.0,-1.0], + #---------------------- + [0.0, 1.0,-1.0, 0.0], + [0.0, 1.0, 0.0,-1.0], + [0.0, 0.0, 1.0,-1.0], + #---------------------- + [0.0,-1.0,-1.0, 0.0], + [0.0,-1.0, 0.0,-1.0], + [0.0, 0.0,-1.0,-1.0], + #---------------------- + [1.0, 1.0, 1.0, 0.0], + [1.0, 1.0, 0.0, 1.0], + [1.0, 0.0, 1.0, 1.0], + [1.0,-1.0, 1.0, 0.0], + [1.0,-1.0, 0.0, 1.0], + [1.0, 0.0,-1.0, 1.0], + [1.0, 1.0,-1.0, 0.0], + [1.0, 1.0, 0.0,-1.0], + [1.0, 0.0, 1.0,-1.0], + [1.0,-1.0,-1.0, 0.0], + [1.0,-1.0, 0.0,-1.0], + [1.0, 0.0,-1.0,-1.0], + #---------------------- + [0.0, 1.0, 1.0, 1.0], + [0.0, 1.0,-1.0, 1.0], + [0.0, 1.0, 1.0,-1.0], + [0.0,-1.0, 1.0, 1.0], + [0.0,-1.0,-1.0, 1.0], + [0.0,-1.0, 1.0,-1.0], + [0.0,-1.0,-1.0,-1.0], + #---------------------- + [1.0, 1.0, 1.0, 1.0], + [1.0,-1.0, 1.0, 1.0], + [1.0, 1.0,-1.0, 1.0], + [1.0, 1.0, 1.0,-1.0], + [1.0,-1.0,-1.0, 1.0], + [1.0,-1.0, 1.0,-1.0], + [1.0, 1.0,-1.0,-1.0], + [1.0,-1.0,-1.0,-1.0], + ]) + specials /= np.linalg.norm(specials,axis=1).reshape(-1,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.from_quaternion(s) for s in specials] + \ + [Rotation.from_quaternion(s) for s in specials_scatter] + \ + [Rotation.from_random() for _ in range(n-len(specials)-len(specials_scatter))] diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index b087cc774..6acca8e79 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -9,83 +9,6 @@ from damask import _rotation n = 1000 atol=1.e-4 - -@pytest.fixture -def set_of_rotations(): - """A set of n random rotations.""" - n = 1100 - scatter=1.e-2 - specials = np.array([ - [1.0, 0.0, 0.0, 0.0], - #---------------------- - [0.0, 1.0, 0.0, 0.0], - [0.0, 0.0, 1.0, 0.0], - [0.0, 0.0, 0.0, 1.0], - [0.0,-1.0, 0.0, 0.0], - [0.0, 0.0,-1.0, 0.0], - [0.0, 0.0, 0.0,-1.0], - #---------------------- - [1.0, 1.0, 0.0, 0.0], - [1.0, 0.0, 1.0, 0.0], - [1.0, 0.0, 0.0, 1.0], - [0.0, 1.0, 1.0, 0.0], - [0.0, 1.0, 0.0, 1.0], - [0.0, 0.0, 1.0, 1.0], - #---------------------- - [1.0,-1.0, 0.0, 0.0], - [1.0, 0.0,-1.0, 0.0], - [1.0, 0.0, 0.0,-1.0], - [0.0, 1.0,-1.0, 0.0], - [0.0, 1.0, 0.0,-1.0], - [0.0, 0.0, 1.0,-1.0], - #---------------------- - [0.0, 1.0,-1.0, 0.0], - [0.0, 1.0, 0.0,-1.0], - [0.0, 0.0, 1.0,-1.0], - #---------------------- - [0.0,-1.0,-1.0, 0.0], - [0.0,-1.0, 0.0,-1.0], - [0.0, 0.0,-1.0,-1.0], - #---------------------- - [1.0, 1.0, 1.0, 0.0], - [1.0, 1.0, 0.0, 1.0], - [1.0, 0.0, 1.0, 1.0], - [1.0,-1.0, 1.0, 0.0], - [1.0,-1.0, 0.0, 1.0], - [1.0, 0.0,-1.0, 1.0], - [1.0, 1.0,-1.0, 0.0], - [1.0, 1.0, 0.0,-1.0], - [1.0, 0.0, 1.0,-1.0], - [1.0,-1.0,-1.0, 0.0], - [1.0,-1.0, 0.0,-1.0], - [1.0, 0.0,-1.0,-1.0], - #---------------------- - [0.0, 1.0, 1.0, 1.0], - [0.0, 1.0,-1.0, 1.0], - [0.0, 1.0, 1.0,-1.0], - [0.0,-1.0, 1.0, 1.0], - [0.0,-1.0,-1.0, 1.0], - [0.0,-1.0, 1.0,-1.0], - [0.0,-1.0,-1.0,-1.0], - #---------------------- - [1.0, 1.0, 1.0, 1.0], - [1.0,-1.0, 1.0, 1.0], - [1.0, 1.0,-1.0, 1.0], - [1.0, 1.0, 1.0,-1.0], - [1.0,-1.0,-1.0, 1.0], - [1.0,-1.0, 1.0,-1.0], - [1.0, 1.0,-1.0,-1.0], - [1.0,-1.0,-1.0,-1.0], - ]) - specials /= np.linalg.norm(specials,axis=1).reshape(-1,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.from_quaternion(s) for s in specials] + \ - [Rotation.from_quaternion(s) for s in specials_scatter] + \ - [Rotation.from_random() for _ in range(n-len(specials)-len(specials_scatter))] - @pytest.fixture def reference_dir(reference_dir_base): """Directory containing reference results.""" From bdb461a5532c6ecf3f3cdca1f89926058ae18a3d Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 30 Jun 2020 18:12:19 +0200 Subject: [PATCH 58/78] more flexible and independent --- python/tests/conftest.py | 23 +++++++++++++++++------ python/tests/test_Rotation.py | 8 ++++++-- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/python/tests/conftest.py b/python/tests/conftest.py index 78ebea2a5..2be40a10a 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -3,8 +3,6 @@ import numpy as np import pytest -from damask import Rotation - def pytest_addoption(parser): parser.addoption("--update", action="store_true", @@ -21,8 +19,21 @@ def reference_dir_base(): return os.path.join(os.path.dirname(__file__),'reference') @pytest.fixture -def set_of_rotations(): +def set_of_quaternions(): """A set of n random rotations.""" + def random_quaternions(N): + r = np.random.rand(N,3) + + A = np.sqrt(r[:,2]) + B = np.sqrt(1.0-r[:,2]) + qu = np.column_stack([np.cos(2.0*np.pi*r[:,0])*A, + np.sin(2.0*np.pi*r[:,1])*B, + np.cos(2.0*np.pi*r[:,1])*B, + np.sin(2.0*np.pi*r[:,0])*A]) + qu[:,0]*=np.sign(qu[:,0]) + + return qu + n = 1100 scatter=1.e-2 specials = np.array([ @@ -92,6 +103,6 @@ def set_of_rotations(): specials_scatter /= np.linalg.norm(specials_scatter,axis=1).reshape(-1,1) specials_scatter[specials_scatter[:,0]<0]*=-1 - return [Rotation.from_quaternion(s) for s in specials] + \ - [Rotation.from_quaternion(s) for s in specials_scatter] + \ - [Rotation.from_random() for _ in range(n-len(specials)-len(specials_scatter))] + return [s for s in specials] + \ + [s for s in specials_scatter] + \ + [s for s in random_quaternions(n-2*len(specials))] diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 6acca8e79..a7f4dd17f 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -14,6 +14,10 @@ def reference_dir(reference_dir_base): """Directory containing reference results.""" return os.path.join(reference_dir_base,'Rotation') +@pytest.fixture +def set_of_rotations(set_of_quaternions): + return [Rotation.from_quaternion(s) for s in set_of_quaternions] + #################################################################################################### # Code below available according to the following conditions on https://github.com/MarDiehl/3Drotations @@ -607,9 +611,9 @@ class TestRotation: (Rotation._qu2ax,qu2ax), (Rotation._qu2ro,qu2ro), (Rotation._qu2ho,qu2ho)]) - def test_quaternion_vectorization(self,set_of_rotations,vectorized,single): + def test_quaternion_vectorization(self,set_of_quaternions,vectorized,single): """Check vectorized implementation for quaternion against single point calculation.""" - qu = np.array([rot.as_quaternion() for rot in set_of_rotations]) + qu = np.array(set_of_quaternions) vectorized(qu.reshape(qu.shape[0]//2,-1,4)) co = vectorized(qu) for q,c in zip(qu,co): From 9a83b11a9923d26fc143fa80e0e4a55d63b50606 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 30 Jun 2020 18:41:59 +0200 Subject: [PATCH 59/78] testing IPF color (vectorization) --- python/tests/conftest.py | 6 +++--- python/tests/test_Orientation.py | 30 +++++++++++++++++++++++------- python/tests/test_Rotation.py | 2 +- python/tests/test_ori_vec.py | 17 ----------------- 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/python/tests/conftest.py b/python/tests/conftest.py index 2be40a10a..af195ad6d 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -103,6 +103,6 @@ def set_of_quaternions(): specials_scatter /= np.linalg.norm(specials_scatter,axis=1).reshape(-1,1) specials_scatter[specials_scatter[:,0]<0]*=-1 - return [s for s in specials] + \ - [s for s in specials_scatter] + \ - [s for s in random_quaternions(n-2*len(specials))] + return np.array([s for s in specials] + \ + [s for s in specials_scatter] + \ + [s for s in random_quaternions(n-2*len(specials))]) diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index 277fa2a4b..750f6176d 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -11,6 +11,15 @@ from damask import Lattice n = 1000 +def IPF_color(orientation,direction): + """TSL color of inverse pole figure for given axis (non-vectorized).""" + for o in orientation.equivalent: + pole = o.rotation@direction + inSST,color = orientation.lattice.in_SST(pole,color=True) + if inSST: break + + return color + @pytest.fixture def reference_dir(reference_dir_base): """Directory containing reference results.""" @@ -26,16 +35,23 @@ class TestOrientation: def test_IPF_cubic(self,color,lattice): cube = damask.Orientation(damask.Rotation(),lattice) for direction in set(permutations(np.array(color['direction']))): - assert np.allclose(cube.IPFcolor(np.array(direction)),np.array(color['RGB'])) + assert np.allclose(cube.IPF_color(np.array(direction)),np.array(color['RGB'])) @pytest.mark.parametrize('lattice',Lattice.lattices) - def test_IPF(self,lattice): + def test_IPF_equivalent(self,set_of_quaternions,lattice): direction = np.random.random(3)*2.0-1 - for rot in [Rotation.from_random() for r in range(n//100)]: - R = damask.Orientation(rot,lattice) - color = R.IPFcolor(direction) - for equivalent in R.equivalent: - assert np.allclose(color,R.IPFcolor(direction)) + for ori in Orientation(Rotation(set_of_quaternions),lattice)[200]: + color = ori.IPF_color(direction) + for equivalent in ori.equivalent: + assert np.allclose(color,equivalent.IPF_color(direction)) + + + @pytest.mark.parametrize('lattice',Lattice.lattices) + def test_IPF_vectorize(self,set_of_quaternions,lattice): + for ori in Orientation(Rotation(set_of_quaternions),lattice)[200]: + direction = np.random.random(3)*2.0-1 + assert np.allclose(ori.IPF_color(direction),IPF_color(ori,direction)) + @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) @pytest.mark.parametrize('lattice',['fcc','bcc']) diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index a7f4dd17f..20d01af4a 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -6,7 +6,7 @@ import numpy as np from damask import Rotation from damask import _rotation -n = 1000 +n = 1100 atol=1.e-4 @pytest.fixture diff --git a/python/tests/test_ori_vec.py b/python/tests/test_ori_vec.py index ff5fe80bc..772096201 100644 --- a/python/tests/test_ori_vec.py +++ b/python/tests/test_ori_vec.py @@ -97,20 +97,3 @@ class TestOrientation_vec: assert all(ori_vec.reduced_vec.rotation.as_Rodrigues()[2] == ori2.reduced().rotation.as_Rodrigues() ) assert all(ori_vec.reduced_vec.rotation.as_cubochoric()[3] == ori3.reduced().rotation.as_cubochoric() ) assert all(ori_vec.reduced_vec.rotation.as_axis_angle()[4] == ori4.reduced().rotation.as_axis_angle() ) - - - @pytest.mark.parametrize('lattice',['bcc','fcc','bct']) - def test_IPFcolor_vec(self,lattice): - ori0=Orientation(rot0,lattice) - ori1=Orientation(rot1,lattice) - ori2=Orientation(rot2,lattice) - ori3=Orientation(rot3,lattice) - - quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),\ - rot2.as_quaternion(),rot3.as_quaternion()]) - ori_vec=Orientation(quat,lattice) - - assert np.allclose( ori_vec.IPF_color(np.array([0,0,1]))[0],ori0.IPFcolor(np.array([0,0,1]))) - assert np.allclose( ori_vec.IPF_color(np.array([0,2,1]))[1],ori1.IPFcolor(np.array([0,2,1]))) - assert np.allclose( ori_vec.IPF_color(np.array([0,3,1]))[2],ori2.IPFcolor(np.array([0,3,1]))) - assert np.allclose( ori_vec.IPF_color(np.array([4,0,1]))[3],ori3.IPFcolor(np.array([4,0,1]))) From 49d448dcede646f8a231d4befc9da386fea01cd6 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 30 Jun 2020 21:43:57 +0200 Subject: [PATCH 60/78] vectorized and cleaned --- python/damask/_lattice.py | 260 ++++++------------------------- python/damask/_orientation.py | 67 ++------ python/damask/_result.py | 6 +- python/damask/_rotation.py | 2 +- python/tests/test_Lattice.py | 122 ++++++++++++++- python/tests/test_Orientation.py | 9 +- python/tests/test_Result.py | 6 +- python/tests/test_ori_vec.py | 38 ----- 8 files changed, 194 insertions(+), 316 deletions(-) diff --git a/python/damask/_lattice.py b/python/damask/_lattice.py index ee718adcd..74ef61a50 100644 --- a/python/damask/_lattice.py +++ b/python/damask/_lattice.py @@ -29,7 +29,7 @@ class Symmetry: raise KeyError(f'Crystal system "{system}" is unknown') self.system = system.lower() if isinstance(system,str) else system - self.lattice = self.system # for compatibility + self.lattice = self.system # ToDo: for compatibility def __copy__(self): @@ -82,85 +82,10 @@ class Symmetry: otherOrder = self.crystal_systems.index(other.system) return (myOrder > otherOrder) - (myOrder < otherOrder) - def symmetryOperations(self,members=[]): - """List (or single element) of symmetry operations as rotations.""" - if self.system == 'cubic': - symQuats = [ - [ 1.0, 0.0, 0.0, 0.0 ], - [ 0.0, 1.0, 0.0, 0.0 ], - [ 0.0, 0.0, 1.0, 0.0 ], - [ 0.0, 0.0, 0.0, 1.0 ], - [ 0.0, 0.0, 0.5*np.sqrt(2), 0.5*np.sqrt(2) ], - [ 0.0, 0.0, 0.5*np.sqrt(2),-0.5*np.sqrt(2) ], - [ 0.0, 0.5*np.sqrt(2), 0.0, 0.5*np.sqrt(2) ], - [ 0.0, 0.5*np.sqrt(2), 0.0, -0.5*np.sqrt(2) ], - [ 0.0, 0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0 ], - [ 0.0, -0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0 ], - [ 0.5, 0.5, 0.5, 0.5 ], - [-0.5, 0.5, 0.5, 0.5 ], - [-0.5, 0.5, 0.5, -0.5 ], - [-0.5, 0.5, -0.5, 0.5 ], - [-0.5, -0.5, 0.5, 0.5 ], - [-0.5, -0.5, 0.5, -0.5 ], - [-0.5, -0.5, -0.5, 0.5 ], - [-0.5, 0.5, -0.5, -0.5 ], - [-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], - [ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], - [-0.5*np.sqrt(2), 0.0, 0.5*np.sqrt(2), 0.0 ], - [-0.5*np.sqrt(2), 0.0, -0.5*np.sqrt(2), 0.0 ], - [-0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0, 0.0 ], - [-0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0, 0.0 ], - ] - elif self.system == 'hexagonal': - symQuats = [ - [ 1.0, 0.0, 0.0, 0.0 ], - [-0.5*np.sqrt(3), 0.0, 0.0, -0.5 ], - [ 0.5, 0.0, 0.0, 0.5*np.sqrt(3) ], - [ 0.0, 0.0, 0.0, 1.0 ], - [-0.5, 0.0, 0.0, 0.5*np.sqrt(3) ], - [-0.5*np.sqrt(3), 0.0, 0.0, 0.5 ], - [ 0.0, 1.0, 0.0, 0.0 ], - [ 0.0, -0.5*np.sqrt(3), 0.5, 0.0 ], - [ 0.0, 0.5, -0.5*np.sqrt(3), 0.0 ], - [ 0.0, 0.0, 1.0, 0.0 ], - [ 0.0, -0.5, -0.5*np.sqrt(3), 0.0 ], - [ 0.0, 0.5*np.sqrt(3), 0.5, 0.0 ], - ] - elif self.system == 'tetragonal': - symQuats = [ - [ 1.0, 0.0, 0.0, 0.0 ], - [ 0.0, 1.0, 0.0, 0.0 ], - [ 0.0, 0.0, 1.0, 0.0 ], - [ 0.0, 0.0, 0.0, 1.0 ], - [ 0.0, 0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0 ], - [ 0.0, -0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0 ], - [ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], - [-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], - ] - elif self.system == 'orthorhombic': - symQuats = [ - [ 1.0,0.0,0.0,0.0 ], - [ 0.0,1.0,0.0,0.0 ], - [ 0.0,0.0,1.0,0.0 ], - [ 0.0,0.0,0.0,1.0 ], - ] - else: - symQuats = [ - [ 1.0,0.0,0.0,0.0 ], - ] - - symOps = list(map(Rotation, - np.array(symQuats)[np.atleast_1d(members) if members != [] else range(len(symQuats))])) - try: - iter(members) # asking for (even empty) list of members? - except TypeError: - return symOps[0] # no, return rotation object - else: - return symOps # yes, return list of rotations @property def symmetry_operations(self): - """Symmetry operations as Rotations.""" + """Symmetry operations as Quaternions.""" if self.system == 'cubic': symQuats = [ [ 1.0, 0.0, 0.0, 0.0 ], @@ -228,42 +153,40 @@ class Symmetry: return np.array(symQuats) - def inFZ(self,rodrigues): + def in_FZ(self,rho): """ - Check whether given Rodrigues-Frank vector falls into fundamental zone of own symmetry. + Check whether given Rodrigues-Frank vector falls into fundamental zone. Fundamental zone in Rodrigues space is point symmetric around origin. """ - if (len(rodrigues) != 3): - raise ValueError('Input is not a Rodrigues-Frank vector.\n') + if(rho.shape[-1] != 3): + raise ValueError('Input is not a Rodrigues-Frank vector.') - if np.any(rodrigues == np.inf): return False # ToDo: MD: not sure if needed + rho_abs = np.abs(rho) - Rabs = abs(rodrigues) - - if self.system == 'cubic': - return np.sqrt(2.0)-1.0 >= Rabs[0] \ - and np.sqrt(2.0)-1.0 >= Rabs[1] \ - and np.sqrt(2.0)-1.0 >= Rabs[2] \ - and 1.0 >= Rabs[0] + Rabs[1] + Rabs[2] - elif self.system == 'hexagonal': - return 1.0 >= Rabs[0] and 1.0 >= Rabs[1] and 1.0 >= Rabs[2] \ - and 2.0 >= np.sqrt(3)*Rabs[0] + Rabs[1] \ - and 2.0 >= np.sqrt(3)*Rabs[1] + Rabs[0] \ - and 2.0 >= np.sqrt(3) + Rabs[2] - elif self.system == 'tetragonal': - return 1.0 >= Rabs[0] and 1.0 >= Rabs[1] \ - and np.sqrt(2.0) >= Rabs[0] + Rabs[1] \ - and np.sqrt(2.0) >= Rabs[2] + 1.0 - elif self.system == 'orthorhombic': - return 1.0 >= Rabs[0] and 1.0 >= Rabs[1] and 1.0 >= Rabs[2] - else: - return True + with np.errstate(invalid='ignore'): + # using '*'/prod for 'and' + if self.system == 'cubic': + return np.where(np.prod(np.sqrt(2)-1. >= rho_abs,axis=-1) * \ + (1. >= np.sum(rho_abs,axis=-1)),True,False) + elif self.system == 'hexagonal': + return np.where(np.prod(1. >= rho_abs,axis=-1) * \ + (2. >= np.sqrt(3)*rho_abs[...,0] + rho_abs[...,1]) * \ + (2. >= np.sqrt(3)*rho_abs[...,1] + rho_abs[...,0]) * \ + (2. >= np.sqrt(3) + rho_abs[...,2]),True,False) + elif self.system == 'tetragonal': + return np.where(np.prod(1. >= rho_abs[...,:2],axis=-1) * \ + (np.sqrt(2) >= rho_abs[...,0] + rho_abs[...,1]) * \ + (np.sqrt(2) >= rho_abs[...,2] + 1.),True,False) + elif self.system == 'orthorhombic': + return np.where(np.prod(1. >= rho_abs,axis=-1),True,False) + else: + return np.where(np.all(np.isfinite(rho_abs),axis=-1),True,False) - def inDisorientationSST(self,rodrigues): + def in_disorientation_SST(self,rho): """ - Check whether given Rodrigues-Frank vector (of misorientation) falls into standard stereographic triangle of own symmetry. + Check whether given Rodrigues-Frank vector (of misorientation) falls into standard stereographic triangle. References ---------- @@ -271,115 +194,32 @@ class Symmetry: https://doi.org/10.1107/S0108767391006864 """ - if (len(rodrigues) != 3): - raise ValueError('Input is not a Rodrigues-Frank vector.\n') - R = rodrigues + if(rho.shape[-1] != 3): + raise ValueError('Input is not a Rodrigues-Frank vector.') - epsilon = 0.0 - if self.system == 'cubic': - return R[0] >= R[1]+epsilon and R[1] >= R[2]+epsilon and R[2] >= epsilon - elif self.system == 'hexagonal': - return R[0] >= np.sqrt(3)*(R[1]-epsilon) and R[1] >= epsilon and R[2] >= epsilon - elif self.system == 'tetragonal': - return R[0] >= R[1]-epsilon and R[1] >= epsilon and R[2] >= epsilon - elif self.system == 'orthorhombic': - return R[0] >= epsilon and R[1] >= epsilon and R[2] >= epsilon - else: - return True - - - def inSST(self, - vector, - proper = False, - color = False): - """ - Check whether given vector falls into standard stereographic triangle of own symmetry. - - proper considers only vectors with z >= 0, hence uses two neighboring SSTs. - Return inverse pole figure color if requested. - Bases are computed from - - >>> basis = {'cubic' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,1.]/np.sqrt(2.), # direction of green - ... [1.,1.,1.]/np.sqrt(3.)]).T), # direction of blue - ... 'hexagonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,0.], # direction of green - ... [np.sqrt(3.),1.,0.]/np.sqrt(4.)]).T), # direction of blue - ... 'tetragonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,0.], # direction of green - ... [1.,1.,0.]/np.sqrt(2.)]).T), # direction of blue - ... 'orthorhombic': np.linalg.inv(np.array([[0.,0.,1.], # direction of red - ... [1.,0.,0.], # direction of green - ... [0.,1.,0.]]).T), # direction of blue - ... } - - """ - if self.system == 'cubic': - basis = {'improper':np.array([ [-1. , 0. , 1. ], - [ np.sqrt(2.) , -np.sqrt(2.) , 0. ], - [ 0. , np.sqrt(3.) , 0. ] ]), - 'proper':np.array([ [ 0. , -1. , 1. ], - [-np.sqrt(2.) , np.sqrt(2.) , 0. ], - [ np.sqrt(3.) , 0. , 0. ] ]), - } - elif self.system == 'hexagonal': - basis = {'improper':np.array([ [ 0. , 0. , 1. ], - [ 1. , -np.sqrt(3.) , 0. ], - [ 0. , 2. , 0. ] ]), - 'proper':np.array([ [ 0. , 0. , 1. ], - [-1. , np.sqrt(3.) , 0. ], - [ np.sqrt(3.) , -1. , 0. ] ]), - } - elif self.system == 'tetragonal': - basis = {'improper':np.array([ [ 0. , 0. , 1. ], - [ 1. , -1. , 0. ], - [ 0. , np.sqrt(2.) , 0. ] ]), - 'proper':np.array([ [ 0. , 0. , 1. ], - [-1. , 1. , 0. ], - [ np.sqrt(2.) , 0. , 0. ] ]), - } - elif self.system == 'orthorhombic': - basis = {'improper':np.array([ [ 0., 0., 1.], - [ 1., 0., 0.], - [ 0., 1., 0.] ]), - 'proper':np.array([ [ 0., 0., 1.], - [-1., 0., 0.], - [ 0., 1., 0.] ]), - } - else: # direct exit for unspecified symmetry - if color: - return (True,np.zeros(3,'d')) + with np.errstate(invalid='ignore'): + # using '*' for 'and' + if self.system == 'cubic': + return np.where((rho[...,0] >= rho[...,1]) * \ + (rho[...,1] >= rho[...,2]) * \ + (rho[...,2] >= 0),True,False) + elif self.system == 'hexagonal': + return np.where((rho[...,0] >= rho[...,1]*np.sqrt(3)) * \ + (rho[...,1] >= 0) * \ + (rho[...,2] >= 0),True,False) + elif self.system == 'tetragonal': + return np.where((rho[...,0] >= rho[...,1]) * \ + (rho[...,1] >= 0) * \ + (rho[...,2] >= 0),True,False) + elif self.system == 'orthorhombic': + return np.where((rho[...,0] >= 0) * \ + (rho[...,1] >= 0) * \ + (rho[...,2] >= 0),True,False) else: - return True - - v = np.array(vector,dtype=float) - if proper: # check both improper ... - theComponents = np.around(np.dot(basis['improper'],v),12) - inSST = np.all(theComponents >= 0.0) - if not inSST: # ... and proper SST - theComponents = np.around(np.dot(basis['proper'],v),12) - inSST = np.all(theComponents >= 0.0) - else: - v[2] = abs(v[2]) # z component projects identical - theComponents = np.around(np.dot(basis['improper'],v),12) # for positive and negative values - inSST = np.all(theComponents >= 0.0) - - if color: # have to return color array - if inSST: - rgb = np.power(theComponents/np.linalg.norm(theComponents),0.5) # smoothen color ramps - rgb = np.minimum(np.ones(3,dtype=float),rgb) # limit to maximum intensity - rgb /= max(rgb) # normalize to (HS)V = 1 - else: - rgb = np.zeros(3,dtype=float) - return (inSST,rgb) - else: - return inSST + return np.ones_like(rho[...,0],dtype=bool) - def in_SST(self, - vector, - proper = False, - color = False): + def in_SST(self,vector,proper=False,color=False): """ Check whether given vector falls into standard stereographic triangle of own symmetry. @@ -503,7 +343,7 @@ class Lattice: # ToDo: Make a subclass of Symmetry! # transition to subclass self.system = self.symmetry.system self.in_SST = self.symmetry.in_SST - self.inFZ = self.symmetry.inFZ + self.in_FZ = self.symmetry.in_FZ def __repr__(self): diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 7d0f11c49..b6b24e951 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -40,8 +40,6 @@ class Orientation: # ToDo: make subclass of lattice and Rotation self.rotation = Rotation.from_quaternion(rotation) # assume quaternion def __getitem__(self,item): - if isinstance(item,tuple) and len(item) >= len(self): - raise IndexError('Too many indices') return self.__class__(self.rotation[item],self.lattice) @@ -61,8 +59,8 @@ class Orientation: # ToDo: make subclass of lattice and Rotation if self.lattice.symmetry != other.lattice.symmetry: raise NotImplementedError('disorientation between different symmetry classes not supported yet.') - mySymEqs = self.equivalentOrientations() if SST else self.equivalentOrientations([0]) # take all or only first sym operation - otherSymEqs = other.equivalentOrientations() + mySymEqs = self.equivalent if SST else self.equivalent[0] # take all or only first sym operation + otherSymEqs = other.equivalent for i,sA in enumerate(mySymEqs): aInv = sA.rotation.inversed() @@ -71,7 +69,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation r = b*aInv for k in range(2): r.inverse() - breaker = self.lattice.inFZ(r.as_Rodrigues(vector=True)) \ + breaker = self.in_FZ \ and (not SST or other.lattice.symmetry.inDisorientationSST(r.as_Rodrigues(vector=True))) if breaker: break if breaker: break @@ -81,17 +79,10 @@ class Orientation: # ToDo: make subclass of lattice and Rotation # ... own sym, other sym, # self-->other: True, self<--other: False - def inFZ_vec(self): + + def in_FZ(self): """Check if orientations fall into Fundamental Zone.""" - if not self.rotation.shape: - return self.lattice.inFZ(self.rotation.as_Rodrigues(vector=True)) - else: - return [self.lattice.inFZ(\ - self.rotation.as_Rodrigues(vector=True)[l]) for l in range(self.rotation.shape[0])] - - - def inFZ(self): - return self.lattice.inFZ(self.rotation.as_Rodrigues(vector=True)) + return self.lattice.in_FZ(self.rotation.as_Rodrigues(vector=True)) @property def equivalent(self): @@ -112,16 +103,6 @@ class Orientation: # ToDo: make subclass of lattice and Rotation return self.__class__(s@r,self.lattice) - def equivalentOrientations(self,members=[]): - """List of orientations which are symmetrically equivalent.""" - try: - iter(members) # asking for (even empty) list of members? - except TypeError: - return self.__class__(self.lattice.symmetry.symmetryOperations(members)*self.rotation,self.lattice) # no, return rotation object - else: - return [self.__class__(q*self.rotation,self.lattice) \ - for q in self.lattice.symmetry.symmetryOperations(members)] # yes, return list of rotations - def relatedOrientations_vec(self,model): """List of orientations related by the given orientation relationship.""" h = self.lattice.relationOperations(model) @@ -149,7 +130,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation r= 1 if not self.rotation.shape else equi.shape[1] #number of rotations num_equi=equi.shape[0] #number of equivalente orientations quat= np.reshape( equi.as_quaternion(), (r*num_equi,4) ,order='F') #equivalents are listed in intiuitive order - boolean=Orientation(quat, self.lattice).inFZ_vec() #check which ones are in FZ + boolean=Orientation(quat, self.lattice).in_FZ() #check which ones are in FZ if sum(boolean) == r: return self.__class__(quat[boolean],self.lattice) @@ -162,13 +143,10 @@ class Orientation: # ToDo: make subclass of lattice and Rotation return self.__class__(quat[index],self.lattice) - - - def reduced(self): """Transform orientation to fall into fundamental zone according to symmetry.""" - for me in self.equivalentOrientations(): - if self.lattice.inFZ(me.rotation.as_Rodrigues(vector=True)): break + for me in self.equivalent: + if self.lattice.in_FZ(me.rotation.as_Rodrigues(vector=True)): break return self.__class__(me.rotation,self.lattice) @@ -179,35 +157,23 @@ class Orientation: # ToDo: make subclass of lattice and Rotation SST = True): """Axis rotated according to orientation (using crystal symmetry to ensure location falls into SST).""" if SST: # pole requested to be within SST - for i,o in enumerate(self.equivalentOrientations()): # test all symmetric equivalent quaternions + for i,o in enumerate(self.equivalent): # test all symmetric equivalent quaternions pole = o.rotation@axis # align crystal direction to axis - if self.lattice.symmetry.inSST(pole,proper): break # found SST version + if self.lattice.in_SST(pole,proper): break # found SST version else: pole = self.rotation@axis # align crystal direction to axis return (pole,i if SST else 0) - def IPFcolor(self,axis): + def IPF_color(self,axis): #ToDo axis or direction? """TSL color of inverse pole figure for given axis.""" - color = np.zeros(3,'d') - - for o in self.equivalent: - pole = o.rotation@axis # align crystal direction to axis - inSST,color = self.lattice.symmetry.inSST(pole,color=True) - if inSST: break - - return color - - - def IPF_color(self,axis): - """TSL color of inverse pole figure for given axis. Not for hex or triclinic lattices.""" eq = self.equivalent pole = eq.rotation @ np.broadcast_to(axis/np.linalg.norm(axis),eq.rotation.shape+(3,)) in_SST, color = self.lattice.in_SST(pole,color=True) # ignore duplicates (occur for highly symmetric orientations) - found = np.zeros_like(in_SST[1],dtype=bool) + found = np.zeros_like(in_SST[0],dtype=bool) c = np.empty(color.shape[1:]) for s in range(in_SST.shape[0]): c = np.where(np.expand_dims(np.logical_and(in_SST[s],~found),-1),color[s],c) @@ -220,17 +186,18 @@ class Orientation: # ToDo: make subclass of lattice and Rotation def fromAverage(orientations, weights = []): """Create orientation from average of list of orientations.""" - # further read: Orientation distribution analysis in deformed grains, https://doi.org/10.1107/S0021889801003077 + # further read: Orientation distribution analysis in deformed grains + # https://doi.org/10.1107/S0021889801003077 if not all(isinstance(item, Orientation) for item in orientations): raise TypeError("Only instances of Orientation can be averaged.") closest = [] ref = orientations[0] for o in orientations: - closest.append(o.equivalentOrientations( + closest.append(o.equivalent[ ref.disorientation(o, SST = False, # select (o[ther]'s) sym orientation - symmetries = True)[2]).rotation) # with lowest misorientation + symmetries = True)[2]].rotation) # with lowest misorientation return Orientation(Rotation.fromAverage(closest,weights),ref.lattice) diff --git a/python/damask/_result.py b/python/damask/_result.py index e6dac9370..1396b560d 100644 --- a/python/damask/_result.py +++ b/python/damask/_result.py @@ -733,7 +733,7 @@ class Result: @staticmethod - def _add_IPFcolor(q,l): + def _add_IPF_color(q,l): m = util.scale_to_coprime(np.array(l)) o = Orientation(Rotation(rfn.structured_to_unstructured(q['data'])), @@ -749,7 +749,7 @@ class Result: 'Creator': inspect.stack()[0][3][1:] } } - def add_IPFcolor(self,q,l): + def add_IPF_color(self,q,l): """ Add RGB color tuple of inverse pole figure (IPF) color. @@ -761,7 +761,7 @@ class Result: Lab frame direction for inverse pole figure. """ - self._add_generic_pointwise(self._add_IPFcolor,{'q':q},{'l':l}) + self._add_generic_pointwise(self._add_IPF_color,{'q':q},{'l':l}) @staticmethod diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 686b144ae..accd453cc 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -470,7 +470,7 @@ class Rotation: elif hasattr(shape, '__iter__'): r = np.random.random(tuple(shape)+(3,)) else: - r = np.random.random((shape,3)) + r = np.random.rand(shape,3) A = np.sqrt(r[...,2]) B = np.sqrt(1.0-r[...,2]) diff --git a/python/tests/test_Lattice.py b/python/tests/test_Lattice.py index 3d9c35706..8e4616660 100644 --- a/python/tests/test_Lattice.py +++ b/python/tests/test_Lattice.py @@ -3,10 +3,118 @@ import random import pytest import numpy as np +from damask import Orientation +from damask import Rotation from damask import Symmetry +def in_FZ(system,rho): + """Non-vectorized version of 'in_FZ'.""" + rho_abs = abs(rho) + + if system == 'cubic': + return np.sqrt(2.0)-1.0 >= rho_abs[0] \ + and np.sqrt(2.0)-1.0 >= rho_abs[1] \ + and np.sqrt(2.0)-1.0 >= rho_abs[2] \ + and 1.0 >= rho_abs[0] + rho_abs[1] + rho_abs[2] + elif system == 'hexagonal': + return 1.0 >= rho_abs[0] and 1.0 >= rho_abs[1] and 1.0 >= rho_abs[2] \ + and 2.0 >= np.sqrt(3)*rho_abs[0] + rho_abs[1] \ + and 2.0 >= np.sqrt(3)*rho_abs[1] + rho_abs[0] \ + and 2.0 >= np.sqrt(3) + rho_abs[2] + elif system == 'tetragonal': + return 1.0 >= rho_abs[0] and 1.0 >= rho_abs[1] \ + and np.sqrt(2.0) >= rho_abs[0] + rho_abs[1] \ + and np.sqrt(2.0) >= rho_abs[2] + 1.0 + elif system == 'orthorhombic': + return 1.0 >= rho_abs[0] and 1.0 >= rho_abs[1] and 1.0 >= rho_abs[2] + else: + return np.all(np.isfinite(rho_abs)) + + +def in_disorientation_SST(system,rho): + """Non-vectorized version of 'in_Disorientation_SST'.""" + epsilon = 0.0 + if system == 'cubic': + return rho[0] >= rho[1]+epsilon and rho[1] >= rho[2]+epsilon and rho[2] >= epsilon + elif system == 'hexagonal': + return rho[0] >= np.sqrt(3)*(rho[1]-epsilon) and rho[1] >= epsilon and rho[2] >= epsilon + elif system == 'tetragonal': + return rho[0] >= rho[1]-epsilon and rho[1] >= epsilon and rho[2] >= epsilon + elif system == 'orthorhombic': + return rho[0] >= epsilon and rho[1] >= epsilon and rho[2] >= epsilon + else: + return True + + +def in_SST(system,vector,proper = False): + """Non-vectorized version of 'in_SST'.""" + if system == 'cubic': + basis = {'improper':np.array([ [-1. , 0. , 1. ], + [ np.sqrt(2.) , -np.sqrt(2.) , 0. ], + [ 0. , np.sqrt(3.) , 0. ] ]), + 'proper':np.array([ [ 0. , -1. , 1. ], + [-np.sqrt(2.) , np.sqrt(2.) , 0. ], + [ np.sqrt(3.) , 0. , 0. ] ]), + } + elif system == 'hexagonal': + basis = {'improper':np.array([ [ 0. , 0. , 1. ], + [ 1. , -np.sqrt(3.) , 0. ], + [ 0. , 2. , 0. ] ]), + 'proper':np.array([ [ 0. , 0. , 1. ], + [-1. , np.sqrt(3.) , 0. ], + [ np.sqrt(3.) , -1. , 0. ] ]), + } + elif system == 'tetragonal': + basis = {'improper':np.array([ [ 0. , 0. , 1. ], + [ 1. , -1. , 0. ], + [ 0. , np.sqrt(2.) , 0. ] ]), + 'proper':np.array([ [ 0. , 0. , 1. ], + [-1. , 1. , 0. ], + [ np.sqrt(2.) , 0. , 0. ] ]), + } + elif system == 'orthorhombic': + basis = {'improper':np.array([ [ 0., 0., 1.], + [ 1., 0., 0.], + [ 0., 1., 0.] ]), + 'proper':np.array([ [ 0., 0., 1.], + [-1., 0., 0.], + [ 0., 1., 0.] ]), + } + else: + return True + + v = np.array(vector,dtype=float) + if proper: + theComponents = np.around(np.dot(basis['improper'],v),12) + inSST = np.all(theComponents >= 0.0) + if not inSST: + theComponents = np.around(np.dot(basis['proper'],v),12) + inSST = np.all(theComponents >= 0.0) + else: + v[2] = abs(v[2]) + theComponents = np.around(np.dot(basis['improper'],v),12) + inSST = np.all(theComponents >= 0.0) + + return inSST + + +@pytest.fixture +def set_of_rodrigues(set_of_quaternions): + return Rotation(set_of_quaternions).as_Rodrigues(vector=True)[:200] + class TestSymmetry: + @pytest.mark.parametrize('system',Symmetry.crystal_systems) + def test_in_FZ_vectorize(self,set_of_rodrigues,system): + for i,in_FZ_ in enumerate(Symmetry(system).in_FZ(set_of_rodrigues)): + assert in_FZ_ == in_FZ(system,set_of_rodrigues[i]) + + @pytest.mark.parametrize('system',Symmetry.crystal_systems) + def test_in_disorientation_SST_vectorize(self,set_of_rodrigues,system): + for i,in_disorientation_SST_ in enumerate(Symmetry(system).in_disorientation_SST(set_of_rodrigues)): + assert in_disorientation_SST_ == in_disorientation_SST(system,set_of_rodrigues[i]) + + @pytest.mark.parametrize('invalid_symmetry',['fcc','bcc','hello']) def test_invalid_symmetry(self,invalid_symmetry): with pytest.raises(KeyError): @@ -22,19 +130,19 @@ class TestSymmetry: assert Symmetry(symmetries[0]) != Symmetry(symmetries[1]) @pytest.mark.parametrize('system',Symmetry.crystal_systems) - def test_inFZ(self,system): - assert Symmetry(system).inFZ(np.zeros(3)) + def test_in_FZ(self,system): + assert Symmetry(system).in_FZ(np.zeros(3)) @pytest.mark.parametrize('system',Symmetry.crystal_systems) - def test_inDisorientationSST(self,system): - assert Symmetry(system).inDisorientationSST(np.zeros(3)) + def test_in_disorientation_SST(self,system): + assert Symmetry(system).in_disorientation_SST(np.zeros(3)) @pytest.mark.parametrize('system',Symmetry.crystal_systems) @pytest.mark.parametrize('proper',[True,False]) - def test_inSST(self,system,proper): - assert Symmetry(system).inSST(np.zeros(3),proper) + def test_in_SST(self,system,proper): + assert Symmetry(system).in_SST(np.zeros(3),proper) - @pytest.mark.parametrize('function',['inFZ','inDisorientationSST']) + @pytest.mark.parametrize('function',['in_FZ','in_disorientation_SST']) def test_invalid_argument(self,function): s = Symmetry() # noqa with pytest.raises(ValueError): diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index 750f6176d..f6f25e0a7 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -40,7 +40,7 @@ class TestOrientation: @pytest.mark.parametrize('lattice',Lattice.lattices) def test_IPF_equivalent(self,set_of_quaternions,lattice): direction = np.random.random(3)*2.0-1 - for ori in Orientation(Rotation(set_of_quaternions),lattice)[200]: + for ori in Orientation(Rotation(set_of_quaternions),lattice)[:200]: color = ori.IPF_color(direction) for equivalent in ori.equivalent: assert np.allclose(color,equivalent.IPF_color(direction)) @@ -48,9 +48,10 @@ class TestOrientation: @pytest.mark.parametrize('lattice',Lattice.lattices) def test_IPF_vectorize(self,set_of_quaternions,lattice): - for ori in Orientation(Rotation(set_of_quaternions),lattice)[200]: - direction = np.random.random(3)*2.0-1 - assert np.allclose(ori.IPF_color(direction),IPF_color(ori,direction)) + direction = np.random.random(3)*2.0-1 + oris = Orientation(Rotation(set_of_quaternions),lattice)[:200] + for i,color in enumerate(oris.IPF_color(direction)): + assert np.allclose(color,IPF_color(oris[i],direction)) @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) diff --git a/python/tests/test_Result.py b/python/tests/test_Result.py index d7946e5e0..aec91db9f 100644 --- a/python/tests/test_Result.py +++ b/python/tests/test_Result.py @@ -153,8 +153,8 @@ class TestResult: assert np.allclose(in_memory,in_file) @pytest.mark.parametrize('d',[[1,0,0],[0,1,0],[0,0,1]]) - def test_add_IPFcolor(self,default,d): - default.add_IPFcolor('orientation',d) + def test_add_IPF_color(self,default,d): + default.add_IPF_color('orientation',d) loc = {'orientation': default.get_dataset_location('orientation'), 'color': default.get_dataset_location('IPFcolor_[{} {} {}]'.format(*d))} qu = default.read_dataset(loc['orientation']).view(np.double).reshape(-1,4) @@ -162,7 +162,7 @@ class TestResult: in_memory = np.empty((qu.shape[0],3),np.uint8) for i,q in enumerate(qu): o = damask.Orientation(q,crystal_structure).reduced() - in_memory[i] = np.uint8(o.IPFcolor(np.array(d))*255) + in_memory[i] = np.uint8(o.IPF_color(np.array(d))*255) in_file = default.read_dataset(loc['color']) assert np.allclose(in_memory,in_file) diff --git a/python/tests/test_ori_vec.py b/python/tests/test_ori_vec.py index 772096201..7cb465046 100644 --- a/python/tests/test_ori_vec.py +++ b/python/tests/test_ori_vec.py @@ -16,44 +16,6 @@ rot3= Rotation.from_random() #average class TestOrientation_vec: - #@pytest.mark.xfail - @pytest.mark.parametrize('lattice',Lattice.lattices) - def test_equivalent_vec(self,lattice): - ori0=Orientation(rot0,lattice) - ori1=Orientation(rot1,lattice) - ori2=Orientation(rot2,lattice) - ori3=Orientation(rot3,lattice) - - quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),rot2.as_quaternion(),rot3.as_quaternion()]) - ori_vec=Orientation(quat,lattice) - - for s in range(len(ori_vec.lattice.symmetry.symmetryOperations())): - assert all(ori_vec.equivalent.rotation.as_Eulers()[s,0] == \ - ori0.equivalent[s].rotation.as_Eulers()) - assert all(ori_vec.equivalent.rotation.as_quaternion()[s,1] == \ - ori1.equivalent[s].rotation.as_quaternion()) - assert all(ori_vec.equivalent.rotation.as_Rodrigues()[s,2] == \ - ori2.equivalent[s].rotation.as_Rodrigues()) - assert all(ori_vec.equivalent.rotation.as_cubochoric()[s,3] == \ - ori3.equivalent[s].rotation.as_cubochoric()) - - @pytest.mark.parametrize('lattice',Lattice.lattices) - def test_inFZ_vec(self,lattice): - ori0=Orientation(rot0,lattice) - ori1=Orientation(rot1,lattice) - ori2=Orientation(rot2,lattice) - ori3=Orientation(rot3,lattice) - ori4=ori0.reduced() ; rot4=ori4.rotation #ensure 1 of them is in FZ - - quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),\ - rot2.as_quaternion(),rot3.as_quaternion(), rot4.as_quaternion()]) - ori_vec=Orientation(quat,lattice) - - assert ori_vec.inFZ_vec()[0] == ori0.inFZ() - assert ori_vec.inFZ_vec()[1] == ori1.inFZ() - assert ori_vec.inFZ_vec()[2] == ori2.inFZ() - assert ori_vec.inFZ_vec()[3] == ori3.inFZ() - assert ori_vec.inFZ_vec()[4] == ori4.inFZ() @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) From ef0c78745a1b7146bac19c71fd285608da47c194 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 30 Jun 2020 22:33:58 +0200 Subject: [PATCH 61/78] fix for vectorized in_SST + test --- python/damask/_lattice.py | 14 +++++++++----- python/damask/_orientation.py | 1 - python/tests/test_Lattice.py | 19 ++++++++++++++----- python/tests/test_Result.py | 2 +- python/tests/test_Rotation.py | 2 +- 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/python/damask/_lattice.py b/python/damask/_lattice.py index 74ef61a50..a337aa1a1 100644 --- a/python/damask/_lattice.py +++ b/python/damask/_lattice.py @@ -160,7 +160,7 @@ class Symmetry: Fundamental zone in Rodrigues space is point symmetric around origin. """ if(rho.shape[-1] != 3): - raise ValueError('Input is not a Rodrigues-Frank vector.') + raise ValueError('Input is not a Rodrigues-Frank vector field.') rho_abs = np.abs(rho) @@ -195,7 +195,7 @@ class Symmetry: """ if(rho.shape[-1] != 3): - raise ValueError('Input is not a Rodrigues-Frank vector.') + raise ValueError('Input is not a Rodrigues-Frank vector field.') with np.errstate(invalid='ignore'): # using '*' for 'and' @@ -242,6 +242,9 @@ class Symmetry: ... } """ + if(vector.shape[-1] != 3): + raise ValueError('Input is not a 3D vector field.') + if self.system == 'cubic': basis = {'improper':np.array([ [-1. , 0. , 1. ], [ np.sqrt(2.) , -np.sqrt(2.) , 0. ], @@ -280,16 +283,17 @@ class Symmetry: else: return np.ones_like(vector[...,0],bool) - b_p = np.broadcast_to(basis['proper'], vector.shape+(3,)) + + b_i = np.broadcast_to(basis['improper'],vector.shape+(3,)) if proper: - b_i = np.broadcast_to(basis['improper'],vector.shape+(3,)) + b_p = np.broadcast_to(basis['proper'], vector.shape+(3,)) improper = np.all(np.around(np.einsum('...ji,...i',b_i,vector),12)>=0.0,axis=-1,keepdims=True) theComponents = np.where(np.broadcast_to(improper,vector.shape), np.around(np.einsum('...ji,...i',b_i,vector),12), np.around(np.einsum('...ji,...i',b_p,vector),12)) else: vector_ = np.block([vector[...,0:2],np.abs(vector[...,2:3])]) # z component projects identical - theComponents = np.around(np.einsum('...ji,...i',b_p,vector_),12) + theComponents = np.around(np.einsum('...ji,...i',b_i,vector_),12) in_SST = np.all(theComponents >= 0.0,axis=-1) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index b6b24e951..c59ababda 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -79,7 +79,6 @@ class Orientation: # ToDo: make subclass of lattice and Rotation # ... own sym, other sym, # self-->other: True, self<--other: False - def in_FZ(self): """Check if orientations fall into Fundamental Zone.""" return self.lattice.in_FZ(self.rotation.as_Rodrigues(vector=True)) diff --git a/python/tests/test_Lattice.py b/python/tests/test_Lattice.py index 8e4616660..f53c3ad03 100644 --- a/python/tests/test_Lattice.py +++ b/python/tests/test_Lattice.py @@ -106,14 +106,23 @@ class TestSymmetry: @pytest.mark.parametrize('system',Symmetry.crystal_systems) def test_in_FZ_vectorize(self,set_of_rodrigues,system): - for i,in_FZ_ in enumerate(Symmetry(system).in_FZ(set_of_rodrigues)): - assert in_FZ_ == in_FZ(system,set_of_rodrigues[i]) + result = Symmetry(system).in_FZ(set_of_rodrigues.reshape(50,4,3)).reshape(200) + for i,r in enumerate(result): + assert r == in_FZ(system,set_of_rodrigues[i]) @pytest.mark.parametrize('system',Symmetry.crystal_systems) def test_in_disorientation_SST_vectorize(self,set_of_rodrigues,system): - for i,in_disorientation_SST_ in enumerate(Symmetry(system).in_disorientation_SST(set_of_rodrigues)): - assert in_disorientation_SST_ == in_disorientation_SST(system,set_of_rodrigues[i]) + result = Symmetry(system).in_disorientation_SST(set_of_rodrigues.reshape(50,4,3)).reshape(200) + for i,r in enumerate(result): + assert r == in_disorientation_SST(system,set_of_rodrigues[i]) + @pytest.mark.parametrize('proper',[True,False]) + @pytest.mark.parametrize('system',Symmetry.crystal_systems) + def test_in_SST_vectorize(self,system,proper): + vecs = np.random.rand(20,4,3) + result = Symmetry(system).in_SST(vecs,proper).reshape(20*4) + for i,r in enumerate(result): + assert r == in_SST(system,vecs.reshape(20*4,3)[i],proper) @pytest.mark.parametrize('invalid_symmetry',['fcc','bcc','hello']) def test_invalid_symmetry(self,invalid_symmetry): @@ -142,7 +151,7 @@ class TestSymmetry: def test_in_SST(self,system,proper): assert Symmetry(system).in_SST(np.zeros(3),proper) - @pytest.mark.parametrize('function',['in_FZ','in_disorientation_SST']) + @pytest.mark.parametrize('function',['in_FZ','in_disorientation_SST','in_SST']) def test_invalid_argument(self,function): s = Symmetry() # noqa with pytest.raises(ValueError): diff --git a/python/tests/test_Result.py b/python/tests/test_Result.py index aec91db9f..b27a3d5f3 100644 --- a/python/tests/test_Result.py +++ b/python/tests/test_Result.py @@ -319,4 +319,4 @@ class TestResult: def test_XDMF(self,tmp_path,single_phase): os.chdir(tmp_path) - single_phase.write_XDMF + single_phase.write_XDMF() diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 20d01af4a..b4166bf1b 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -20,7 +20,7 @@ def set_of_rotations(set_of_quaternions): #################################################################################################### -# Code below available according to the following conditions on https://github.com/MarDiehl/3Drotations +# Code below available according to the following conditions #################################################################################################### # Copyright (c) 2017-2019, Martin Diehl/Max-Planck-Institut für Eisenforschung GmbH # Copyright (c) 2013-2014, Marc De Graef/Carnegie Mellon University From 23365660d823f432c12e95ec9b9aebd7653ece22 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 30 Jun 2020 23:17:50 +0200 Subject: [PATCH 62/78] polishing --- python/damask/_orientation.py | 1 + python/damask/_rotation.py | 14 +++++++------- python/tests/test_Lattice.py | 1 - 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index c59ababda..cb28c160e 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -40,6 +40,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation self.rotation = Rotation.from_quaternion(rotation) # assume quaternion def __getitem__(self,item): + """Iterate over leading/leftmost dimension of Orientation array.""" return self.__class__(self.rotation[item],self.lattice) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index accd453cc..26fd3e261 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -81,17 +81,16 @@ class Rotation: ]) - def __len__(self): - return 0 if self.shape == () else len(self.shape) - - def __getitem__(self,item): + """Iterate over leading/leftmost dimension of Rotation array.""" + if self.shape == (): return self.copy() if isinstance(item,tuple) and len(item) >= len(self): raise IndexError('Too many indices') return self.__class__(self.quaternion[item]) def __len__(self): + """Length of leading/leftmost dimension of Rotation array.""" return 0 if self.shape == () else self.shape[0] @@ -104,9 +103,10 @@ class Rotation: other : numpy.ndarray or Rotation Vector, second or fourth order tensor, or rotation object that is rotated. - Todo - ---- - Check rotation of 4th order tensor + Returns + ------- + other_rot : numpy.ndarray or Rotation + Rotated vector, second or fourth order tensor, or rotation object. """ if isinstance(other, Rotation): diff --git a/python/tests/test_Lattice.py b/python/tests/test_Lattice.py index f53c3ad03..aa201c645 100644 --- a/python/tests/test_Lattice.py +++ b/python/tests/test_Lattice.py @@ -3,7 +3,6 @@ import random import pytest import numpy as np -from damask import Orientation from damask import Rotation from damask import Symmetry From de8e9b5fc1b6b7305787f15b9c8666260aa09929 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 1 Jul 2020 00:37:02 +0200 Subject: [PATCH 63/78] fast reduced operation --- python/damask/_orientation.py | 50 ++++++++++++++------------------ python/tests/test_Orientation.py | 7 +++++ python/tests/test_Result.py | 2 +- python/tests/test_ori_vec.py | 20 ------------- 4 files changed, 30 insertions(+), 49 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index cb28c160e..57407a6ce 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -3,7 +3,7 @@ import numpy as np from . import Lattice from . import Rotation -class Orientation: # ToDo: make subclass of lattice and Rotation +class Orientation: # ToDo: make subclass of lattice and Rotation? """ Crystallographic orientation. @@ -44,6 +44,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation return self.__class__(self.rotation[item],self.lattice) + # ToDo: Discuss vectorization/calling signature def disorientation(self, other, SST = True, @@ -80,17 +81,19 @@ class Orientation: # ToDo: make subclass of lattice and Rotation # ... own sym, other sym, # self-->other: True, self<--other: False + @property def in_FZ(self): """Check if orientations fall into Fundamental Zone.""" return self.lattice.in_FZ(self.rotation.as_Rodrigues(vector=True)) + @property def equivalent(self): """ Return orientations which are symmetrically equivalent. One dimension (length according to symmetrically equivalent orientations) - is added to the left of the rotation array. + is added to the left of the Rotation array. """ s = self.lattice.symmetry.symmetry_operations @@ -98,9 +101,8 @@ class Orientation: # ToDo: make subclass of lattice and Rotation s = Rotation(np.broadcast_to(s,s.shape[:1]+self.rotation.quaternion.shape)) r = np.broadcast_to(self.rotation.quaternion,s.shape[:1]+self.rotation.quaternion.shape) - r = Rotation(r) - return self.__class__(s@r,self.lattice) + return self.__class__(s@Rotation(r),self.lattice) def relatedOrientations_vec(self,model): @@ -123,34 +125,24 @@ class Orientation: # ToDo: make subclass of lattice and Rotation r = self.lattice.relationOperations(model) return [self.__class__(o*self.rotation,r['lattice']) for o in r['rotations']] + @property - def reduced_vec(self): - """Transform orientation to fall into fundamental zone according to symmetry.""" - equi= self.equivalent.rotation #equivalent orientations - r= 1 if not self.rotation.shape else equi.shape[1] #number of rotations - num_equi=equi.shape[0] #number of equivalente orientations - quat= np.reshape( equi.as_quaternion(), (r*num_equi,4) ,order='F') #equivalents are listed in intiuitive order - boolean=Orientation(quat, self.lattice).in_FZ() #check which ones are in FZ - if sum(boolean) == r: - return self.__class__(quat[boolean],self.lattice) - - else: - print('More than 1 equivalent orientation has been found for an orientation') - index=np.empty(r, dtype=int) - for l,h in enumerate(range(0,r*num_equi, num_equi)): - index[l]=np.where(boolean[h:h+num_equi])[0][0] + (l*num_equi) #get first index that is true then go check to next orientation - - return self.__class__(quat[index],self.lattice) - - def reduced(self): """Transform orientation to fall into fundamental zone according to symmetry.""" - for me in self.equivalent: - if self.lattice.in_FZ(me.rotation.as_Rodrigues(vector=True)): break + eq = self.equivalent + in_FZ = eq.in_FZ - return self.__class__(me.rotation,self.lattice) + # remove duplicates (occur for highly symmetric orientations) + found = np.zeros_like(in_FZ[0],dtype=bool) + q = self.rotation.quaternion[0] + for s in range(in_FZ.shape[0]): + q = np.where(np.expand_dims(np.logical_and(in_FZ[s],~found),-1),eq.rotation.quaternion[s],q) + found = np.logical_or(in_FZ[s],found) + + return self.__class__(q,self.lattice) + # ToDo: vectorize def inversePole(self, axis, proper = False, @@ -159,7 +151,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation if SST: # pole requested to be within SST for i,o in enumerate(self.equivalent): # test all symmetric equivalent quaternions pole = o.rotation@axis # align crystal direction to axis - if self.lattice.in_SST(pole,proper): break # found SST version + if self.lattice.in_SST(pole,proper): break # found SST version else: pole = self.rotation@axis # align crystal direction to axis @@ -172,7 +164,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation pole = eq.rotation @ np.broadcast_to(axis/np.linalg.norm(axis),eq.rotation.shape+(3,)) in_SST, color = self.lattice.in_SST(pole,color=True) - # ignore duplicates (occur for highly symmetric orientations) + # remove duplicates (occur for highly symmetric orientations) found = np.zeros_like(in_SST[0],dtype=bool) c = np.empty(color.shape[1:]) for s in range(in_SST.shape[0]): @@ -182,6 +174,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation return c + # ToDo: Discuss vectorization/calling signature @staticmethod def fromAverage(orientations, weights = []): @@ -202,6 +195,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation return Orientation(Rotation.fromAverage(closest,weights),ref.lattice) + # ToDo: Discuss vectorization/calling signature def average(self,other): """Calculate the average rotation.""" return Orientation.fromAverage([self,other]) diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index f6f25e0a7..78781e47d 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -54,6 +54,13 @@ class TestOrientation: assert np.allclose(color,IPF_color(oris[i],direction)) + @pytest.mark.parametrize('lattice',Lattice.lattices) + def test_reduced(self,set_of_quaternions,lattice): + oris = Orientation(Rotation(set_of_quaternions),lattice) + reduced = oris.reduced + assert np.all(reduced.in_FZ) and oris.rotation.shape == reduced.rotation.shape + + @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) @pytest.mark.parametrize('lattice',['fcc','bcc']) def test_relationship_forward_backward(self,model,lattice): diff --git a/python/tests/test_Result.py b/python/tests/test_Result.py index b27a3d5f3..676e94d50 100644 --- a/python/tests/test_Result.py +++ b/python/tests/test_Result.py @@ -161,7 +161,7 @@ class TestResult: crystal_structure = default.get_crystal_structure() in_memory = np.empty((qu.shape[0],3),np.uint8) for i,q in enumerate(qu): - o = damask.Orientation(q,crystal_structure).reduced() + o = damask.Orientation(q,crystal_structure).reduced in_memory[i] = np.uint8(o.IPF_color(np.array(d))*255) in_file = default.read_dataset(loc['color']) assert np.allclose(in_memory,in_file) diff --git a/python/tests/test_ori_vec.py b/python/tests/test_ori_vec.py index 7cb465046..9280af164 100644 --- a/python/tests/test_ori_vec.py +++ b/python/tests/test_ori_vec.py @@ -39,23 +39,3 @@ class TestOrientation_vec: ori2.relatedOrientations(model)[s].rotation.as_Rodrigues()) assert all(ori_vec.relatedOrientations_vec(model).rotation.as_cubochoric()[s,3] == \ ori3.relatedOrientations(model)[s].rotation.as_cubochoric()) - - @pytest.mark.parametrize('lattice',Lattice.lattices) - def test_reduced_vec(self,lattice): - ori0=Orientation(rot0,lattice) - ori1=Orientation(rot1,lattice) - ori2=Orientation(rot2,lattice) - ori3=Orientation(rot3,lattice) - #ensure 1 of them is in FZ - ori4=ori0.reduced() - rot4=ori4.rotation - - quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),\ - rot2.as_quaternion(),rot3.as_quaternion(), rot4.as_quaternion()]) - ori_vec=Orientation(quat,lattice) - - assert all(ori_vec.reduced_vec.rotation.as_Eulers()[0] == ori0.reduced().rotation.as_Eulers() ) - assert all(ori_vec.reduced_vec.rotation.as_quaternion()[1] == ori1.reduced().rotation.as_quaternion() ) - assert all(ori_vec.reduced_vec.rotation.as_Rodrigues()[2] == ori2.reduced().rotation.as_Rodrigues() ) - assert all(ori_vec.reduced_vec.rotation.as_cubochoric()[3] == ori3.reduced().rotation.as_cubochoric() ) - assert all(ori_vec.reduced_vec.rotation.as_axis_angle()[4] == ori4.reduced().rotation.as_axis_angle() ) From e18a5b8a1b2d75c470b880e3e83ad923509e6450 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 1 Jul 2020 08:48:30 +0200 Subject: [PATCH 64/78] simplifications + more tests --- python/damask/_lattice.py | 43 +++++++++--------- python/damask/_orientation.py | 75 ++++++++++++++++---------------- python/tests/test_Orientation.py | 31 ++++++++----- python/tests/test_ori_vec.py | 41 ----------------- 4 files changed, 79 insertions(+), 111 deletions(-) delete mode 100644 python/tests/test_ori_vec.py diff --git a/python/damask/_lattice.py b/python/damask/_lattice.py index a337aa1a1..160b3ba17 100644 --- a/python/damask/_lattice.py +++ b/python/damask/_lattice.py @@ -1,7 +1,5 @@ import numpy as np -from . import Rotation - class Symmetry: """ @@ -29,7 +27,6 @@ class Symmetry: raise KeyError(f'Crystal system "{system}" is unknown') self.system = system.lower() if isinstance(system,str) else system - self.lattice = self.system # ToDo: for compatibility def __copy__(self): @@ -219,6 +216,7 @@ class Symmetry: return np.ones_like(rho[...,0],dtype=bool) + #ToDo: IPF color in separate function def in_SST(self,vector,proper=False,color=False): """ Check whether given vector falls into standard stereographic triangle of own symmetry. @@ -311,9 +309,9 @@ class Symmetry: # ****************************************************************************************** class Lattice: # ToDo: Make a subclass of Symmetry! """ - Lattice system. + Bravais lattice. - Currently, this contains only a mapping from Bravais lattice to symmetry + This contains only a mapping from Bravais lattice to symmetry and orientation relationships. It could include twin and slip systems. References @@ -331,7 +329,7 @@ class Lattice: # ToDo: Make a subclass of Symmetry! } - def __init__(self, lattice): + def __init__(self,lattice,c_over_a=None): """ New lattice of given type. @@ -345,20 +343,20 @@ class Lattice: # ToDo: Make a subclass of Symmetry! self.symmetry = Symmetry(self.lattices[lattice]['system']) # transition to subclass - self.system = self.symmetry.system - self.in_SST = self.symmetry.in_SST - self.in_FZ = self.symmetry.in_FZ - + self.system = self.symmetry.system + self.in_SST = self.symmetry.in_SST + self.in_FZ = self.symmetry.in_FZ + self.in_disorientation_SST = self.symmetry.in_disorientation_SST def __repr__(self): """Report basic lattice information.""" - return 'Bravais lattice {} ({} symmetry)'.format(self.lattice,self.symmetry) + return 'Bravais lattice {} ({} crystal system)'.format(self.lattice,self.symmetry) # Kurdjomov--Sachs orientation relationship for fcc <-> bcc transformation # from S. Morito et al., Journal of Alloys and Compounds 577:s587-s592, 2013 # also see K. Kitahara et al., Acta Materialia 54:1279-1288, 2006 - KS = {'mapping':{'fcc':0,'bcc':1}, + _KS = {'mapping':{'fcc':0,'bcc':1}, 'planes': np.array([ [[ 1, 1, 1],[ 0, 1, 1]], [[ 1, 1, 1],[ 0, 1, 1]], @@ -412,7 +410,7 @@ class Lattice: # ToDo: Make a subclass of Symmetry! # Greninger--Troiano orientation relationship for fcc <-> bcc transformation # from Y. He et al., Journal of Applied Crystallography 39:72-81, 2006 - GT = {'mapping':{'fcc':0,'bcc':1}, + _GT = {'mapping':{'fcc':0,'bcc':1}, 'planes': np.array([ [[ 1, 1, 1],[ 1, 0, 1]], [[ 1, 1, 1],[ 1, 1, 0]], @@ -466,7 +464,7 @@ class Lattice: # ToDo: Make a subclass of Symmetry! # Greninger--Troiano' orientation relationship for fcc <-> bcc transformation # from Y. He et al., Journal of Applied Crystallography 39:72-81, 2006 - GTprime = {'mapping':{'fcc':0,'bcc':1}, + _GTprime = {'mapping':{'fcc':0,'bcc':1}, 'planes': np.array([ [[ 7, 17, 17],[ 12, 5, 17]], [[ 17, 7, 17],[ 17, 12, 5]], @@ -520,7 +518,7 @@ class Lattice: # ToDo: Make a subclass of Symmetry! # Nishiyama--Wassermann orientation relationship for fcc <-> bcc transformation # from H. Kitahara et al., Materials Characterization 54:378-386, 2005 - NW = {'mapping':{'fcc':0,'bcc':1}, + _NW = {'mapping':{'fcc':0,'bcc':1}, 'planes': np.array([ [[ 1, 1, 1],[ 0, 1, 1]], [[ 1, 1, 1],[ 0, 1, 1]], @@ -550,7 +548,7 @@ class Lattice: # ToDo: Make a subclass of Symmetry! # Pitsch orientation relationship for fcc <-> bcc transformation # from Y. He et al., Acta Materialia 53:1179-1190, 2005 - Pitsch = {'mapping':{'fcc':0,'bcc':1}, + _Pitsch = {'mapping':{'fcc':0,'bcc':1}, 'planes': np.array([ [[ 0, 1, 0],[ -1, 0, 1]], [[ 0, 0, 1],[ 1, -1, 0]], @@ -580,7 +578,7 @@ class Lattice: # ToDo: Make a subclass of Symmetry! # Bain orientation relationship for fcc <-> bcc transformation # from Y. He et al., Journal of Applied Crystallography 39:72-81, 2006 - Bain = {'mapping':{'fcc':0,'bcc':1}, + _Bain = {'mapping':{'fcc':0,'bcc':1}, 'planes': np.array([ [[ 1, 0, 0],[ 1, 0, 0]], [[ 0, 1, 0],[ 0, 1, 0]], @@ -590,7 +588,8 @@ class Lattice: # ToDo: Make a subclass of Symmetry! [[ 0, 0, 1],[ 1, 0, 1]], [[ 1, 0, 0],[ 1, 1, 0]]],dtype='float')} - def relationOperations(self,model): + + def relation_operations(self,model): """ Crystallographic orientation relationships for phase transformations. @@ -612,8 +611,8 @@ class Lattice: # ToDo: Make a subclass of Symmetry! https://doi.org/10.1016/j.actamat.2004.11.021 """ - models={'KS':self.KS, 'GT':self.GT, 'GT_prime':self.GTprime, - 'NW':self.NW, 'Pitsch': self.Pitsch, 'Bain':self.Bain} + models={'KS':self._KS, 'GT':self._GT, 'GT_prime':self._GTprime, + 'NW':self._NW, 'Pitsch': self._Pitsch, 'Bain':self._Bain} try: relationship = models[model] except KeyError : @@ -639,6 +638,8 @@ class Lattice: # ToDo: Make a subclass of Symmetry! otherDir = miller[otherDir_id]/ np.linalg.norm(miller[otherDir_id]) otherMatrix = np.array([otherDir,np.cross(otherPlane,otherDir),otherPlane]) - r['rotations'].append(Rotation.from_matrix(np.dot(otherMatrix.T,myMatrix))) + r['rotations'].append(np.dot(otherMatrix.T,myMatrix)) + + r['rotations'] = np.array(r['rotations']) return r diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index 57407a6ce..3c2d73a92 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -71,8 +71,8 @@ class Orientation: # ToDo: make subclass of lattice and Rotation? r = b*aInv for k in range(2): r.inverse() - breaker = self.in_FZ \ - and (not SST or other.lattice.symmetry.inDisorientationSST(r.as_Rodrigues(vector=True))) + breaker = self.lattice.in_FZ(r.as_Rodrigues(vector=True)) \ + and (not SST or other.lattice.in_disorientation_SST(r.as_Rodrigues(vector=True))) if breaker: break if breaker: break if breaker: break @@ -90,40 +90,36 @@ class Orientation: # ToDo: make subclass of lattice and Rotation? @property def equivalent(self): """ - Return orientations which are symmetrically equivalent. + Orientations which are symmetrically equivalent. - One dimension (length according to symmetrically equivalent orientations) + One dimension (length according to number of symmetrically equivalent orientations) is added to the left of the Rotation array. """ - s = self.lattice.symmetry.symmetry_operations - s = s.reshape(s.shape[:1]+(1,)*len(self.rotation.shape)+(4,)) - s = Rotation(np.broadcast_to(s,s.shape[:1]+self.rotation.quaternion.shape)) + o = self.lattice.symmetry.symmetry_operations + o = o.reshape(o.shape[:1]+(1,)*len(self.rotation.shape)+(4,)) + o = Rotation(np.broadcast_to(o,o.shape[:1]+self.rotation.quaternion.shape)) - r = np.broadcast_to(self.rotation.quaternion,s.shape[:1]+self.rotation.quaternion.shape) + s = np.broadcast_to(self.rotation.quaternion,o.shape[:1]+self.rotation.quaternion.shape) - return self.__class__(s@Rotation(r),self.lattice) + return self.__class__(o@Rotation(s),self.lattice) - def relatedOrientations_vec(self,model): - """List of orientations related by the given orientation relationship.""" - h = self.lattice.relationOperations(model) - rot= h['rotations'] - op=np.array([o.as_quaternion() for o in rot]) + def related(self,model): + """ + Orientations related by the given orientation relationship. - s = op.reshape(op.shape[:1]+(1,)*len(self.rotation.shape)+(4,)) - s = Rotation(np.broadcast_to(s,s.shape[:1]+self.rotation.quaternion.shape)) + One dimension (length according to number of related orientations) + is added to the left of the Rotation array. - r = np.broadcast_to(self.rotation.quaternion,s.shape[:1]+self.rotation.quaternion.shape) - r = Rotation(r) + """ + o = Rotation.from_matrix(self.lattice.relation_operations(model)['rotations']).as_quaternion() + o = o.reshape(o.shape[:1]+(1,)*len(self.rotation.shape)+(4,)) + o = Rotation(np.broadcast_to(o,o.shape[:1]+self.rotation.quaternion.shape)) - return self.__class__(s@r,h['lattice']) + s = np.broadcast_to(self.rotation.quaternion,o.shape[:1]+self.rotation.quaternion.shape) - - def relatedOrientations(self,model): - """List of orientations related by the given orientation relationship.""" - r = self.lattice.relationOperations(model) - return [self.__class__(o*self.rotation,r['lattice']) for o in r['rotations']] + return self.__class__(o@Rotation(s),self.lattice.relation_operations(model)['lattice']) @property @@ -136,26 +132,31 @@ class Orientation: # ToDo: make subclass of lattice and Rotation? found = np.zeros_like(in_FZ[0],dtype=bool) q = self.rotation.quaternion[0] for s in range(in_FZ.shape[0]): + #something fishy... why does q needs to be initialized? q = np.where(np.expand_dims(np.logical_and(in_FZ[s],~found),-1),eq.rotation.quaternion[s],q) found = np.logical_or(in_FZ[s],found) return self.__class__(q,self.lattice) - # ToDo: vectorize - def inversePole(self, - axis, - proper = False, - SST = True): + def inverse_pole(self,axis,proper=False,SST=True): """Axis rotated according to orientation (using crystal symmetry to ensure location falls into SST).""" - if SST: # pole requested to be within SST - for i,o in enumerate(self.equivalent): # test all symmetric equivalent quaternions - pole = o.rotation@axis # align crystal direction to axis - if self.lattice.in_SST(pole,proper): break # found SST version - else: - pole = self.rotation@axis # align crystal direction to axis + if SST: + eq = self.equivalent + pole = eq.rotation @ np.broadcast_to(axis/np.linalg.norm(axis),eq.rotation.shape+(3,)) + in_SST = self.lattice.in_SST(pole,proper=proper) + + # remove duplicates (occur for highly symmetric orientations) + found = np.zeros_like(in_SST[0],dtype=bool) + p = pole[0] + for s in range(in_SST.shape[0]): + p = np.where(np.expand_dims(np.logical_and(in_SST[s],~found),-1),pole[s],p) + found = np.logical_or(in_SST[s],found) + + return p + else: + return self.rotation @ np.broadcast_to(axis/np.linalg.norm(axis),self.rotation.shape+(3,)) - return (pole,i if SST else 0) def IPF_color(self,axis): #ToDo axis or direction? @@ -166,7 +167,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation? # remove duplicates (occur for highly symmetric orientations) found = np.zeros_like(in_SST[0],dtype=bool) - c = np.empty(color.shape[1:]) + c = color[0] for s in range(in_SST.shape[0]): c = np.where(np.expand_dims(np.logical_and(in_SST[s],~found),-1),color[s],c) found = np.logical_or(in_SST[s],found) diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index 78781e47d..4be146ec7 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -28,6 +28,22 @@ def reference_dir(reference_dir_base): class TestOrientation: + @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) + @pytest.mark.parametrize('lattice',['fcc','bcc']) + def test_relationship_vectorize(self,set_of_quaternions,lattice,model): + result = Orientation(set_of_quaternions[:200].reshape(50,4,4),lattice).related(model) + ref_qu = result.rotation.quaternion.reshape(-1,200,4) + for i in range(200): + single = Orientation(set_of_quaternions[i],lattice).related(model).rotation.quaternion + assert np.allclose(ref_qu[:,i,:],single) + + @pytest.mark.parametrize('lattice',Lattice.lattices) + def test_IPF_vectorize(self,set_of_quaternions,lattice): + direction = np.random.random(3)*2.0-1 + oris = Orientation(Rotation(set_of_quaternions),lattice)[:200] + for i,color in enumerate(oris.IPF_color(direction)): + assert np.allclose(color,IPF_color(oris[i],direction)) + @pytest.mark.parametrize('color',[{'label':'red', 'RGB':[1,0,0],'direction':[0,0,1]}, {'label':'green','RGB':[0,1,0],'direction':[0,1,1]}, {'label':'blue', 'RGB':[0,0,1],'direction':[1,1,1]}]) @@ -45,15 +61,6 @@ class TestOrientation: for equivalent in ori.equivalent: assert np.allclose(color,equivalent.IPF_color(direction)) - - @pytest.mark.parametrize('lattice',Lattice.lattices) - def test_IPF_vectorize(self,set_of_quaternions,lattice): - direction = np.random.random(3)*2.0-1 - oris = Orientation(Rotation(set_of_quaternions),lattice)[:200] - for i,color in enumerate(oris.IPF_color(direction)): - assert np.allclose(color,IPF_color(oris[i],direction)) - - @pytest.mark.parametrize('lattice',Lattice.lattices) def test_reduced(self,set_of_quaternions,lattice): oris = Orientation(Rotation(set_of_quaternions),lattice) @@ -65,8 +72,8 @@ class TestOrientation: @pytest.mark.parametrize('lattice',['fcc','bcc']) def test_relationship_forward_backward(self,model,lattice): ori = Orientation(Rotation.from_random(),lattice) - for i,r in enumerate(ori.relatedOrientations(model)): - ori2 = r.relatedOrientations(model)[i] + for i,r in enumerate(ori.related(model)): + ori2 = r.related(model)[i] misorientation = ori.rotation.misorientation(ori2.rotation) assert misorientation.asAxisAngle(degrees=True)[3]<1.0e-5 @@ -75,7 +82,7 @@ class TestOrientation: def test_relationship_reference(self,update,reference_dir,model,lattice): reference = os.path.join(reference_dir,'{}_{}.txt'.format(lattice,model)) ori = Orientation(Rotation(),lattice) - eu = np.array([o.rotation.as_Eulers(degrees=True) for o in ori.relatedOrientations(model)]) + eu = np.array([o.rotation.as_Eulers(degrees=True) for o in ori.related(model)]) if update: coords = np.array([(1,i+1) for i,x in enumerate(eu)]) table = damask.Table(eu,{'Eulers':(3,)}) diff --git a/python/tests/test_ori_vec.py b/python/tests/test_ori_vec.py deleted file mode 100644 index 9280af164..000000000 --- a/python/tests/test_ori_vec.py +++ /dev/null @@ -1,41 +0,0 @@ -import pytest -import numpy as np - -from damask import Rotation -from damask import Orientation -from damask import Lattice - -rot0= Rotation.from_random() -rot1= Rotation.from_random() -rot2= Rotation.from_random() -rot3= Rotation.from_random() - -#disorientation - -#fromaverage -#average - -class TestOrientation_vec: - - - @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) - @pytest.mark.parametrize('lattice',['fcc','bcc']) - def test_relatedOrientations_vec(self,model,lattice): - ori0=Orientation(rot0,lattice) - ori1=Orientation(rot1,lattice) - ori2=Orientation(rot2,lattice) - ori3=Orientation(rot3,lattice) - - quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),rot2.as_quaternion(),rot3.as_quaternion()]) - ori_vec=Orientation(quat,lattice) - - - for s in range(len(ori1.lattice.relationOperations(model)['rotations'])): - assert all(ori_vec.relatedOrientations_vec(model).rotation.as_Eulers()[s,0] == \ - ori0.relatedOrientations(model)[s].rotation.as_Eulers()) - assert all(ori_vec.relatedOrientations_vec(model).rotation.as_quaternion()[s,1] == \ - ori1.relatedOrientations(model)[s].rotation.as_quaternion()) - assert all(ori_vec.relatedOrientations_vec(model).rotation.as_Rodrigues()[s,2] == \ - ori2.relatedOrientations(model)[s].rotation.as_Rodrigues()) - assert all(ori_vec.relatedOrientations_vec(model).rotation.as_cubochoric()[s,3] == \ - ori3.relatedOrientations(model)[s].rotation.as_cubochoric()) From be1eb996e01e378440b7d945b517766dc059cfe8 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 1 Jul 2020 15:11:39 +0200 Subject: [PATCH 65/78] more tests and cleaning --- python/damask/_rotation.py | 5 ----- python/tests/test_Orientation.py | 8 +++++++- python/tests/test_Rotation.py | 12 ++++++++++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 91f126c88..28e2b428f 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -627,12 +627,7 @@ class Rotation: return Rotation(q.reshape(r.shape[:-1]+(4,)) if shape is not None else q)._standardize() - # for compatibility (old names do not follow convention) - def asM(self): - return self.M - fromQuaternion = from_quaternion fromEulers = from_Eulers - asAxisAngle = as_axis_angle __mul__ = __matmul__ #################################################################################################### diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index 1c5288923..4a7c3e54d 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -75,7 +75,7 @@ class TestOrientation: for i,r in enumerate(ori.related(model)): ori2 = r.related(model)[i] misorientation = ori.rotation.misorientation(ori2.rotation) - assert misorientation.asAxisAngle(degrees=True)[3]<1.0e-5 + assert misorientation.as_axis_angle(degrees=True)[3]<1.0e-5 @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) @pytest.mark.parametrize('lattice',['fcc','bcc']) @@ -89,3 +89,9 @@ class TestOrientation: table.add('pos',coords) table.to_ASCII(reference) assert np.allclose(eu,damask.Table.from_ASCII(reference).get('Eulers')) + + @pytest.mark.parametrize('lattice',Lattice.lattices) + def test_disorientation360(self,lattice): + R_1 = Orientation(Rotation(),lattice) + R_2 = Orientation(damask.Rotation.from_Eulers([360,0,0],degrees=True),lattice) + assert np.allclose(R_1.disorientation(R_2).as_matrix(),np.eye(3)) diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 0cf2185c0..21d0b5fae 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -905,3 +905,15 @@ class TestRotation: def test_misorientation(self): R = Rotation.from_random() assert np.allclose(R.misorientation(R).as_matrix(),np.eye(3)) + + def test_misorientation360(self): + R_1 = Rotation() + R_2 = Rotation.from_Eulers([360,0,0],degrees=True) + assert np.allclose(R_1.misorientation(R_2).as_matrix(),np.eye(3)) + + @pytest.mark.parametrize('angle',[10,20,30,40,50,60,70,80,90,100,120]) + def test_average(self,angle): + R_1 = Rotation.from_axis_angle([0,0,1,10],degrees=True) + R_2 = Rotation.from_axis_angle([0,0,1,angle],degrees=True) + avg_angle = R_1.average(R_2).as_axis_angle(degrees=True,pair=True)[1] + assert np.isclose(avg_angle,10+(angle-10)/2.) From 4abd77fccf5f19fde9ffceb9aaab57c292bc6cd6 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 1 Jul 2020 16:26:56 +0200 Subject: [PATCH 66/78] more test coverage --- python/damask/_orientation.py | 8 ++++---- python/damask/_rotation.py | 10 ++++++--- python/tests/test_Orientation.py | 35 ++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index b3395acfd..0bb0e1bc8 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -61,7 +61,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation? if self.lattice.symmetry != other.lattice.symmetry: raise NotImplementedError('disorientation between different symmetry classes not supported yet.') - mySymEqs = self.equivalent if SST else self.equivalent[0] #ToDo: This is just me! # take all or only first sym operation + mySymEqs = self.equivalent if SST else self.equivalent[0] #ToDo: This is just me! # take all or only first sym operation otherSymEqs = other.equivalent for i,sA in enumerate(mySymEqs): @@ -177,7 +177,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation? # ToDo: Discuss vectorization/calling signature @staticmethod - def fromAverage(orientations, + def from_average(orientations, weights = []): """Create orientation from average of list of orientations.""" # further read: Orientation distribution analysis in deformed grains @@ -193,10 +193,10 @@ class Orientation: # ToDo: make subclass of lattice and Rotation? SST = False, # select (o[ther]'s) sym orientation symmetries = True)[2]].rotation) # with lowest misorientation - return Orientation(Rotation.fromAverage(closest,weights),ref.lattice) + return Orientation(Rotation.from_average(closest,weights),ref.lattice) # ToDo: Discuss vectorization/calling signature def average(self,other): """Calculate the average rotation.""" - return Orientation.fromAverage([self,other]) + return Orientation.from_average([self,other]) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 28e2b428f..674656d4b 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -199,7 +199,7 @@ class Rotation: """ if self.quaternion.shape != (4,) or other.quaternion.shape != (4,): raise NotImplementedError('Support for multiple rotations missing') - return Rotation.fromAverage([self,other]) + return Rotation.from_average([self,other]) ################################################################################################ @@ -294,7 +294,11 @@ class Rotation: """ ro = Rotation._qu2ro(self.quaternion) - return ro[...,:3]*ro[...,3:4] if vector else ro + if vector: + with np.errstate(invalid='ignore'): + return ro[...,:3]*ro[...,3:4] + else: + return ro def as_homochoric(self): """ @@ -577,7 +581,7 @@ class Rotation: @staticmethod - def fromAverage(rotations,weights = None): + def from_average(rotations,weights = None): """ Average rotation. diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index 4a7c3e54d..84414a343 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -20,6 +20,16 @@ def IPF_color(orientation,direction): return color +def inverse_pole(orientation,axis,proper=False,SST=True): + if SST: + for eq in orientation.equivalent: + pole = eq.rotation @ axis/np.linalg.norm(axis) + if orientation.lattice.in_SST(pole,proper=proper): + return pole + else: + return orientation.rotation @ axis/np.linalg.norm(axis) + + @pytest.fixture def reference_dir(reference_dir_base): """Directory containing reference results.""" @@ -44,6 +54,15 @@ class TestOrientation: for i,color in enumerate(oris.IPF_color(direction)): assert np.allclose(color,IPF_color(oris[i],direction)) + @pytest.mark.parametrize('SST',[False,True]) + @pytest.mark.parametrize('proper',[True,False]) + @pytest.mark.parametrize('lattice',Lattice.lattices) + def test_inverse_pole_vectorize(self,set_of_quaternions,lattice,SST,proper): + axis = np.random.random(3)*2.0-1 + oris = Orientation(Rotation(set_of_quaternions),lattice)[:200] + for i,pole in enumerate(oris.inverse_pole(axis,SST=SST)): + assert np.allclose(pole,inverse_pole(oris[i],axis,SST=SST)) + @pytest.mark.parametrize('color',[{'label':'red', 'RGB':[1,0,0],'direction':[0,0,1]}, {'label':'green','RGB':[0,1,0],'direction':[0,1,1]}, {'label':'blue', 'RGB':[0,0,1],'direction':[1,1,1]}]) @@ -95,3 +114,19 @@ class TestOrientation: R_1 = Orientation(Rotation(),lattice) R_2 = Orientation(damask.Rotation.from_Eulers([360,0,0],degrees=True),lattice) assert np.allclose(R_1.disorientation(R_2).as_matrix(),np.eye(3)) + + @pytest.mark.parametrize('lattice',Lattice.lattices) + @pytest.mark.parametrize('angle',[10,20,30,40]) + def test_average(self,angle,lattice): + R_1 = Orientation(Rotation.from_axis_angle([0,0,1,10],degrees=True),lattice) + R_2 = Orientation(Rotation.from_axis_angle([0,0,1,angle],degrees=True),lattice) + avg_angle = R_1.average(R_2).rotation.as_axis_angle(degrees=True,pair=True)[1] + assert np.isclose(avg_angle,10+(angle-10)/2.) + + @pytest.mark.parametrize('lattice',Lattice.lattices) + def test_from_average(self,lattice): + R_1 = Orientation(Rotation.from_random(),lattice) + eqs = [r for r in R_1.equivalent] + R_2 = damask.Orientation.from_average(eqs) + assert np.allclose(R_1.rotation.quaternion,R_2.rotation.quaternion) + From fcea6d46066bcf8a7920716fe2091a19fa1dd454 Mon Sep 17 00:00:00 2001 From: Test User Date: Wed, 1 Jul 2020 18:11:31 +0200 Subject: [PATCH 67/78] [skip ci] updated version information after successful test of v2.0.3-2752-g0b2d62e9 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index be876f016..1248f6ed7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.3-2717-g52aacf37 +v2.0.3-2752-g0b2d62e9 From 86dc7054a494f034f22394726f6974ed4aa2f279 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 1 Jul 2020 22:11:16 +0200 Subject: [PATCH 68/78] still needed --- python/damask/_rotation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 674656d4b..a605a74b6 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -630,8 +630,9 @@ class Rotation: return Rotation(q.reshape(r.shape[:-1]+(4,)) if shape is not None else q)._standardize() - + # for compatibility (old names do not follow convention) fromEulers = from_Eulers + asAxisAngle = as_axis_angle __mul__ = __matmul__ #################################################################################################### From 208d5109d4371d5c18cfc0fac6c617d04c9012f3 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jul 2020 08:14:13 +0200 Subject: [PATCH 69/78] still needed ... --- python/damask/_rotation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index a605a74b6..59bca3795 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -632,6 +632,7 @@ class Rotation: # for compatibility (old names do not follow convention) fromEulers = from_Eulers + fromQuaternion = from_quaternion asAxisAngle = as_axis_angle __mul__ = __matmul__ From b88becb9d089e10f1e7714182fbc291e987f5e1e Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jul 2020 08:25:35 +0200 Subject: [PATCH 70/78] don't go out of focus during initialization --- python/damask/_environment.py | 22 +++++++++++++++------- python/damask/_vtk.py | 2 +- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/python/damask/_environment.py b/python/damask/_environment.py index 29c0f45c7..7b8b8487e 100644 --- a/python/damask/_environment.py +++ b/python/damask/_environment.py @@ -3,23 +3,30 @@ from pathlib import Path class Environment: + # ToDo: Probably, we don't need a class (just a module with a few functions) def __init__(self): - """Read and provide values of DAMASK configuration.""" - self.screen_width = 1024 - self.screen_height = 768 + """Do Nothing.""" + pass + + @property + def screen_size(self): + width = 1024 + height = 768 try: import wx - _ = wx.App(False) # noqa - self.screenwidth, self.screenheight = wx.GetDisplaySize() + _ = wx.App(False) # noqa + width, height = wx.GetDisplaySize() except ImportError: try: import tkinter tk = tkinter.Tk() - self.screen_width = tk.winfo_screenwidth() - self.screen_height = tk.winfo_screenheight() + width = tk.winfo_screenwidth() + height = tk.winfo_screenheight() tk.destroy() except Exception as e: pass + return (width,height) + @property def options(self): @@ -32,6 +39,7 @@ class Environment: return options + @property def root_dir(self): """Return DAMASK root path.""" diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index 49a4f8958..1e99f83f3 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -242,7 +242,7 @@ class VTK: ren.AddActor(actor) ren.SetBackground(0.2,0.2,0.2) - window.SetSize(Environment().screen_width,Environment().screen_height) + window.SetSize(Environment().screen_size[0],Environment().screen_size[1]) iren = vtk.vtkRenderWindowInteractor() iren.SetRenderWindow(window) From 3b72d0ec725450d576a6d2fa3ea310f20d471164 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Thu, 2 Jul 2020 10:13:47 -0400 Subject: [PATCH 71/78] added __invert__ method to reverse colormap --- python/damask/_colormap.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index 964f22d85..2533d4d36 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -24,6 +24,10 @@ class Colormap(mpl.colors.ListedColormap): return Colormap(np.vstack((self.colors,other.colors)), f'{self.name}+{other.name}') + def __invert__(self): + """Return inverted colormap.""" + return self.reversed() + @staticmethod def from_range(low,high,name='DAMASK colormap',N=256,model='rgb'): """ From f3ff2e741288984d6c4182453f2526eaaab1fbf4 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Thu, 2 Jul 2020 10:25:04 -0400 Subject: [PATCH 72/78] added __iadd__ method --- python/damask/_colormap.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index 2533d4d36..c15befcfa 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -24,6 +24,11 @@ class Colormap(mpl.colors.ListedColormap): return Colormap(np.vstack((self.colors,other.colors)), f'{self.name}+{other.name}') + def __iadd__(self,other): + """Concatenate colormaps.""" + return Colormap(np.vstack((self.colors,other.colors)), + f'{self.name}+{other.name}') + def __invert__(self): """Return inverted colormap.""" return self.reversed() From 5a96708f4143c07d15e74ca938bc4e648296deb9 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Thu, 2 Jul 2020 10:26:04 -0400 Subject: [PATCH 73/78] added __iadd__ method --- python/damask/_colormap.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index c15befcfa..9d57f234b 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -26,8 +26,7 @@ class Colormap(mpl.colors.ListedColormap): def __iadd__(self,other): """Concatenate colormaps.""" - return Colormap(np.vstack((self.colors,other.colors)), - f'{self.name}+{other.name}') + return self.__add__(other) def __invert__(self): """Return inverted colormap.""" From 368a2419312097167bc53c2bd51cce152bf09f1e Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Thu, 2 Jul 2020 10:26:28 -0400 Subject: [PATCH 74/78] added testing of __iadd__ and __invert__ --- python/tests/test_Colormap.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/python/tests/test_Colormap.py b/python/tests/test_Colormap.py index fcc2eca81..f288ffb24 100644 --- a/python/tests/test_Colormap.py +++ b/python/tests/test_Colormap.py @@ -118,6 +118,17 @@ class TestColormap: assert (not np.allclose(c_1.colors,c_2.colors)) and \ np.allclose(c_1.colors,c_2.reversed().colors) + def test_invert(self): + c_1 = Colormap.from_predefined('strain') + c_2 = ~c_1 + assert (not np.allclose(c_1.colors,c_2.colors)) and \ + np.allclose(c_1.colors,(~c_2).colors) + + def test_add(self): + c = Colormap.from_predefined('jet') + c += c + assert (np.allclose(c.colors[:len(c.colors)],c.colors[len(c.colors):])) + def test_list(self): Colormap.list_predefined() @@ -138,4 +149,3 @@ class TestColormap: c.to_file(format=format) time.sleep(.5) assert filecmp.cmp(tmpdir/(name+ext),reference_dir/(name+ext)) - From 81b3c103059fdc931623b274de6abf25cae24317 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Thu, 2 Jul 2020 10:37:57 -0400 Subject: [PATCH 75/78] removed ambiguous variable "l" --- python/tests/test_Colormap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/tests/test_Colormap.py b/python/tests/test_Colormap.py index f288ffb24..fbe65e2bd 100644 --- a/python/tests/test_Colormap.py +++ b/python/tests/test_Colormap.py @@ -127,7 +127,7 @@ class TestColormap: def test_add(self): c = Colormap.from_predefined('jet') c += c - assert (np.allclose(c.colors[:len(c.colors)],c.colors[len(c.colors):])) + assert (np.allclose(c.colors[:len(c.colors)//2],c.colors[len(c.colors)//2:])) def test_list(self): Colormap.list_predefined() From 7b899f1ff19665d621c02aced57212a497968cb3 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Thu, 2 Jul 2020 11:33:09 -0400 Subject: [PATCH 76/78] fixed output number format width for single increment0 --- python/damask/_result.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/damask/_result.py b/python/damask/_result.py index 35879ec80..43573b871 100644 --- a/python/damask/_result.py +++ b/python/damask/_result.py @@ -1222,7 +1222,7 @@ class Result: elif mode.lower()=='point': v = VTK.from_polyData(self.cell_coordinates()) - N_digits = int(np.floor(np.log10(int(self.increments[-1][3:]))))+1 + N_digits = int(np.floor(np.log10(max(1,int(self.increments[-1][3:])))))+1 for inc in util.show_progress(self.iterate('increments'),len(self.selection['increments'])): From d2d7526779f15480fdfa66f91b06fbdcdf1aa18a Mon Sep 17 00:00:00 2001 From: Test User Date: Fri, 3 Jul 2020 00:30:24 +0200 Subject: [PATCH 77/78] [skip ci] updated version information after successful test of v2.0.3-2783-g1a8feab2 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 1248f6ed7..5b3a868b4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.3-2752-g0b2d62e9 +v2.0.3-2783-g1a8feab2 From 6c260945db8957e121dea7d4bc6f0676bcab49ac Mon Sep 17 00:00:00 2001 From: Test User Date: Fri, 3 Jul 2020 03:10:51 +0200 Subject: [PATCH 78/78] [skip ci] updated version information after successful test of v2.0.3-2792-gd4f97f83 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 1248f6ed7..8a08ebebc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.3-2752-g0b2d62e9 +v2.0.3-2792-gd4f97f83