diff --git a/appveyor.yml b/appveyor.yml index 151e0a2b..600d09d0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,25 +1,30 @@ + +image: Visual Studio 2015 + build: false environment: matrix: - - CONDA: C:\Miniconda - - CONDA: C:\Miniconda-x64 - - CONDA: C:\Miniconda36 - - CONDA: C:\Miniconda36-x64 + - CONDA: C:\Python27 + - CONDA: C:\Python27-x64 + - CONDA: C:\Python38-x64 +# Meson errors, no binaries for 32-bits - CONDA: C:\Python38 init: - "ECHO %CONDA%" install: - "SET PATH=%CONDA%;%CONDA%\\Scripts;%CONDA%\\Library\\bin;%CONDA%\\DLLS;%PATH%" - - conda config --set always_yes yes --set changeps1 no - - conda info -a + - "mkdir C:\\tmp" + - "set TMPDIR=C:\\tmp" + - python -c "import sys; print(sys.executable, sys.version)" - python -m pip install --upgrade pip setuptools - python -m pip install pytest numpy - - python -m pip install --ignore-installed certifi --upgrade-strategy only-if-needed --only-binary=h5py --editable . + - python -m pip install --ignore-installed certifi --upgrade-strategy only-if-needed --only-binary=h5py --only-binary=scipy --only-binary=hdf5plugin . test_script: + - cd test - python -m pytest diff --git a/setup.py b/setup.py index 369ae6ec..7d996203 100644 --- a/setup.py +++ b/setup.py @@ -101,7 +101,7 @@ def build_extension(self, ext): # note that the pyf must come first cnames = "_cImageD11.pyf blobs.c cdiffraction.c cimaged11utils.c"+\ " closest.c connectedpixels.c darkflat.c localmaxlabel.c sparse_image.c "+\ -" splat.c" +" splat.c" csources = [os.path.join('src',c) for c in cnames.split()] @@ -147,22 +147,24 @@ def build_extension(self, ext): ] useful = [ # stuff you probably want, and should be able to get easily - "fabio", + 'fabio==0.2.2 ; python_version < "3" and sys_platform == "win32" ', + 'fabio ; python_version >= "3" or sys_platform != "win32" ', "xfab>=0.0.4", # # comes from xfab : "PyCifRW", "matplotlib", # tkGui "pyopengltk", # plot3d in tkGui "scipy", # + 'hdf5plugin==1.4.1 ; python_version < "3" and sys_platform == "win32" ', + 'hdf5plugin ; python_version >= "3" ', 'h5py', 'pyyaml', "pytest", # for the CI 'numba==0.46.0 ; python_version < "3" ', # for some test cases 'llvmlite==0.30.0 ; python_version < "3" ', # for some test cases - 'numba ; python_version > "3" ', # for some test cases + 'numba ; python_version >= "3" ', # for some test cases "bslz4_to_sparse", "fast_histogram", "scikit-image", - "hdf5plugin", "tqdm", ] diff --git a/test/demo/qtsplat.py b/test/demo/qtsplat.py index af63e718..3f27f5ee 100644 --- a/test/demo/qtsplat.py +++ b/test/demo/qtsplat.py @@ -6,6 +6,8 @@ from timeit import default_timer as timer import numpy as np from scipy.spatial.transform import Rotation +if not hasattr( Rotation, 'as_matrix' ): + Rotation.as_matrix = Rotation.as_dcm from ImageD11 import columnfile, transform, grain, cImageD11 import nbsplat @@ -31,33 +33,33 @@ def __init__(self, def loadcolf(self, colfilename): self.colf = columnfile.columnfile( colfilename ) self.nrows = self.colf.nrows - + def loadpars(self, fname): self.colf.parameters.loadparameters( fname ) self.colf.updateGeometry() - + def loadgrains(self, fname): self.grains = grain.read_grain_file( fname ) if len(self.grains)>0: self.resetlabels() self.assignlabels() - + def get(self, name): if name in self.colf.titles: return self.colf.getcolumn(name) else: raise Exception("name "+name+" not found") - + def xyz(self): return [self.colf.getcolumn(name) for name in self.names] - + def grain_drlv2(self, grainid): x,y,z = self.xyz() hkl = np.dot( self.grains[grainid].ubi, self.xyz ) dhkl = np.round(hkl)-hkl drlv2 = (dhkl*dhkl).sum(axis=0) self.colf.addcolumn("drlv2_%d"%(grainid)) - + def resetlabels(self): self.labels = np.empty( self.nrows, 'i') self.labels.fill(-1) @@ -71,7 +73,7 @@ def resetlabels(self): self.colf.zl ) ).T.copy() self.colf.addcolumn( self.labels, "labels" ) self.colf.addcolumn( self.drlv2, "drlv2" ) - + def assignlabels( self ): self.resetlabels() nr = self.nrows @@ -102,7 +104,7 @@ def assignlabels( self ): self.colf.labels[:]= self.labels self.colf.drlv2[:] = self.drlv2 - + class splat3dview: def __init__(self, w=2000, h=1500): self.w = self.h = None @@ -137,12 +139,12 @@ def resetrot(self): self.need_redraw = True self.u0 = np.dot(self.u, self.u0) self.u = np.eye(3, dtype=float) - + def matrix(self): return self.scale*np.dot(self.u, self.u0) - + def drawPixMapOnWidget(self, datas, target ): if self.need_redraw: start = timer() @@ -167,7 +169,7 @@ def drawPixMapOnWidget(self, datas, target ): # order should be here too. # print("draw",timer()-start) # fixme - can we write on i.bits() directly ? - p = rgbtopm( self.rgba ) + p = rgbtopm( self.rgba ) target.setPixmap( p ) if DOPROF: x.disable() @@ -185,8 +187,8 @@ def rgbtopm( rgb ): i = QtGui.QImage(rgb, rgb.shape[1], rgb.shape[0], QtGui.QImage.Format_RGB32 ) return QtGui.QPixmap.fromImage(i) - - + + def colormap(x, colors): # fixme: load save or matplotlib or select xmin = x.min() @@ -198,12 +200,12 @@ def colormap(x, colors): colors[:,1] = np.interp( x, [ xmin, xcen, xmax ], [ 64, 255, 64]) # G colors[:,2] = np.interp( x, [ xmin, xcen, xmax ], [255, 64, 64]) # B colors[:,3] = 0 # alpha - - - - + + + + class Example( QtWidgets.QWidget ): def __init__(self, vu, da): QtWidgets.QWidget.__init__(self) @@ -217,30 +219,30 @@ def __init__(self, vu, da): self.doRot = False self.animate() self.show() - + def mousePressEvent(self, e): #print('down', e.x(),e.y(),e.button() ) self.doRot = True self.resetrot( e.x(), e.y()) - + def resetrot(self, x0, y0 ): self.x0 = x0 self.y0 = y0 self.vu.resetrot() - + def mouseReleaseEvent(self, e): self.mouseMoveEvent(e) #print('up', e.x(),e.y()) self.vu.resetrot( ) self.doRot = False - + def mouseMoveEvent(self,e): # print('move',e.x(),e.y()) axes = "XYZ" if e.modifiers() == QtCore.Qt.ShiftModifier: - axes="ZYX" + axes="ZYX" if e.modifiers() == QtCore.Qt.ControlModifier: - axes="XZY" + axes="XZY" self.rotateview( e.x(), e.y(), axes ) def rotateview(self, ex, ey, axes="XYZ"): @@ -249,7 +251,7 @@ def rotateview(self, ex, ey, axes="XYZ"): dx = (self.x0 - self.ex)/10. # 0.1 deg/px dy = (self.y0 - self.ey)/10. self.vu.rotate( dy, -dx, axes ) - + rotateactions = { QtCore.Qt.Key_X : ( 90, 0, "XYZ"), QtCore.Qt.Key_Y : ( 90, 0, "YZX"), @@ -289,7 +291,7 @@ def hlp(self): #qmb.setText(h) #qmb.show() - + def keyPressEvent(self,e): k = e.key() print("Got key",k) @@ -326,23 +328,23 @@ def keyPressEvent(self,e): else: self.status.setText("No column %s"%(self.coloractions[k])) self.vu.need_redraw=True - + def keyReleaseEvent(self,e): if self.doRot and e.key() == QtCore.Qt.Key_Shift: self.x0 = self.ex self.y0 = self.ey self.vu.resetrot() - + def initUI(self): - + self.layout = QtWidgets.QVBoxLayout() - + self.lbl = QtWidgets.QLabel(self) self.lbl.resize( self.vu.w, self.vu.h ) self.lbl.setMinimumSize( 128, 128 ) e = QtWidgets.QSizePolicy.Expanding - self.lbl.setSizePolicy( e, e ) + self.lbl.setSizePolicy( e, e ) self.layout.addWidget( self.lbl ) self.status = QtWidgets.QLabel(self) @@ -356,11 +358,11 @@ def initUI(self): qb= QtWidgets.QPushButton("Quit", self) qb.clicked.connect( self.close ) self.buttons.addWidget(qb) - + self.layout.addLayout( self.buttons ) self.setLayout( self.layout ) - + def animate(self): self.vu.drawPixMapOnWidget( self.da, self.lbl) QtCore.QTimer.singleShot(1000//24, self.animate ) @@ -369,7 +371,7 @@ def animate(self): def resizeEvent(self, evt): self.vu.need_redraw=True - + def run( colf, pars, grains): @@ -393,4 +395,4 @@ def run( colf, pars, grains): pass run( colf, pars, grains ) - + diff --git a/test/test_misori.py b/test/test_misori.py index 23d9525b..0a9c7fbd 100644 --- a/test/test_misori.py +++ b/test/test_misori.py @@ -5,7 +5,8 @@ import xfab.symmetry import numpy as np -import timeit +import numba +import timeit, time import unittest, cProfile, pstats timer = timeit.default_timer # 1: Triclinic @@ -15,11 +16,53 @@ # 5: Trigonal # 6: Hexagonal # 7: Cubic - + +ROTATIONS = [None] + [np.ascontiguousarray(xfab.symmetry.rotations(i)) for i in range(1,8)] + +def isrotation(mat): + d = np.linalg.det(mat) + i = np.linalg.inv(mat) + return np.allclose(d,1) and np.allclose( i, mat.T ) + +for grp in ROTATIONS[1:]: + for mat in grp: + assert isrotation(mat), "Not a rotation"+str(mat) +print("Rotation are all rotation matrices") +#print(ROTATIONS) +def Umis(umat_1, umat_2, crystal_system): + rot = ROTATIONS[crystal_system] + return _Umis(umat_1, umat_2, rot) + +#@numba.njit(fastmath=True) +def _Umis(umat_1, umat_2, rot): + misorientations = np.empty( (len(rot),2)) + lengths = 0.5 * (rot * np.dot( umat_1.T, umat_2 )).sum(axis=(2,1)) - 0.5 + misorientations[:,0] = np.arange(len(rot)) + misorientations[:,1] = np.arccos(lengths.clip(-1,1)) * 180./np.pi + return misorientations + +from scipy.spatial.transform import Rotation as R +if not hasattr( R, 'as_matrix' ): + R.as_matrix = R.as_dcm + +from xfab import symmetry +start = time.time() +def test_Umis(): + for crystal_system in range(1,8): + U1 = R.random(200).as_matrix() + U2 = R.random(200).as_matrix() + for u1, u2 in zip(U1, U2): + m1 = Umis(u1, u2, crystal_system) + m2 = symmetry.Umis(u1, u2, crystal_system) + assert np.max(np.abs(m1 - m2)) < 1e-5, str(np.max(np.abs(m1 - m2))) + +test_Umis() +print("OK",time.time()-start) def misori_py( u1, u2, sym=7 ): # 7 is cubic - ans = xfab.symmetry.Umis( u1, u2, sym) +# ans = xfab.symmetry.Umis( u1, u2, sym) + ans = Umis( u1, u2, sym ) ang = np.min( ans[:,1] ) trc = np.cos(np.radians(ang))*2+1 return trc @@ -35,7 +78,7 @@ def make_random_orientations( N ): U[:,0,0] = 1 - 2*s*(q[j]*q[j]+q[k]*q[k]) U[:,0,1] = 2*s*(q[i]*q[j]-q[k]*q[r]) U[:,0,2] = 2*s*(q[i]*q[k]+q[j]*q[r]) - U[:,1,0] = 2*s*(q[i]*q[j]+q[k]*q[r]) + U[:,1,0] = 2*s*(q[i]*q[j]+q[k]*q[r]) U[:,1,1] = 1 - 2*s*(q[i]*q[i]+q[k]*q[k]) U[:,1,2] = 2*s*(q[j]*q[k]-q[i]*q[r]) U[:,2,0] = 2*s*(q[i]*q[k]-q[j]*q[r]) @@ -44,7 +87,7 @@ def make_random_orientations( N ): return U - +NROT = 200 class test_random_orientations( unittest.TestCase ): DOPROFILE = False @@ -56,7 +99,7 @@ def setUp(self): self.pr.enable() def tearDown(self): - if self.DOPROFILE: + if self.DOPROFILE: p = pstats.Stats( self.pr ) p.strip_dirs() p.sort_stats ('cumtime') @@ -72,9 +115,9 @@ def test_are_rotations( self ): # determinant is 1 dts = [ np.linalg.det(m) - 1 for m in self.U ] self.assertAlmostEqual( abs(np.array( dts )).max(), 0 ) - + def test_cubic(self): - U = make_random_orientations( 20 ) # xfab is a bit slow + U = make_random_orientations( NROT ) # xfab is a bit slow t0 = timer() pairs = [ (u1, u2) for u1 in U for u2 in U ] c = [ misori_cubic( u1, u2 ) for u1, u2 in pairs ] @@ -84,9 +127,9 @@ def test_cubic(self): if self.DOBENCH: print("C time %f s , pytime %f s"%(t1-t0, t2-t1)) self.assertTrue(np.allclose( np.array(c) ,np.array(c) )) - + def test_monoclinic(self): - U = make_random_orientations( 20 ) # xfab is a bit slow + U = make_random_orientations( NROT ) # xfab is a bit slow t0 = timer() pairs = [ (u1, u2) for u1 in U for u2 in U ] c = [ misori_monoclinic( u1, u2 ) for u1, u2 in pairs ] @@ -98,7 +141,7 @@ def test_monoclinic(self): self.assertTrue(np.allclose( np.array(c) ,np.array(c) )) def test_orthorhombic(self): - U = make_random_orientations( 20 ) # xfab is a bit slow + U = make_random_orientations( NROT ) # xfab is a bit slow t0 = timer() pairs = [ (u1, u2) for u1 in U for u2 in U ] c = [ misori_orthorhombic( u1, u2 ) for u1, u2 in pairs ] @@ -110,7 +153,7 @@ def test_orthorhombic(self): self.assertTrue(np.allclose( np.array(c) ,np.array(c) )) def test_tetragonal(self): - U = make_random_orientations( 20 ) # xfab is a bit slow + U = make_random_orientations( NROT ) # xfab is a bit slow t0 = timer() pairs = [ (u1, u2) for u1 in U for u2 in U ] c = [ misori_tetragonal( u1, u2 ) for u1, u2 in pairs ] @@ -120,10 +163,9 @@ def test_tetragonal(self): if self.DOBENCH: print("C time %f s , pytime %f s"%(t1-t0, t2-t1)) self.assertTrue(np.allclose( np.array(c) ,np.array(c) )) - + def test_cubic_reverse(self): - N = 250 - U = make_random_orientations( N ) + U = make_random_orientations( NROT ) t0 = timer() for u1 in U: for u2 in U: @@ -133,7 +175,7 @@ def test_cubic_reverse(self): t1 = timer() if self.DOBENCH: print("C %d pairs in %f s, %f s per pair"%( - N*N*2, t1-t0, (t1-t0)/N)) + NROT*NROT*2, t1-t0, (t1-t0)/NROT)) def test_cubic_pairmat(self): N = 500 @@ -151,7 +193,7 @@ def test_cubic_pairmat(self): print( "time for distance matrix",t1-t0, t2-t1) self.assertTrue( np.allclose( m0, m1 ) ) - + if __name__=="__main__": unittest.main() - +