#!/usr/bin/python
from __future__ import division
import numpy as np
from Tkinter import *
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
from PIL import Image
from PIL import PngImagePlugin
import ttk
import sys
from fractions import Fraction
from tkFileDialog import *
import os
import matplotlib as mpl
mpl.rcParams['font.size'] = 12

###################################################################"
##### Fonction projection  sur l'abaque
####################################################################

def proj(x,y,z):

    if z==1:
        X=0
        Y=0
    elif z<-0.000001:
        X=250
        Y=250
    else:

        X=x/(1+z)
        Y=y/(1+z)

    return np.array([X,Y],float)

###################################################################"
##### Fonction rotation
####################################################################

def rotation(phi1,phi,phi2):
   phi1=phi1*np.pi/180;
   phi=phi*np.pi/180;
   phi2=phi2*np.pi/180;
   R=np.array([[np.cos(phi1)*np.cos(phi2)-np.cos(phi)*np.sin(phi1)*np.sin(phi2),
            -np.cos(phi)*np.cos(phi2)*np.sin(phi1)-np.cos(phi1)*
            np.sin(phi2),np.sin(phi)*np.sin(phi1)],[np.cos(phi2)*np.sin(phi1)
            +np.cos(phi)*np.cos(phi1)*np.sin(phi2),np.cos(phi)*np.cos(phi1)
            *np.cos(phi2)-np.sin(phi1)*np.sin(phi2), -np.cos(phi1)*np.sin(phi)],
            [np.sin(phi)*np.sin(phi2), np.cos(phi2)*np.sin(phi), np.cos(phi)]],float)
   return R

####################################################################
##### Fonction rotation autour d'un axe
####################################################################

def Rot(th,a,b,c):
   th=th*np.pi/180;
   aa=a/np.linalg.norm([a,b,c]);
   bb=b/np.linalg.norm([a,b,c]);
   cc=c/np.linalg.norm([a,b,c]);
   c1=np.array([[1,0,0],[0,1,0],[0,0,1]],float)
   c2=np.array([[aa**2,aa*bb,aa*cc],[bb*aa,bb**2,bb*cc],[cc*aa,
                cc*bb,cc**2]],float)
   c3=np.array([[0,-cc,bb],[cc,0,-aa],[-bb,aa,0]],float)
   R=np.cos(th)*c1+(1-np.cos(th))*c2+np.sin(th)*c3

   return R

####################################################################
##### Fonction cristal
####################################################################
def crist():
    global axesA,axeshA,axesB,axeshB,D,Dstar,V
    a=eval(a_entry.get())
    b=eval(b_entry.get())
    c=eval(c_entry.get())
    alp=eval(alp_entry.get())
    bet=eval(bet_entry.get())
    gam=eval(gam_entry.get())
    e=eval(e_entry.get())
    d2=eval(d_label_var.get())
    alp=alp*np.pi/180;
    bet=bet*np.pi/180;
    gam=gam*np.pi/180;
    V=a*b*c*np.sqrt(1-(np.cos(alp)**2)-(np.cos(bet))**2-(np.cos(gam))**2+2*b*c*np.cos(alp)*np.cos(bet)*np.cos(gam))
    D=np.array([[a,b*np.cos(gam),c*np.cos(bet)],[0,b*np.sin(gam),  c*(np.cos(alp)-np.cos(bet)*np.cos(gam))/np.sin(gam)],[0,0,V/(a*b*np.sin(gam))]])
    Dstar=np.transpose(np.linalg.inv(D))
    G=np.array([[a**2,a*b*np.cos(gam),a*c*np.cos(bet)],[a*b*np.cos(gam),b**2,b*c*np.cos(alp)],[a*c*np.cos(bet),b*c*np.cos(alp),c**2]])
    axes=np.zeros(((2*e+1)**3-1,3))
    axesh=np.zeros(((2*e+1)**3-1,3))
    id=0
    for i in range(-e,e+1):
        for j in range(-e,e+1):
            for k in range(-e,e+1):
                if (i,j,k)!=(0,0,0):
                    d=1/(np.sqrt(np.dot(np.array([i,j,k]),np.dot(np.linalg.inv(G),np.array([i,j,k])))))
                    if d>d2*0.1*np.amax([a,b,c]):
                        if var_uvw.get()==0:
                            axesh[id,:]=np.dot(Dstar,np.array([i,j,k],float))
                            axes[id,:]=np.array([i,j,k],float)
                        else:
                            axesh[id,:]=np.dot(D,np.array([i,j,k],float))
                            axes[id,:]=np.array([i,j,k],float)
                        id=id+1
    axesA=axes
    axesB=axes
    axeshA=axesh
    axeshB=axesh
    return axesA,axeshA,axesB,axeshB,D,Dstar,V

def dm():
    global dmip
    a=f.add_subplot(111)
    a.figure.clear()
    a=f.add_subplot(111)
    dmip=dmip-eval(d_entry.get())
    d_label_var.set(dmip)
    crist()
    trace()

    return dmip
def dp():
    global dmip
    a=f.add_subplot(111)
    a.figure.clear()
    a=f.add_subplot(111)
    dmip=dmip+eval(d_entry.get())
    d_label_var.set(dmip)
    crist()
    trace()

    return dmip

####################################################################
##### Fonction ajouter un pole
####################################################################
def poleA(pole1,pole2,pole3):
    global MA,axesA,axeshA,Ta,V,D,Dstar

    fp=f.add_subplot(111)
    Gs=np.array([pole1,pole2,pole3],float)

    Pp=np.zeros((1,2),float)

    if var_uvw.get()==0:
        Gsh=np.dot(Dstar,Gs)/np.linalg.norm(np.dot(Dstar,Gs))
    else:
        Gsh=np.dot(D,Gs)/np.linalg.norm(np.dot(D,Gs))

    S=np.dot(MA,Gsh)
    if S[2]<0:
        S=-S
        Gsh=-Gsh
        pole1=-pole1
        pole2=-pole2
        pole3=-pole3
    Pp=proj(S[0],S[1],S[2])*600/2
    l=str(int(pole1))+str(int(pole2))+str(int(pole3))
    fp.plot(Pp[0]+600/2,Pp[1]+600/2,'ro')
    fp.annotate(l,(Pp[0]+600/2,Pp[1]+600/2))
    fp.axis([0,600,0,600])
    fp.axis('off')
    fp.figure.canvas.draw()

    axesA=np.vstack((axesA,np.array([pole1,pole2,pole3])))
    axesA=np.vstack((axesA,np.array([-pole1,-pole2,-pole3])))
    Ta=np.vstack((Ta,np.array([S[0],S[1],S[2]])))
    Ta=np.vstack((Ta,np.array([-S[0],-S[1],-S[2]])))
    axeshA=np.vstack((axeshA,np.array([Gsh[0],Gsh[1],Gsh[2]])))
    axeshA=np.vstack((axeshA,np.array([-Gsh[0],-Gsh[1],-Gsh[2]])))
    return axesA,axeshA,Ta

def poleB(pole1,pole2,pole3):
    global MB,axesB,axeshB,Tb,V,D,Dstar

    fp=f.add_subplot(111)
    Gs=np.array([pole1,pole2,pole3],float)

    Pp=np.zeros((1,2),float)

    if var_uvw.get()==0:
        Gsh=np.dot(Dstar,Gs)/np.linalg.norm(np.dot(Dstar,Gs))
    else:
        Gsh=np.dot(D,Gs)/np.linalg.norm(np.dot(D,Gs))

    S=np.dot(MB,Gsh)
    if S[2]<0:
        S=-S
        Gsh=-Gsh
        pole1=-pole1
        pole2=-pole2
        pole3=-pole3
    Pp=proj(S[0],S[1],S[2])*600/2
    l=str(int(pole1))+str(int(pole2))+str(int(pole3))
    fp.plot(Pp[0]+600/2,Pp[1]+600/2,'ro')
    fp.annotate(l,(Pp[0]+600/2,Pp[1]+600/2))
    fp.axis([0,600,0,600])
    fp.axis('off')
    fp.figure.canvas.draw()

    axesB=np.vstack((axesB,np.array([pole1,pole2,pole3])))
    axesB=np.vstack((axesB,np.array([-pole1,-pole2,-pole3])))
    Tb=np.vstack((Tb,np.array([S[0],S[1],S[2]])))
    Tb=np.vstack((Tb,np.array([-S[0],-S[1],-S[2]])))
    axeshB=np.vstack((axeshB,np.array([Gsh[0],Gsh[1],Gsh[2]])))
    axeshB=np.vstack((axeshB,np.array([-Gsh[0],-Gsh[1],-Gsh[2]])))
    return axesB,axeshB,Tb

def addpoleA_sym():

    pole1A=eval(pole1A_entry.get())
    pole2A=eval(pole2A_entry.get())
    pole3A=eval(pole3A_entry.get())
    poleA(pole1A,pole2A,pole3A)
    poleA(pole1A,pole2A,-pole3A)
    poleA(pole1A,-pole2A,pole3A)
    poleA(-pole1A,pole2A,pole3A)
    poleA(pole2A,pole1A,pole3A)
    poleA(pole2A,pole1A,-pole3A)
    poleA(pole2A,-pole1A,pole3A)
    poleA(-pole2A,pole1A,pole3A)
    poleA(pole2A,pole3A,pole1A)
    poleA(pole2A,pole3A,-pole1A)
    poleA(pole2A,-pole3A,pole1A)
    poleA(-pole2A,pole3A,pole1A)
    poleA(pole1A,pole3A,pole2A)
    poleA(pole1A,pole3A,-pole2A)
    poleA(pole1A,-pole3A,pole2A)
    poleA(-pole1A,pole3A,pole2A)
    poleA(pole3A,pole1A,pole2A)
    poleA(pole3A,pole1A,-pole2A)
    poleA(pole3A,-pole1A,pole2A)
    poleA(-pole3A,pole1A,pole2A)
    poleA(pole3A,pole2A,pole1A)
    poleA(pole3A,pole2A,-pole1A)
    poleA(pole3A,-pole2A,pole1A)
    poleA(-pole3A,pole2A,pole1A)
    trace()

def addpoleB_sym():

    pole1B=eval(pole1B_entry.get())
    pole2B=eval(pole2B_entry.get())
    pole3B=eval(pole3B_entry.get())
    poleB(pole1B,pole2B,pole3B)
    poleB(pole1B,pole2B,-pole3B)
    poleB(pole1B,-pole2B,pole3B)
    poleB(-pole1B,pole2B,pole3B)
    poleB(pole2B,pole1B,pole3B)
    poleB(pole2B,pole1B,-pole3B)
    poleB(pole2B,-pole1B,pole3B)
    poleB(-pole2B,pole1B,pole3B)
    poleB(pole2B,pole3B,pole1B)
    poleB(pole2B,pole3B,-pole1B)
    poleB(pole2B,-pole3B,pole1B)
    poleB(-pole2B,pole3B,pole1B)
    poleB(pole1B,pole3B,pole2B)
    poleB(pole1B,pole3B,-pole2B)
    poleB(pole1B,-pole3B,pole2B)
    poleB(-pole1B,pole3B,pole2B)
    poleB(pole3B,pole1B,pole2B)
    poleB(pole3B,pole1B,-pole2B)
    poleB(pole3B,-pole1B,pole2B)
    poleB(-pole3B,pole1B,pole2B)
    poleB(pole3B,pole2B,pole1B)
    poleB(pole3B,pole2B,-pole1B)
    poleB(pole3B,-pole2B,pole1B)
    poleB(-pole3B,pole2B,pole1B)
    trace()

def addpoleA():

    pole1A=eval(pole1A_entry.get())
    pole2A=eval(pole2A_entry.get())
    pole3A=eval(pole3A_entry.get())
    poleA(pole1A,pole2A,pole3A)
    trace()
def addpoleB():

    pole1B=eval(pole1B_entry.get())
    pole2B=eval(pole2B_entry.get())
    pole3B=eval(pole3B_entry.get())
    poleB(pole1B,pole2B,pole3B)
    trace()
####################################################################
##### Fonction tracer plan
####################################################################
def trace_planA():
    global MA,axes,axesh,Ta,V,D,Dstar
    f2=f.add_subplot(111)
    pole1A=eval(pole1A_entry.get())
    pole2A=eval(pole2A_entry.get())
    pole3A=eval(pole3A_entry.get())
    Gs=np.array([pole1A,pole2A,pole3A],float)
    if var_uvw.get()==0:
        Gsh=np.dot(Dstar,Gs)/np.linalg.norm(np.dot(Dstar,Gs))
    else:
        Gsh=np.dot(D,Gs)/np.linalg.norm(np.dot(D,Gs))
    S=np.dot(MA,Gsh)
    if S[2]<0:
        S=-S
        Gsh=-Gsh
        pole1A=-pole1A
        pole2A=-pole2A
        pole3A=-pole3A
    r=np.sqrt(S[0]**2+S[1]**2+S[2]**2)
    A=np.zeros((2,100))
    Q=np.zeros((1,2))
    if S[2]==0:
         t=90
         w=0
    else:
         t=np.arctan2(S[1],S[0])*180/np.pi
         w=0
    ph=np.arccos(S[2]/r)*180/np.pi
    for g in np.linspace(-np.pi,np.pi-0.00001,100):
        Aa=np.dot(Rot(t,0,0,1),np.dot(Rot(ph,0,1,0),np.array([np.sin(g),np.cos(g),0])))
        A[:,w]=proj(Aa[0],Aa[1],Aa[2])*600/2
        if A[0,w]<>75000:
            Q=np.vstack((Q,A[:,w]))
            w=w+1
    Q=np.delete(Q,0,0)
    f2.plot(Q[:,0]+600/2,Q[:,1]+600/2,'r')
    f2.axis([0,600,0,600])
    f2.axis('off')
    f2.figure.canvas.draw()
    trace()

def trace_planB():
    global MB,axes,axesh,Tb,V,D,Dstar
    f2=f.add_subplot(111)
    pole1B=eval(pole1B_entry.get())
    pole2B=eval(pole2B_entry.get())
    pole3B=eval(pole3B_entry.get())
    Gs=np.array([pole1B,pole2B,pole3B],float)
    if var_uvw.get()==0:
        Gsh=np.dot(Dstar,Gs)/np.linalg.norm(np.dot(Dstar,Gs))
    else:
        Gsh=np.dot(D,Gs)/np.linalg.norm(np.dot(D,Gs))
    S=np.dot(MB,Gsh)
    if S[2]<0:
        S=-S
        Gsh=-Gsh
        pole1B=-pole1B
        pole2B=-pole2B
        pole3B=-pole3B
    r=np.sqrt(S[0]**2+S[1]**2+S[2]**2)
    A=np.zeros((2,100))
    Q=np.zeros((1,2))
    if S[2]==0:
         t=90
         w=0
    else:
         t=np.arctan2(S[1],S[0])*180/np.pi
         w=0
    ph=np.arccos(S[2]/r)*180/np.pi
    for g in np.linspace(-np.pi,np.pi-0.00001,100):
        Aa=np.dot(Rot(t,0,0,1),np.dot(Rot(ph,0,1,0),np.array([np.sin(g),np.cos(g),0])))
        A[:,w]=proj(Aa[0],Aa[1],Aa[2])*600/2
        if A[0,w]<>75000:
            Q=np.vstack((Q,A[:,w]))
            w=w+1
    Q=np.delete(Q,0,0)
    f2.plot(Q[:,0]+600/2,Q[:,1]+600/2,'r')
    f2.axis([0,600,0,600])
    f2.axis('off')
    f2.figure.canvas.draw()
    trace()

####################################################################
##### Click a pole
####################################################################
def click_a_pole(event):

    global MB,Dstar,D
    x=event.x
    y=event.y
    x=(x-411)*2/620
    y=-(y-400)*2/620
    X=2*x/(1+x**2+y**2)
    Y=2*y/(1+x**2+y**2)
    Z=(-1+x**2+y**2)/(1+x**2+y**2)
    if Z<0:
        X=-X
        Y=-Y
    A=np.dot(np.linalg.inv(MB),np.array([X,Y,Z]))
    n=0
    L=np.zeros((3,16**3))

    for i in range(-8,9,1):
        for j in range(-8,9,1):
            for k in range(-8,9,1):
                if np.linalg.norm([i,j,k])<>0:
                    if var_uvw.get()==0:
                        Q=np.dot(Dstar,np.array([i,j,k],float))/np.linalg.norm(np.dot(Dstar,np.array([i,j,k],float)))
                        if np.abs(Q[0]-A[0])<0.05 and np.abs(Q[1]-A[1])<0.05 and np.abs(Q[2]-A[2])<0.05:
                            L[:,n]=np.array([i,j,k],float)
                            n=n+1

                    else:

                        Q=np.dot(D,np.array([i,j,k],float))/np.linalg.norm(np.dot(D,np.array([i,j,k],float)))
                        if np.abs(Q[0]-A[0])<0.05 and np.abs(Q[1]-A[1])<0.05 and np.abs(Q[2]-A[2])<0.05:
                            L[:,n]=np.array([i,j,k],float)
                            n=n+1

    if np.linalg.norm(L[:,0])<>0:
        poleB(L[0,0],L[1,0],L[2,0])
        trace()
####################################################################
##### Inclinaison-beta
####################################################################
####################################################################
##### Fonction desorientation
####################################################################
def Rota(t,u,v,w,g):
    Ae=np.dot(g,np.array([u,v,w]))
    Re=Rot(t,Ae[0],Ae[1],Ae[2])
    return Re

def cryststruct():
    global cs
    a=eval(a_entry.get())
    b=eval(b_entry.get())
    c=eval(c_entry.get())
    alp=eval(alp_entry.get())
    bet=eval(bet_entry.get())
    gam=eval(gam_entry.get())

    if  gam==90 and alp==90 and bet==90 and a==b and b==c:
       cs=1

    if gam==120 and alp==90 and bet==90:
        cs=2

    if gam==90 and alp==90 and bet==90 and a==b and b<>c:
        cs=3

    if alp<>90 and a==b and b==c:
        cs=4

    if gam==90 and alp==90 and bet==90 and a<>b and b<>c:
        cs=5

    if gam<>90 and alp==90 and bet==90 and a<>b and b<>c:
        cs=6

    if gam<>90 and alp<>90 and bet<>90 and a<>b and b<>c:
        cs=7
    return cs

def Sy(g):
    global cs
    if cs==1:
        S1=Rota(90,1,0,0,g);
        S2=Rota(180,1,0,0,g);
        S3=Rota(270,1,0,0,g);
        S4=Rota(90,0,1,0,g);
        S5=Rota(180,0,1,0,g);
        S6=Rota(270,0,1,0,g);
        S7=Rota(90,0,0,1,g);
        S8=Rota(180,0,0,1,g);
        S9=Rota(270,0,0,1,g);
        S10=Rota(180,1,1,0,g);
        S11=Rota(180,1,0,1,g);

        S12=Rota(180,0,1,1,g);
        S13=Rota(180,-1,1,0,g);
        S14=Rota(180,-1,0,1,g);
        S15=Rota(180,0,-1,1,g);
        S16=Rota(120,1,1,1,g);
        S17=Rota(240,1,1,1,g);
        S18=Rota(120,-1,1,1,g);
        S19=Rota(240,-1,1,1,g);
        S20=Rota(120,1,-1,1,g);
        S21=Rota(240,1,-1,1,g);
        S22=Rota(120,1,1,-1,g);
        S23=Rota(240,1,1,-1,g);
        S24=np.eye(3,3);
        S=np.vstack((S1,S2,S3,S4,S5,S6,S7,S8,S9,S10,S11,S12,S13,S14,S15,S16,S17,S18,S19,S20,S21,S22,S23,S24))

    if cs==2:
        S1=Rota(60,0,0,1,g);
        S2=Rota(120,0,0,1,g);
        S3=Rota(180,0,0,1,g);
        S4=Rota(240,0,0,1,g);
        S5=Rota(300,0,0,1,g);
        S6=np.eye(3,3);
        S7=Rota(180,0,0,1,g);
        S8=Rota(180,0,1,0,g);
        S9=Rota(180,1/2,np.sqrt(3)/2,0,g);
        S10=Rota(180,-1/2,np.sqrt(3)/2,0,g);
        S11=Rota(180,np.sqrt(3)/2,1/2,0,g);
        S12=Rota(180,-np.sqrt(3)/2,1/2,0,g);
        S=np.vstack((S1,S2,S3,S4,S5,S6,S7,S8,S9,S10,S11,S12))

    if cs==3:
        S1=Rota(90,0,0,1,g);
        S2=Rota(180,0,0,1,g);
        S3=Rota(270,0,0,1,g);
        S4=Rota(180,0,1,0,g);
        S5=Rota(180,1,0,0,g);
        S6=Rota(180,1,1,0,g);
        S7=Rota(180,1,-1,0,g);
        S8=np.eye(3,3)
        S=np.vstack((S1,S2,S3,S4,S5,S6,S7,S8))

    if cs==4:
        S1=Rota(60,0,0,1,g);
        S2=Rota(120,0,0,1,g);
        S3=Rota(180,0,0,1,g);
        S4=Rota(240,0,0,1,g);
        S5=Rota(300,0,0,1,g);
        S6=np.eye(3,3);
        S7=Rota(180,0,0,1,g);
        S8=Rota(180,0,1,0,g);
        S9=Rota(180,1/2,np.sqrt(3)/2,0,g);
        S10=Rota(180,-1/2,np.sqrt(3)/2,0,g);
        S11=Rota(180,np.sqrt(3)/2,1/2,0,g);
        S12=Rota(180,-np.sqrt(3)/2,1/2,0,g);
        S=np.vstack((S1,S2,S3,S4,S5,S6,S7,S8,S9,S10,S11,S12))

    if cs==5:
        S1=Rota(180,0,0,1,g);
        S2=Rota(180,1,0,0,g);
        S3=Rota(180,0,1,0,g);
        S4=np.eye(3,3);
        S=np.vstack((S1,S2,S3,S4))

    if cs==6:
        S1=Rota(180,0,1,0,g);
        S2=np.eye(3,3);
        S=np.vstack((S1,S2))

    if cs==7:
        S=np.eye(3,3);

    return S

def null(A, rcond=None):

    u, s, vh = np.linalg.svd(A, full_matrices=True)
    M, N = u.shape[0], vh.shape[1]
    if rcond is None:
        rcond = np.finfo(s.dtype).eps * max(M, N)
    tol = np.amax(s) * rcond
    num = np.sum(s > tol, dtype=int)
    Q = vh[num:,:].T.conj()
    return Q

def desorientation():
    global D0,S,D1,cs,V,Qp
    a = f.add_subplot(111)
    a.figure.clear()
    a = f.add_subplot(111)
    fn = os.path.join(os.path.dirname(__file__), 'stereo.png')
    img=np.array(Image.open(fn))

    cryststruct()
    phi1a=eval(phi1A_entry.get())
    phia=eval(phiA_entry.get())
    phi2a=eval(phi2A_entry.get())
    phi1b=eval(phi1B_entry.get())
    phib=eval(phiB_entry.get())
    phi2b=eval(phi2B_entry.get())

    gA=rotation(phi1a,phia,phi2a)
    gB=rotation(phi1b,phib,phi2b)
    k=0
    S=Sy(gA)

    D0=np.zeros((int(np.shape(S)[0]/3),5))
    D1=np.zeros((int(np.shape(S)[0]/3),3))
    Qp=np.zeros((int(np.shape(S)[0]/3),2))
    for i in range(0,np.shape(S)[0],3):
        In=np.dot(np.array([[S[i,0],S[i+1,0],S[i+2,0]],[S[i,1],S[i+1,1],S[i+2,1]],[S[i,2],S[i+1,2],S[i+2,2]]]),gA)
        Ing=np.dot(In,np.array([0,0,1]))
        In2=np.dot(Rot(-phi2b,Ing[0],Ing[1],Ing[2]),In)
        Ing2=np.dot(In2,np.array([1,0,0]))
        In3=np.dot(Rot(-phib,Ing2[0],Ing2[1],Ing2[2]),In2)
        Ing3=np.dot(In3,np.array([0,0,1]))
        A=np.dot(Rot(-phi1b,Ing3[0],Ing3[1],Ing3[2]),In3)-np.eye(3)
        V=null(A,0.001).T

        if 0.5*(np.trace(A+np.eye(3))-1)>1:
        	D0[k,3]=0
        elif 0.5*(np.trace(A+np.eye(3))-1)<-1:
        	D0[k,3]=180
        else:
        	D0[k,3]=np.arccos(0.5*(np.trace(A+np.eye(3))-1))*180/np.pi

        if np.abs(D0[k,3])<1e-5:
        	D0[k,0]=0
        	D0[k,1]=0
        	D0[k,2]=0
        else:
		D0[k,0]=V[0,0]/np.linalg.norm(V)
		D0[k,1]=V[0,1]/np.linalg.norm(V)
		D0[k,2]=V[0,2]/np.linalg.norm(V)

        Ds1=np.dot(np.linalg.inv(gB),np.array([D0[k,0],D0[k,1],D0[k,2]]))

        F0=Fraction(Ds1[0]).limit_denominator(10)
        F1=Fraction(Ds1[1]).limit_denominator(10)
        F2=Fraction(Ds1[2]).limit_denominator(10)

        D1[k,0]=F0.numerator*F1.denominator*F2.denominator
        D1[k,1]=F1.numerator*F0.denominator*F2.denominator
        D1[k,2]=F2.numerator*F0.denominator*F1.denominator

        if D0[k,2]<0:
           D0[k,0]=-D0[k,0]
           D0[k,1]=-D0[k,1]
           D0[k,2]=-D0[k,2]
           D1[k,0]=-D1[k,0]
           D1[k,1]=-D1[k,1]
           D1[k,2]=-D1[k,2]

        D0[k,4]=k
        Qp[k,:]=proj(D0[k,0],D0[k,1],D0[k,2])*600/2

        k=k+1

    a.plot(Qp[:,0]+600/2,Qp[:,1]+600/2,'ro')
    a.axis([0,600,0,600])
    a.imshow(img,interpolation="bicubic")
    a.axis('off')
    a.figure.canvas.draw()
    trace()

    return Qp,S,D1
####################################################################
##### Fonction principale
####################################################################
def trace():
    global Ta,Tb,axesA,axeshA,MA,axesB,axeshB,MB,Qp,S,D1,show_ind,D0
    a = f.add_subplot(111)

    fn = os.path.join(os.path.dirname(__file__), 'stereo.png')
    img=np.array(Image.open(fn))

    Pa=np.zeros((np.shape(axesA)[0],2))
    Pb=np.zeros((np.shape(axesB)[0],2))

    for i in range(0,np.shape(axesA)[0]):
        axeshA[i,:]=axeshA[i,:]/np.linalg.norm(axeshA[i,:])
        Ta[i,:]=np.dot(MA,axeshA[i,:])
        Pa[i,:]=proj(Ta[i,0],Ta[i,1],Ta[i,2])*600/2
        if show_ind.get()==1:
            m=np.amax([np.abs(axesA[i,0]),np.abs(axesA[i,1]),np.abs(axesA[i,2])])
            if (np.around(axesA[i,0]/m)==axesA[i,0]/m) & (np.around(axesA[i,1]/m)==axesA[i,1]/m) & (np.around(axesA[i,2]/m)==axesA[i,2]/m):
                sA=str(int(axesA[i,0]/m))+str(int(axesA[i,1]/m))+str(int(axesA[i,2]/m))
            else:
               sA=str(int(axesA[i,0]))+str(int(axesA[i,1]))+str(int(axesA[i,2]))
            a.annotate(sA,(Pa[i,0]+600/2,Pa[i,1]+600/2))
    for i in range(0,np.shape(axesB)[0]):
        axeshB[i,:]=axeshB[i,:]/np.linalg.norm(axeshB[i,:])
        Tb[i,:]=np.dot(MB,axeshB[i,:])
        Pb[i,:]=proj(Tb[i,0],Tb[i,1],Tb[i,2])*600/2
        if show_ind.get()==1:
            m=np.amax([np.abs(axesB[i,0]),np.abs(axesB[i,1]),np.abs(axesB[i,2])])
            if (np.around(axesB[i,0]/m)==axesB[i,0]/m) & (np.around(axesB[i,1]/m)==axesB[i,1]/m) & (np.around(axesB[i,2]/m)==axesB[i,2]/m):
                sB=str(int(axesB[i,0]/m))+str(int(axesB[i,1]/m))+str(int(axesB[i,2]/m))
            else:
               sB=str(int(axesB[i,0]))+str(int(axesB[i,1]))+str(int(axesB[i,2]))
            a.annotate(sB,(Pb[i,0]+600/2,Pb[i,1]+600/2))

    for l in range(0,int(np.shape(S)[0]/3)):
        if show_angle.get()==1:
            sangle=str(np.round(D0[l,3],decimals=1))
            a.annotate(sangle,(Qp[l,0]+600/2,Qp[l,1]+600/2),size=8)
        if show_axe.get()==1:
            saxe=str(int(D1[l,0]))+','+str(int(D1[l,1]))+','+str(int(D1[l,2]))
            a.annotate(saxe,(Qp[l,0]+600/2,Qp[l,1]+600/2),size=8)
        if show_num.get()==1:
            snum=str(int(D0[l,4]))
            a.annotate(snum,(Qp[l,0]+600/2,Qp[l,1]+600/2),size=10)

    a.plot(Pa[:,0]+600/2,Pa[:,1]+600/2,'bo')
    a.plot(Pb[:,0]+600/2,Pb[:,1]+600/2,'go')
    a.plot(Qp[:,0]+600/2,Qp[:,1]+600/2,'ro')
    a.axis([0,600,0,600])
    a.imshow(img,interpolation="bicubic")
    a.axis('off')
    a.figure.canvas.draw()

def princ():
    global Ta,Tb,MA,MB

    a = f.add_subplot(111)
    a.figure.clear()
    a = f.add_subplot(111)
    phi1a=eval(phi1A_entry.get())
    phia=eval(phiA_entry.get())
    phi2a=eval(phi2A_entry.get())
    phi1b=eval(phi1B_entry.get())
    phib=eval(phiB_entry.get())
    phi2b=eval(phi2B_entry.get())
    fn = os.path.join(os.path.dirname(__file__), 'stereo.png')
    img=np.array(Image.open(fn))
    crist()
    Pa=np.zeros((np.shape(axesA)[0],2))
    Ta=np.zeros((np.shape(axesA)))
    Pb=np.zeros((np.shape(axesB)[0],2))
    Tb=np.zeros((np.shape(axesB)))

    for i in range(0,np.shape(axesA)[0]):
        axeshA[i,:]=axeshA[i,:]/np.linalg.norm(axeshA[i,:])
        Ta[i,:]=np.dot(rotation(phi1a,phia,phi2a),axeshA[i,:])
        Pa[i,:]=proj(Ta[i,0],Ta[i,1],Ta[i,2])*600/2
        m=np.amax([np.abs(axesA[i,0]),np.abs(axesA[i,1]),np.abs(axesA[i,2])])
        if (np.around(axesA[i,0]/m)==axesA[i,0]/m) & (np.around(axesA[i,1]/m)==axesA[i,1]/m) & (np.around(axesA[i,2]/m)==axesA[i,2]/m):
            sA=str(int(axesA[i,0]/m))+str(int(axesA[i,1]/m))+str(int(axesA[i,2]/m))
        else:
           sA=str(int(axesA[i,0]))+str(int(axesA[i,1]))+str(int(axesA[i,2]))
        a.annotate(sA,(Pa[i,0]+600/2,Pa[i,1]+600/2))
    for i in range(0,np.shape(axesB)[0]):
        axeshB[i,:]=axeshB[i,:]/np.linalg.norm(axeshB[i,:])
        Tb[i,:]=np.dot(rotation(phi1b,phib,phi2b),axeshB[i,:])
        Pb[i,:]=proj(Tb[i,0],Tb[i,1],Tb[i,2])*600/2
        m=np.amax([np.abs(axesB[i,0]),np.abs(axesB[i,1]),np.abs(axesB[i,2])])
        if (np.around(axesB[i,0]/m)==axesB[i,0]/m) & (np.around(axesB[i,1]/m)==axesB[i,1]/m) & (np.around(axesB[i,2]/m)==axesB[i,2]/m):
            sB=str(int(axesA[i,0]/m))+str(int(axesA[i,1]/m))+str(int(axesA[i,2]/m))
        else:
           sB=str(int(axesB[i,0]))+str(int(axesB[i,1]))+str(int(axesB[i,2]))
        a.annotate(sB,(Pb[i,0]+600/2,Pb[i,1]+600/2))

    a.plot(Pa[:,0]+600/2,Pa[:,1]+600/2,'bo')
    a.plot(Pb[:,0]+600/2,Pb[:,1]+600/2,'go')
    a.axis([0,600,0,600])

    a.imshow(img,interpolation="bicubic")
    a.axis('off')
    a.figure.canvas.draw()

    MA=rotation(phi1a,phia,phi2a)
    MB=rotation(phi1b,phib,phi2b)

    return Ta,MA,MB,Tb

######################################################################
# GUI
######################################################################

def file_save():
    global D1,D0,D
    fout = asksaveasfile(mode='w', defaultextension=".txt")

    for i in range(np.shape(D1)[0]):

		text2save = str(int(D0[i,4]))+'\t'+'['+str(int(D1[i,0]))+','+str(int(D1[i,1]))+','+str(int(D1[i,2]))+']'+'\t '+str(np.around(D0[i,3],decimals=2))
		fout.write("%s\n" % text2save)

    fout.close()

def image_save():

    s = asksaveasfile(mode='w', defaultextension=".jpg")
    if s:
        f.savefig(s.name)
    #s.close()

####################################################
#fonction d'initialisation
##################################################
def init():
    global var_uvw,D1,S,Qp,show_ind,show_angle,show_axe,show_num,dmip,d_label_var
    fn = os.path.join(os.path.dirname(__file__), 'stereo.png')
    img=np.array(Image.open(fn))
    a = f.add_subplot(111)
    a.axis('off')
    a.imshow(img,interpolation="bicubic")
    a.figure.canvas.draw()
    S=np.zeros((1,5))
    Qp=np.zeros((1,2))
    D1=np.zeros((1,5))
    var_uvw=IntVar()
    show_ind=IntVar()
    show_angle=IntVar()
    show_axe=IntVar()
    show_num=IntVar()
    d_label_var=StringVar()
    d_label_var.set(0)
    dmip=0
    return var_uvw,show_ind,show_angle,show_axe,show_num

##############################################################
# fonction pour quitter
#######################################################
def _quit():
    root.quit()     # stops mainloop
    root.destroy()  # this is necessary on Windows to prevent
                    # Fatal Python Error: PyEval_RestoreThread: NULL tstate
#############################################################

root = Tk()
root.wm_title("Misorientation")
root.geometry('1220x798+10+40')
root.configure(bg = '#BDBDBD')
#root.resizable(0,0)
#s=ttk.Style()
#s.theme_use('clam')
style = ttk.Style()
theme = style.theme_use()
default = style.lookup(theme, 'background')

################################################
# Creation d'une zone pour tracer des graphiques
################################################
f = Figure(facecolor='white',figsize=[2,2],dpi=100)

canvas = FigureCanvasTkAgg(f, master=root)

canvas.get_tk_widget().place(x=0,y=0,height=800,width=800)
canvas._tkcanvas.bind('<Button-3>', click_a_pole)
canvas.show()
toolbar = NavigationToolbar2TkAgg( canvas, root )
toolbar.zoom('off')
toolbar.update()

###################################################

init()
#import _imaging
#print _imaging.__file__

##############################################
# Boutons
##############################################
phi1A_entry = Entry (master=root)
phi1A_entry.place(relx=0.72,rely=0.5,relheight=0.03,relwidth=0.07)
phi1A_entry.configure(background="white")
phi1A_entry.configure(foreground="black")
phi1A_entry.configure(highlightbackground="#e0e0dfdfe3e3")
phi1A_entry.configure(highlightcolor="#000000")
phi1A_entry.configure(insertbackground="#000000")
phi1A_entry.configure(selectbackground="#c4c4c4")
phi1A_entry.configure(selectforeground="black")

phiA_entry = Entry (master=root)
phiA_entry.place(relx=0.72,rely=0.55,relheight=0.03,relwidth=0.07)
phiA_entry.configure(background="white")
phiA_entry.configure(foreground="black")
phiA_entry.configure(highlightcolor="black")
phiA_entry.configure(insertbackground="black")
phiA_entry.configure(selectbackground="#c4c4c4")
phiA_entry.configure(selectforeground="black")

label_euler = Label (master=root)
label_euler.place(relx=0.77,rely=0.42,height=46,width=163)
label_euler.configure(activebackground="#cccccc")
label_euler.configure(activeforeground="black")
label_euler.configure(cursor="fleur")
label_euler.configure(foreground="black")
label_euler.configure(highlightcolor="black")
label_euler.configure(text='''Euler angles \n A blue , B green''')

phi2A_entry = Entry (master=root)
phi2A_entry.place(relx=0.72,rely=0.6,relheight=0.03,relwidth=0.07)
phi2A_entry.configure(background="white")
phi2A_entry.configure(foreground="black")
phi2A_entry.configure(highlightcolor="black")
phi2A_entry.configure(insertbackground="black")
phi2A_entry.configure(selectbackground="#c4c4c4")
phi2A_entry.configure(selectforeground="black")

button_trace = Button (master=root)
button_trace.place(relx=0.7,rely=0.66,height=21,width=49)
button_trace.configure(activebackground="#f9f9f9")
button_trace.configure(activeforeground="black")
button_trace.configure(background="#ff0000")
button_trace.configure(command=princ)
button_trace.configure(foreground="black")
button_trace.configure(highlightcolor="black")
button_trace.configure(pady="0")
button_trace.configure(text='''PLOT''')

Phi1A_label = Label (master=root)
Phi1A_label.place(relx=0.67,rely=0.5,height=19,width=50)
Phi1A_label.configure(activebackground="#cccccc")
Phi1A_label.configure(activeforeground="black")
Phi1A_label.configure(foreground="black")
Phi1A_label.configure(highlightcolor="black")
Phi1A_label.configure(text='''Phi1A''')

PhiA_label = Label (master=root)
PhiA_label.place(relx=0.67,rely=0.55,height=19,width=50)
PhiA_label.configure(activebackground="#cccccc")
PhiA_label.configure(activeforeground="black")
PhiA_label.configure(foreground="black")
PhiA_label.configure(highlightcolor="black")
PhiA_label.configure(text='''PhiA''')

Phi2A_label = Label (master=root)
Phi2A_label.place(relx=0.67,rely=0.6,height=19,width=50)
Phi2A_label.configure(activebackground="#cccccc")
Phi2A_label.configure(activeforeground="black")
Phi2A_label.configure(foreground="black")
Phi2A_label.configure(highlightcolor="black")
Phi2A_label.configure(text='''Phi2A''')

phi1B_entry = Entry (master=root)
phi1B_entry.place(relx=0.86,rely=0.5,relheight=0.03,relwidth=0.07)
phi1B_entry.configure(background="white")

phi1B_entry.configure(foreground="black")
phi1B_entry.configure(highlightbackground="#e0e0dfdfe3e3")
phi1B_entry.configure(highlightcolor="#000000")
phi1B_entry.configure(insertbackground="#000000")
phi1B_entry.configure(selectbackground="#c4c4c4")
phi1B_entry.configure(selectforeground="black")

Phi1B = Label (master=root)
Phi1B.place(relx=0.81,rely=0.5,height=19,width=50)
Phi1B.configure(activebackground="#cccccc")
Phi1B.configure(activeforeground="black")
Phi1B.configure(foreground="black")
Phi1B.configure(highlightcolor="black")
Phi1B.configure(text='''Phi1B''')

PhiB_label1 = Label (master=root)
PhiB_label1.place(relx=0.81,rely=0.55,height=19,width=50)
PhiB_label1.configure(activebackground="#cccccc")
PhiB_label1.configure(activeforeground="black")
PhiB_label1.configure(foreground="black")
PhiB_label1.configure(highlightcolor="black")
PhiB_label1.configure(text='''PhiB''')

Phi2B_label2 = Label (master=root)
Phi2B_label2.place(relx=0.81,rely=0.6,height=19,width=50)
Phi2B_label2.configure(activebackground="#cccccc")
Phi2B_label2.configure(activeforeground="black")
Phi2B_label2.configure(foreground="black")
Phi2B_label2.configure(highlightcolor="black")
Phi2B_label2.configure(text='''Phi2B''')

phiB_entry = Entry (master=root)
phiB_entry.place(relx=0.86,rely=0.55,relheight=0.03,relwidth=0.07)
phiB_entry.configure(background="white")

phiB_entry.configure(foreground="black")
phiB_entry.configure(highlightbackground="#e0e0dfdfe3e3")
phiB_entry.configure(highlightcolor="#000000")
phiB_entry.configure(insertbackground="#000000")
phiB_entry.configure(selectbackground="#c4c4c4")
phiB_entry.configure(selectforeground="black")

phi2B_entry = Entry (master=root)
phi2B_entry.place(relx=0.86,rely=0.6,relheight=0.03,relwidth=0.07)
phi2B_entry.configure(background="white")

phi2B_entry.configure(foreground="black")
phi2B_entry.configure(highlightbackground="#e0e0dfdfe3e3")
phi2B_entry.configure(highlightcolor="#000000")
phi2B_entry.configure(insertbackground="#000000")

phi2B_entry.configure(selectbackground="#c4c4c4")
phi2B_entry.configure(selectforeground="black")

button_desorientation = Button (master=root)
button_desorientation.place(relx=0.81,rely=0.66,height=21,width=124)

button_desorientation.configure(activebackground="#f9f9f9")
button_desorientation.configure(activeforeground="black")
button_desorientation.configure(background="#00ff00")
button_desorientation.configure(command=desorientation)
button_desorientation.configure(foreground="black")
button_desorientation.configure(highlightcolor="black")
button_desorientation.configure(pady="0")
button_desorientation.configure(text='''MISORIENTATION''')

Cristal_label = Label (master=root)
Cristal_label.place(relx=0.66,rely=0.03,height=19,width=142)
Cristal_label.configure(text='''Crystal Parameters''')

a_cristal_label = Label (master=root)
a_cristal_label.place(relx=0.68,rely=0.06,height=19,width=12)
a_cristal_label.configure(text='''a''')

b_cristal_label = Label (master=root)
b_cristal_label.place(relx=0.68,rely=0.1,height=19,width=12)
b_cristal_label.configure(activebackground="#f9f9f9")
b_cristal_label.configure(activeforeground="black")
b_cristal_label.configure(foreground="black")
b_cristal_label.configure(highlightcolor="black")
b_cristal_label.configure(text='''b''')

c_cristal_label = Label (master=root)
c_cristal_label.place(relx=0.68,rely=0.14,height=19,width=11)
c_cristal_label.configure(activebackground="#f9f9f9")
c_cristal_label.configure(activeforeground="black")
c_cristal_label.configure(foreground="black")
c_cristal_label.configure(highlightcolor="black")
c_cristal_label.configure(text='''c''')

alp_cristal_label = Label (master=root)
alp_cristal_label.place(relx=0.67,rely=0.18,height=19,width=42)
alp_cristal_label.configure(activebackground="#f9f9f9")
alp_cristal_label.configure(activeforeground="black")
alp_cristal_label.configure(foreground="black")
alp_cristal_label.configure(highlightcolor="black")
alp_cristal_label.configure(text='''alpha''')

bet_cristal_label = Label (master=root)
bet_cristal_label.place(relx=0.67,rely=0.22,height=19,width=42)
bet_cristal_label.configure(activebackground="#f9f9f9")
bet_cristal_label.configure(activeforeground="black")
bet_cristal_label.configure(foreground="black")
bet_cristal_label.configure(highlightcolor="black")
bet_cristal_label.configure(text='''beta''')

gam_cristal_label = Label (master=root)
gam_cristal_label.place(relx=0.66,rely=0.26,height=19,width=52)
gam_cristal_label.configure(activebackground="#f9f9f9")
gam_cristal_label.configure(activeforeground="black")
gam_cristal_label.configure(foreground="black")
gam_cristal_label.configure(highlightcolor="black")
gam_cristal_label.configure(text='''gamma''')

a_entry = Entry (master=root)
a_entry.place(relx=0.7,rely=0.06,relheight=0.03,relwidth=0.06)
a_entry.configure(background="white")
a_entry.configure(insertbackground="black")

b_entry = Entry (master=root)
b_entry.place(relx=0.7,rely=0.1,relheight=0.03,relwidth=0.06)
b_entry.configure(background="white")
b_entry.configure(foreground="black")
b_entry.configure(highlightcolor="black")
b_entry.configure(insertbackground="black")
b_entry.configure(selectbackground="#c4c4c4")
b_entry.configure(selectforeground="black")

c_entry = Entry (master=root)
c_entry.place(relx=0.7,rely=0.14,relheight=0.03,relwidth=0.06)
c_entry.configure(background="white")
c_entry.configure(foreground="black")
c_entry.configure(highlightcolor="black")
c_entry.configure(insertbackground="black")
c_entry.configure(selectbackground="#c4c4c4")
c_entry.configure(selectforeground="black")

alp_entry = Entry (master=root)
alp_entry.place(relx=0.71,rely=0.18,relheight=0.03,relwidth=0.06)
alp_entry.configure(background="white")
alp_entry.configure(foreground="black")
alp_entry.configure(highlightcolor="black")
alp_entry.configure(insertbackground="black")
alp_entry.configure(selectbackground="#c4c4c4")
alp_entry.configure(selectforeground="black")

bet_entry = Entry (master=root)
bet_entry.place(relx=0.71,rely=0.22,relheight=0.03,relwidth=0.06)
bet_entry.configure(background="white")
bet_entry.configure(foreground="black")
bet_entry.configure(highlightcolor="black")
bet_entry.configure(insertbackground="black")
bet_entry.configure(selectbackground="#c4c4c4")
bet_entry.configure(selectforeground="black")

gam_entry = Entry (master=root)
gam_entry.place(relx=0.71,rely=0.26,relheight=0.03,relwidth=0.06)
gam_entry.configure(background="white")
gam_entry.configure(foreground="black")
gam_entry.configure(highlightcolor="black")
gam_entry.configure(insertbackground="black")
gam_entry.configure(selectbackground="#c4c4c4")
gam_entry.configure(selectforeground="black")

uvw_button = Checkbutton (master=root)
uvw_button.place(relx=0.75,rely=0.66,relheight=0.03,relwidth=0.04)
uvw_button.configure(text='''uvw''')
uvw_button.configure(variable=var_uvw)

e_label = Label (master=root)
e_label.place(relx=0.66,rely=0.31,height=19,width=86)
e_label.configure(text='''Max indices''')

e_entry = Entry (master=root)
e_entry.place(relx=0.74,rely=0.31,relheight=0.03,relwidth=0.05)
e_entry.configure(background="white")
e_entry.configure(insertbackground="black")

e2_label = Label (master=root)
e2_label.place(relx=0.68,rely=0.36,height=19,width=12)
e2_label.configure(text='''d''')

dm_button = Button (master=root)
dm_button.place(relx=0.7,rely=0.36,height=21,width=13)
dm_button.configure(activebackground="#f9f9f9")
dm_button.configure(activeforeground="black")
dm_button.configure(command=dm)
dm_button.configure(foreground="black")
dm_button.configure(highlightcolor="black")
dm_button.configure(pady="0")
dm_button.configure(text='''-''')

d_entry = Entry (master=root)
d_entry.place(relx=0.72,rely=0.36,relheight=0.02,relwidth=0.04)
d_entry.configure(background="white")
d_entry.configure(foreground="black")
d_entry.configure(highlightcolor="black")
d_entry.configure(insertbackground="black")
d_entry.configure(selectbackground="#c4c4c4")
d_entry.configure(selectforeground="black")

dp_button = Button (master=root)
dp_button.place(relx=0.76,rely=0.36,height=21,width=17)
dp_button.configure(activebackground="#f9f9f9")
dp_button.configure(activeforeground="black")
dp_button.configure(command=dp)
dp_button.configure(foreground="black")
dp_button.configure(highlightcolor="black")
dp_button.configure(pady="0")
dp_button.configure(text='''+''')

d_label = Label (master=root)
d_label.place(relx=0.73,rely=0.39,height=19,width=16)
d_label.configure(textvariable=d_label_var)

label_addpoleA = Label (master=root)
label_addpoleA.place(relx=0.81,rely=0.03,height=19,width=90)
label_addpoleA.configure(activebackground="#cccccc")
label_addpoleA.configure(activeforeground="black")
label_addpoleA.configure(foreground="black")
label_addpoleA.configure(highlightcolor="black")
label_addpoleA.configure(text='''Add pole A''')

pole1A_entry = Entry (master=root)
pole1A_entry.place(relx=0.81,rely=0.06,relheight=0.02
,relwidth=0.04)
pole1A_entry.configure(background="white")
pole1A_entry.configure(foreground="black")
pole1A_entry.configure(highlightcolor="black")
pole1A_entry.configure(insertbackground="black")
pole1A_entry.configure(selectbackground="#c4c4c4")
pole1A_entry.configure(selectforeground="black")

pole2A_entry = Entry (master=root)
pole2A_entry.place(relx=0.87,rely=0.06,relheight=0.02
,relwidth=0.04)
pole2A_entry.configure(background="white")
pole2A_entry.configure(foreground="black")
pole2A_entry.configure(highlightcolor="black")
pole2A_entry.configure(insertbackground="black")
pole2A_entry.configure(selectbackground="#c4c4c4")
pole2A_entry.configure(selectforeground="black")

pole3A_entry = Entry (master=root)
pole3A_entry.place(relx=0.93,rely=0.06,relheight=0.02
,relwidth=0.04)
pole3A_entry.configure(background="white")
pole3A_entry.configure(foreground="black")
pole3A_entry.configure(highlightcolor="black")
pole3A_entry.configure(insertbackground="black")
pole3A_entry.configure(selectbackground="#c4c4c4")
pole3A_entry.configure(selectforeground="black")

addpoleA_button = Button (master=root)
addpoleA_button.place(relx=0.81,rely=0.11,height=31,width=57)
addpoleA_button.configure(activebackground="#f9f9f9")
addpoleA_button.configure(activeforeground="black")
addpoleA_button.configure(command=addpoleA)
addpoleA_button.configure(foreground="black")
addpoleA_button.configure(highlightcolor="black")
addpoleA_button.configure(pady="0")
addpoleA_button.configure(text='''Add''')

symA_button = Button (master=root)
symA_button.place(relx=0.87,rely=0.11,height=31,width=71)
symA_button.configure(command=addpoleA_sym)
symA_button.configure(pady="0")
symA_button.configure(text='''Symetry''')

trace_planA_button = Button (master=root)
trace_planA_button.place(relx=0.93,rely=0.11,height=31,width=81)
trace_planA_button.configure(command=trace_planA)
trace_planA_button.configure(pady="0")
trace_planA_button.configure(text='''Draw plane''')

label_addpoleB = Label (master=root)
label_addpoleB.place(relx=0.81,rely=0.2,height=19,width=90)
label_addpoleB.configure(activebackground="#cccccc")
label_addpoleB.configure(activeforeground="black")
label_addpoleB.configure(foreground="black")
label_addpoleB.configure(highlightcolor="black")
label_addpoleB.configure(text='''Add pole B''')

pole1B_entry = Entry (master=root)
pole1B_entry.place(relx=0.81,rely=0.24,relheight=0.02
,relwidth=0.04)
pole1B_entry.configure(background="white")
pole1B_entry.configure(foreground="black")
pole1B_entry.configure(highlightcolor="black")
pole1B_entry.configure(insertbackground="black")
pole1B_entry.configure(selectbackground="#c4c4c4")
pole1B_entry.configure(selectforeground="black")

pole2B_entry = Entry (master=root)
pole2B_entry.place(relx=0.87,rely=0.24,relheight=0.02
,relwidth=0.04)
pole2B_entry.configure(background="white")
pole2B_entry.configure(foreground="black")
pole2B_entry.configure(highlightcolor="black")
pole2B_entry.configure(insertbackground="black")
pole2B_entry.configure(selectbackground="#c4c4c4")
pole2B_entry.configure(selectforeground="black")

pole3B_entry = Entry (master=root)
pole3B_entry.place(relx=0.93,rely=0.24,relheight=0.02
,relwidth=0.04)
pole3B_entry.configure(background="white")
pole3B_entry.configure(foreground="black")
pole3B_entry.configure(highlightcolor="black")
pole3B_entry.configure(insertbackground="black")
pole3B_entry.configure(selectbackground="#c4c4c4")
pole3B_entry.configure(selectforeground="black")

addpoleB_button = Button (master=root)
addpoleB_button.place(relx=0.81,rely=0.28,height=31,width=55)
addpoleB_button.configure(activebackground="#f9f9f9")
addpoleB_button.configure(activeforeground="black")
addpoleB_button.configure(command=addpoleB)
addpoleB_button.configure(foreground="black")
addpoleB_button.configure(highlightcolor="black")
addpoleB_button.configure(pady="0")
addpoleB_button.configure(text='''Add''')

symB_button = Button (master=root)
symB_button.place(relx=0.87,rely=0.28,height=31,width=71)
symB_button.configure(command=addpoleB_sym)
symB_button.configure(pady="0")
symB_button.configure(text='''Symetry''')

trace_planB_button = Button (master=root)
trace_planB_button.place(relx=0.93,rely=0.28,height=31,width=81)
trace_planB_button.configure(command=trace_planB)
trace_planB_button.configure(pady="0")
trace_planB_button.configure(text='''Draw plane''')

show_ind_button = Checkbutton (master=root)
show_ind_button.place(relx=0.81,rely=0.7,relheight=0.03
                ,relwidth=0.11)
show_ind_button.configure(text='''Show indices''')
show_ind_button.configure(variable=show_ind)

show_angle_button = Checkbutton (master=root)
show_angle_button.place(relx=0.81,rely=0.74,relheight=0.03
                ,relwidth=0.11)
show_angle_button.configure(text='''Show angle''')
show_angle_button.configure(variable=show_angle)

show_axe_button = Checkbutton (master=root)
show_axe_button.place(relx=0.81,rely=0.78,relheight=0.03
                ,relwidth=0.11)
show_axe_button.configure(text='''Show axes''')
show_axe_button.configure(variable=show_axe)

show_num_button = Checkbutton (master=root)
show_num_button.place(relx=0.81,rely=0.82,relheight=0.03
                ,relwidth=0.11)
show_num_button.configure(text='''Show numbers''')
show_num_button.configure(variable=show_num)

menu = Menu(master=root)
filemenu = Menu(menu, tearoff=0)
menu.add_cascade(label="Save", menu=filemenu)

root.config(menu=menu)
filemenu.add_command(label="Save data", command=file_save)
filemenu.add_command(label="Save figure", command=image_save)
######################################################################################################
######## importer des structures cristallines depuis un fichier Nom,a,b,c,alpha,beta,gamma,space group
######################################################################################################

def structure(i0):
    global x0

    a_entry.delete(0,END)
    a_entry.insert(1,eval(x0[i0][1]))
    b_entry.delete(0,END)
    b_entry.insert(1,eval(x0[i0][2]))
    c_entry.delete(0,END)
    c_entry.insert(1,eval(x0[i0][3]))
    alp_entry.delete(0,END)
    alp_entry.insert(1,eval(x0[i0][4]))
    bet_entry.delete(0,END)
    bet_entry.insert(1,eval(x0[i0][5]))
    gam_entry.delete(0,END)
    gam_entry.insert(1,eval(x0[i0][6]))

def createstructure(i):
    return lambda:structure(i)

cristalmenu=Menu(menu,tearoff=0)
menu.add_cascade(label="Structures", menu=cristalmenu)
file_struct=open(os.path.join(os.path.dirname(__file__), 'structure.txt') ,"r")

x0=[]
i=0
for line in file_struct:
    x0.append(map(str, line.split()))
    cristalmenu.add_command(label=x0[i][0], command=createstructure(i))
    i=i+1

file_struct.close()

#######################################################################################################

phi1A_entry.insert(0,0)
phiA_entry.insert(0,0)
phi2A_entry.insert(0,0)
phi1B_entry.insert(0,0)
phiB_entry.insert(0,0)
phi2B_entry.insert(0,0)
e_entry.insert(1,1)
d_entry.insert(1,1)

mainloop()


"""
MOST OF THIS CODE IS NOT USED
ITS COPY/PASTED AND LEFT HERE FOR CONVENIENCE
"""

import os
import sys

# in case our module isn't installed (running from this folder)
if not os.path.abspath('../../../') in sys.path:
    sys.path.append('../../../') # helps spyder get docs

import swhlab
import swhlab.common as cm
import matplotlib.pyplot as plt
import numpy as np

import warnings # suppress VisibleDeprecationWarning warning
warnings.filterwarnings("ignore", category=np.VisibleDeprecationWarning)

def analyzeSweep(abf,plotToo=True,color=None,label=None):
    Y=abf.sweepYsmartbase()[abf.pointsPerSec*.5:]
    AV,SD=np.average(Y),np.std(Y)
    dev=5 # number of stdevs from the avg to set the range
    R1,R2=[(AV-SD)*dev,(AV+SD)*dev]
    nBins=1000
    hist,bins=np.histogram(Y,bins=nBins,range=[R1,R2],density=True)
    histSmooth=abf.convolve(hist,cm.kernel_gaussian(nBins/5))

    if plotToo:
        plt.plot(bins[1:],hist,'.',color=color,alpha=.2,ms=10)
        plt.plot(bins[1:],histSmooth,'-',color=color,lw=5,alpha=.5,label=label)

    return

if __name__=="__main__":
    #abfFile=R"C:\Users\scott\Documents\important\demodata\abfs\16d07022.abf"
    abfFile=R"X:\Data\2P01\2016\2016-09-01 PIR TGOT\16d07022.abf"
    abf=swhlab.ABF(abfFile)

    # prepare figure
    plt.figure(figsize=(10,10))
    plt.grid()
    plt.title("smart baseline value distribution")
    plt.xlabel(abf.units2)
    plt.ylabel("normalized density")

    # do the analysis
    abf.kernel=abf.kernel_gaussian(sizeMS=500)
    abf.setsweep(175)
    analyzeSweep(abf,color='b',label="baseline")
    abf.setsweep(200)
    analyzeSweep(abf,color='g',label="TGOT")
    abf.setsweep(375)
    analyzeSweep(abf,color='y',label="washout")

    # show figure
    plt.legend()
    plt.margins(0,.1)
    plt.show()

    print("DONE")


# vim: set encoding=utf-8

#  Copyright (c) 2016 Intel Corporation
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#

"""Test guassian mixture models against known values"""
import unittest
from collections import Counter
from numpy.testing import assert_almost_equal
from sparktkregtests.lib import sparktk_test

class GMMModelTest(sparktk_test.SparkTKTestCase):

    def setUp(self):
        data_file = self.get_file("gmm_data.csv")
        self.frame = self.context.frame.import_csv(
            data_file, schema=[("x1", float), ("x2", float)])

    def test_train(self):
        """ Verify that model operates as expected in straightforward case"""
        model = self.context.models.clustering.gmm.train(
                    self.frame, ["x1", "x2"],
                    column_scalings=[1.0, 1.0],
                    k=5,
                    max_iterations=500,
                    seed=20,
                    convergence_tol=0.0001)

        actual_mu = [g.mu for g in model.gaussians]
        actual_sigma = [g.sigma for g in model.gaussians]
        expected_mu = \
            [[7.0206, -10.1706],
            [7.8322, -10.2383],
            [-1.3816, 6.7215],
            [-0.04184, 5.8039],
            [-4.1743, 8.5564]]
        expected_sigma = \
            [[[0.2471, -0.3325],
            [-0.3325, 0.5828]],
            [[2.3005, 0.6906],
            [0.6906, 2.1103]],
            [[1.5941, -3.5325],
            [-3.5325, 7.8424]],
            [[0.9849, 0.04328],
            [0.04328, 0.3736]],
            [[0.1168, 0.1489],
            [0.1489, 0.9757]]]
        assert_almost_equal(actual_mu, expected_mu, decimal=3)
        assert_almost_equal(actual_sigma, expected_sigma, decimal=3)

    def test_predict(self):
        """ Tests output of predict """
        model = self.context.models.clustering.gmm.train(
            self.frame, ["x1", "x2"],
            column_scalings=[1.0, 1.0],
            k=3,
            max_iterations=100,
            seed=15)
        predicted_frame = model.predict(self.frame)
        results_df = predicted_frame.to_pandas(self.frame.count())

        actual_cluster_sizes = Counter(results_df["predicted_cluster"].tolist())
        expected_cluster_sizes = {2: 27, 0: 17, 1: 6}
        self.assertItemsEqual(actual_cluster_sizes, expected_cluster_sizes)

    def test_gmm_1_cluster(self):
        """Test gmm doesn't error on k=1"""
        model = self.context.models.clustering.gmm.train(
            self.frame, ["x1", "x2"], [1.0, 1.0], k=1)

    def test_gmm_1_iteration(self):
        """Train on 1 iteration only, shouldn't throw exception"""
        model = self.context.models.clustering.gmm.train(
            self.frame, ["x1"], column_scalings=[1.0],
            max_iterations=1)

    def test_gmm_high_convergence(self):
        """Train on high convergence, should not throw exception"""
        model = self.context.models.clustering.gmm.train(
            self.frame, ["x1", "x2"], column_scalings=[1.0, 1.0],
            convergence_tol=1e6)

    def test_gmm_negative_seed(self):
        """Train on negative seed, shouldn't throw exception"""
        model = self.context.models.clustering.gmm.train(
            self.frame, ["x1", "x2"], column_scalings=[1.0, 1.0],
            seed=-20)

    def test_gmm_0_scalings(self):
        """all-zero column scalings, shouldn't throw exception"""
        model = self.context.models.clustering.gmm.train(
            self.frame, ["x1", "x2"], column_scalings=[0.0, 0.0])

    def test_gmm_negative_scalings(self):
        """negative column scalings, shouldn't throw exception"""
        model = self.context.models.clustering.gmm.train(
            self.frame, ["x1", "x2"], column_scalings=[-1.0, -1.0])

    def test_gmm_empty_frame(self):
        """ Verify that model operates as expected in straightforward case"""
        # Train on an empty frame
        block_data = []
        frame = self.context.frame.create(
            block_data,
            [("x1", float)])

        with self.assertRaisesRegexp(
                Exception, "empty collection"):
            self.context.models.clustering.gmm.train(
                frame, ["x1"], column_scalings=[1.0])

    def test_0_classes_errors(self):
        """Train on 0 classes, should error"""
        with self.assertRaisesRegexp(
                Exception, "k must be at least 1"):
            self.context.models.clustering.gmm.train(
                self.frame, ["x1", "x2"], column_scalings=[1.0, 1.0], k=0)

    def test_negative_classes(self):
        """Train on negative classes, should error"""
        with self.assertRaisesRegexp(
                Exception, "k must be at least 1"):
            self.context.models.clustering.gmm.train(
                self.frame, ["x1"], column_scalings=[1.0], k=-5)

    def test_0_iterations(self):
        """Train on 0 iterations, should error"""
        with self.assertRaisesRegexp(
                Exception, "maxIterations must be a positive value"):
            self.context.models.clustering.gmm.train(
                self.frame, ["x1"], column_scalings=[1.0],
                max_iterations=0)

    def test_negative_iterations(self):
        """Train on negative iterations, should error"""
        with self.assertRaisesRegexp(
                Exception, "maxIterations must be a positive value"):
            self.context.models.clustering.gmm.train(
                self.frame, ["x1"], column_scalings=[1.0],
                max_iterations=-20)

    def test_wrong_column_scalings(self):
        """Insufficient column scalings, should error"""
        with self.assertRaisesRegexp(
                Exception, "columnWeights must not be null or empty"):
            self.context.models.clustering.gmm.train(
                self.frame, ["x1"], column_scalings=[])

    def test_too_many_column_scalings(self):
        """Extra column scalings, should error"""
        with self.assertRaisesRegexp(
                Exception,
                "Length of columnWeights and observationColumns.*"):
            self.context.models.clustering.gmm.train(
                self.frame, ["x1", "x2"], column_scalings=[1.0, 1.0, 1.0])

    def test_missing_column_scalings(self):
        """Missing column scalings, should error"""
        with self.assertRaisesRegexp(
            TypeError, "train\(\) takes at least 3 arguments.*"):
            self.context.models.clustering.gmm.train(
                self.frame, ["x1", "x2"], k=2)

if __name__ == "__main__":
    unittest.main()


"""
======================================
Sparse inverse covariance estimation
======================================

Using the GraphLasso estimator to learn a covariance and sparse precision
from a small number of samples.

To estimate a probabilistic model (e.g. a Gaussian model), estimating the
precision matrix, that is the inverse covariance matrix, is as important
as estimating the covariance matrix. Indeed a Gaussian model is
parametrized by the precision matrix.

To be in favorable recovery conditions, we sample the data from a model
with a sparse inverse covariance matrix. In addition, we ensure that the
data is not too much correlated (limiting the largest coefficient of the
precision matrix) and that there a no small coefficients in the
precision matrix that cannot be recovered. In addition, with a small
number of observations, it is easier to recover a correlation matrix
rather than a covariance, thus we scale the time series.

Here, the number of samples is slightly larger than the number of
dimensions, thus the empirical covariance is still invertible. However,
as the observations are strongly correlated, the empirical covariance
matrix is ill-conditioned and as a result its inverse --the empirical
precision matrix-- is very far from the ground truth.

If we use l2 shrinkage, as with the Ledoit-Wolf estimator, as the number
of samples is small, we need to shrink a lot. As a result, the
Ledoit-Wolf precision is fairly close to the ground truth precision, that
is not far from being diagonal, but the off-diagonal structure is lost.

The l1-penalized estimator can recover part of this off-diagonal
structure. It learns a sparse precision. It is not able to
recover the exact sparsity pattern: it detects too many non-zero
coefficients. However, the highest non-zero coefficients of the l1
estimated correspond to the non-zero coefficients in the ground truth.
Finally, the coefficients of the l1 precision estimate are biased toward
zero: because of the penalty, they are all smaller than the corresponding
ground truth value, as can be seen on the figure.

Note that, the color range of the precision matrices is tweaked to
improve readability of the figure. The full range of values of the
empirical precision is not displayed.

The alpha parameter of the GraphLasso setting the sparsity of the model is
set by internal cross-validation in the GraphLassoCV. As can be
seen on figure 2, the grid to compute the cross-validation score is
iteratively refined in the neighborhood of the maximum.
"""
print(__doc__)
# author: Gael Varoquaux <gael.varoquaux@inria.fr>
# License: BSD 3 clause
# Copyright: INRIA

import numpy as np
from scipy import linalg
from sklearn.datasets import make_sparse_spd_matrix
from sklearn.covariance import GraphLassoCV, ledoit_wolf
import matplotlib.pyplot as plt

##############################################################################
# Generate the data
n_samples = 60
n_features = 20

prng = np.random.RandomState(1)
prec = make_sparse_spd_matrix(n_features, alpha=.98,
                              smallest_coef=.4,
                              largest_coef=.7,
                              random_state=prng)
cov = linalg.inv(prec)
d = np.sqrt(np.diag(cov))
cov /= d
cov /= d[:, np.newaxis]
prec *= d
prec *= d[:, np.newaxis]
X = prng.multivariate_normal(np.zeros(n_features), cov, size=n_samples)
X -= X.mean(axis=0)
X /= X.std(axis=0)

##############################################################################
# Estimate the covariance
emp_cov = np.dot(X.T, X) / n_samples

model = GraphLassoCV()
model.fit(X)
cov_ = model.covariance_
prec_ = model.precision_

lw_cov_, _ = ledoit_wolf(X)
lw_prec_ = linalg.inv(lw_cov_)

##############################################################################
# Plot the results
plt.figure(figsize=(10, 6))
plt.subplots_adjust(left=0.02, right=0.98)

# plot the covariances
covs = [('Empirical', emp_cov), ('Ledoit-Wolf', lw_cov_),
        ('GraphLasso', cov_), ('True', cov)]
vmax = cov_.max()
for i, (name, this_cov) in enumerate(covs):
    plt.subplot(2, 4, i + 1)
    plt.imshow(this_cov, interpolation='nearest', vmin=-vmax, vmax=vmax,
               cmap=plt.cm.RdBu_r)
    plt.xticks(())
    plt.yticks(())
    plt.title('%s covariance' % name)

# plot the precisions
precs = [('Empirical', linalg.inv(emp_cov)), ('Ledoit-Wolf', lw_prec_),
         ('GraphLasso', prec_), ('True', prec)]
vmax = .9 * prec_.max()
for i, (name, this_prec) in enumerate(precs):
    ax = plt.subplot(2, 4, i + 5)
    plt.imshow(np.ma.masked_equal(this_prec, 0),
               interpolation='nearest', vmin=-vmax, vmax=vmax,
               cmap=plt.cm.RdBu_r)
    plt.xticks(())
    plt.yticks(())
    plt.title('%s precision' % name)
    ax.set_axis_bgcolor('.7')

# plot the model selection metric
plt.figure(figsize=(4, 3))
plt.axes([.2, .15, .75, .7])
plt.plot(model.cv_alphas_, np.mean(model.grid_scores, axis=1), 'o-')
plt.axvline(model.alpha_, color='.5')
plt.title('Model selection')
plt.ylabel('Cross-validation score')
plt.xlabel('alpha')

plt.show()


"""
=========================
Multi-dimensional scaling
=========================

An illustration of the metric and non-metric MDS on generated noisy data.

The reconstructed points using the metric MDS and non metric MDS are slightly
shifted to avoid overlapping.
"""

# Author: Nelle Varoquaux <nelle.varoquaux@gmail.com>
# Licence: BSD

print(__doc__)
import numpy as np

from matplotlib import pyplot as plt
from matplotlib.collections import LineCollection

from sklearn import manifold
from sklearn.metrics import euclidean_distances
from sklearn.decomposition import PCA

n_samples = 20
seed = np.random.RandomState(seed=3)
X_true = seed.randint(0, 20, 2 * n_samples).astype(np.float)
X_true = X_true.reshape((n_samples, 2))
# Center the data
X_true -= X_true.mean()

similarities = euclidean_distances(X_true)

# Add noise to the similarities
noise = np.random.rand(n_samples, n_samples)
noise = noise + noise.T
noise[np.arange(noise.shape[0]), np.arange(noise.shape[0])] = 0
similarities += noise

mds = manifold.MDS(n_components=2, max_iter=3000, eps=1e-9, random_state=seed,
                   dissimilarity="precomputed", n_jobs=1)
pos = mds.fit(similarities).embedding_

nmds = manifold.MDS(n_components=2, metric=False, max_iter=3000, eps=1e-12,
                    dissimilarity="precomputed", random_state=seed, n_jobs=1,
                    n_init=1)
npos = nmds.fit_transform(similarities, init=pos)

# Rescale the data
pos *= np.sqrt((X_true ** 2).sum()) / np.sqrt((pos ** 2).sum())
npos *= np.sqrt((X_true ** 2).sum()) / np.sqrt((npos ** 2).sum())

# Rotate the data
clf = PCA(n_components=2)
X_true = clf.fit_transform(X_true)

pos = clf.fit_transform(pos)

npos = clf.fit_transform(npos)

fig = plt.figure(1)
ax = plt.axes([0., 0., 1., 1.])

plt.scatter(X_true[:, 0], X_true[:, 1], c='r', s=20)
plt.scatter(pos[:, 0], pos[:, 1], s=20, c='g')
plt.scatter(npos[:, 0], npos[:, 1], s=20, c='b')
plt.legend(('True position', 'MDS', 'NMDS'), loc='best')

similarities = similarities.max() / similarities * 100
similarities[np.isinf(similarities)] = 0

# Plot the edges
start_idx, end_idx = np.where(pos)
#a sequence of (*line0*, *line1*, *line2*), where::
#            linen = (x0, y0), (x1, y1), ... (xm, ym)
segments = [[X_true[i, :], X_true[j, :]]
            for i in range(len(pos)) for j in range(len(pos))]
values = np.abs(similarities)
lc = LineCollection(segments,
                    zorder=0, cmap=plt.cm.hot_r,
                    norm=plt.Normalize(0, values.max()))
lc.set_array(similarities.flatten())
lc.set_linewidths(0.5 * np.ones(len(segments)))
ax.add_collection(lc)

plt.show()


"""
This test module has tests relating to t-plots

All functions in /calculations/tplot.py are tested here.
The purposes are:

    - testing the user-facing API function (tplot)
    - testing individual low level functions against known results.

Functions are tested against pre-calculated values on real isotherms.
All pre-calculated data for characterisation can be found in the
/.conftest file together with the other isotherm parameters.
"""

import pytest
from matplotlib.testing.decorators import cleanup
from numpy import isclose

import pygaps
import pygaps.utilities.exceptions as pgEx

from .conftest import DATA
from .conftest import DATA_N77_PATH

@pytest.mark.characterisation
class TestTPlot():
    """Tests t-plot calculations."""
    def test_alphas_checks(self, basic_pointisotherm):
        """Checks for built-in safeguards."""

        # Will raise a "no suitable model exception"
        with pytest.raises(pgEx.ParameterError):
            pygaps.t_plot(basic_pointisotherm, thickness_model='random')

    @pytest.mark.parametrize('sample', [sample for sample in DATA])
    def test_tplot(self, sample):
        """Test calculation with several model isotherms."""
        sample = DATA[sample]
        # exclude datasets where it is not applicable
        if sample.get('t_area', None):

            filepath = DATA_N77_PATH / sample['file']
            isotherm = pygaps.isotherm_from_json(filepath)

            res = pygaps.t_plot(isotherm)
            results = res.get('results')

            err_relative = 0.1  # 10 percent
            err_absolute_area = 0.1  # units
            err_absolute_volume = 0.01  # units

            assert isclose(
                results[-1].get('adsorbed_volume'), sample['t_pore_volume'],
                err_relative, err_absolute_area
            )
            assert isclose(
                results[0].get('area'), sample['t_area'], err_relative,
                err_absolute_volume
            )

    def test_tplot_choice(self):
        """Test choice of points."""

        sample = DATA['MCM-41']
        filepath = DATA_N77_PATH / sample['file']
        isotherm = pygaps.isotherm_from_json(filepath)

        res = pygaps.t_plot(isotherm, limits=[0.7, 1.0])
        results = res.get('results')

        err_relative = 0.1  # 10 percent
        err_absolute_area = 0.1  # units
        err_absolute_volume = 0.01  # units

        assert isclose(
            results[-1].get('adsorbed_volume'), sample['t_pore_volume'],
            err_relative, err_absolute_area
        )
        assert isclose(
            results[-1].get('area'), sample['s_t_area'], err_relative,
            err_absolute_volume
        )

    @cleanup
    def test_tplot_output(self):
        """Test verbosity."""
        sample = DATA['MCM-41']
        filepath = DATA_N77_PATH / sample['file']
        isotherm = pygaps.isotherm_from_json(filepath)
        pygaps.t_plot(isotherm, 'Halsey', verbose=True)


# Opus/UrbanSim urban simulation software.
# Copyright (C) 2005-2009 University of Washington
# See opus_core/LICENSE

# script to produce a number of PSRC indicators --
# this illustrates using traits-based configurations programatically

from opus_core.configurations.dataset_pool_configuration import DatasetPoolConfiguration
from opus_core.indicator_framework.core.source_data import SourceData
from opus_core.indicator_framework.image_types.matplotlib_map import Map
from opus_core.indicator_framework.image_types.matplotlib_chart import Chart
from opus_core.indicator_framework.image_types.table import Table
from opus_core.indicator_framework.image_types.geotiff_map import GeotiffMap
from opus_core.indicator_framework.image_types.dataset_table import DatasetTable
from opus_core.indicator_framework.image_types.matplotlib_lorenzcurve import LorenzCurve

#some cache_directories and run descriptions
#cache_directory = r'Y:/urbansim_cache/run_1090.2006_11_14_12_12'
#run_description = '(run 1090 - double highway capacity 11/28/2006)'
#cache_directory = r'Y:/urbansim_cache/run_1091.2006_11_14_12_12'
#run_description = '(run 1091 - baseline 11/28/2006)'
#cache_directory = r'D:\urbansim_cache\run_1454.2006_12_12_16_28'
#run_description = '(run 1454 - travel data from quick travel model)'
cache_directory = r'D:\urbansim_cache\run_1090.2006_11_14_12_12'
run_description = '(run 1453 - travel data from full travel model)'
#cache_directory = r'Y:\urbansim_cache\run_1431.2006_12_08_09_45'
#run_description = '(run 1431 - baseyear travel data from travel model run)'
#cache_directory = r'D:\urbansim_cache\run_1154.2006_11_17_20_06'
#run_description = '(run 1154 - no ugb + double highway capacity 11/28/2006)'
#cache_directory = r'D:\urbansim_cache\run_1155.2006_11_17_20_07'
#run_description = '(run 1155 - no ugb 11/28/2006)'

source_data = SourceData(
    cache_directory = cache_directory,
    run_description = run_description,
    years = [1980, 1981, 1982],
    dataset_pool_configuration = DatasetPoolConfiguration(
        package_order=['eugene','urbansim','opus_core'],
        ),
)
single_year_requests = [
    Table(
        attribute = 'urbansim.zone.population',
        dataset_name = 'zone',
        source_data = source_data,
        ),
    Table(
        attribute = 'urbansim.zone.number_of_jobs',
        dataset_name = 'zone',
        source_data = source_data,
        ),

    Map(
        attribute = 'urbansim.zone.population',
        scale = [1, 60000],
        dataset_name = 'zone',
        source_data = source_data,
        ),
    Map(
        attribute = 'urbansim.zone.number_of_jobs',
        scale = [1, 60000],
        dataset_name = 'zone',
        source_data = source_data,
        ),
    Map(
        scale = [-8000, 40000],
        attribute = 'urbansim_population_change',
        source_data = source_data,
        expression = {'operation': 'change',
                      'operands': ['urbansim.zone.population']},
        dataset_name = 'zone',
        ),
    Map(
        scale = [-2000, 40000],
        attribute = 'urbansim_employment_change',
        source_data = source_data,
        expression = {'operation': 'change',
                      'operands': ['urbansim.zone.number_of_jobs']},
        dataset_name = 'zone',
        ),
    ]

source_data = SourceData(
    cache_directory = cache_directory,
    run_description = run_description,
    years = [1980, 1981, 1982],
    dataset_pool_configuration = DatasetPoolConfiguration(
        package_order=['eugene','urbansim','opus_core'],
        ),
)

multi_year_requests = [
    Table(
        attribute = 'alldata.aggregate_all(urbansim.gridcell.residential_units, function=sum)',
        dataset_name = 'alldata',
        source_data = source_data,
        name = 'residential_units'
        ),
    Chart(
        attribute = 'alldata.aggregate_all(urbansim.gridcell.residential_units, function=sum)',
        dataset_name = 'alldata',
        source_data = source_data,
        name = 'residential_units'
        ),
    Table(
        attribute = 'alldata.aggregate_all(urbansim.gridcell.number_of_jobs, function=sum)',
        dataset_name = 'alldata',
        source_data = source_data,
        name =  'number_of_jobs'
        ),

    ]

if __name__ == '__main__':
    from opus_core.indicator_framework.core.indicator_factory import IndicatorFactory

    IndicatorFactory().create_indicators(
        indicators = single_year_requests,
        display_error_box = False,
        show_results = True)
    IndicatorFactory().create_indicators(
        indicators = multi_year_requests,
        display_error_box = False,
        show_results = True)


# -*- coding: utf-8 -*-
import os
import csv
import datetime
import pandas as pd
from itertools import chain
from functools import partial

from anacode import codes

def backup(root, files):
    """Backs up `files` from `root` directory and return list of backed up
    file names. Backed up files will have datetime suffix appended to original
    file name.

    :param root: Absolute path to folder where files to backup are located
    :type root: str
    :param files: Names of files that needs backing up
    :type files: str
    :return: list -- List of backed up file names
    """
    backed_up = []
    join = os.path.join
    root_contents = os.listdir(root)
    dt_str = datetime.datetime.utcnow().strftime('%Y%m%d%H%M%S')
    for file_name in files:
        if file_name not in root_contents:
            continue
        new_name = file_name + '_' + dt_str
        os.rename(join(root, file_name), join(root, new_name))
        backed_up.append(new_name)
    return backed_up

HEADERS = {
    'categories': [u'doc_id', u'text_order', u'category', u'probability'],
    'concepts': [u'doc_id', u'text_order', u'concept', u'freq',
                 u'relevance_score', u'concept_type'],
    'concepts_surface_strings': [u'doc_id', u'text_order', u'concept',
                                 u'surface_string', u'text_span'],
    'sentiments': [u'doc_id', u'text_order', u'sentiment_value'],
    'absa_entities': [u'doc_id', u'text_order', u'entity_name', u'entity_type',
                      u'surface_string', u'text_span'],
    'absa_normalized_texts': [u'doc_id', u'text_order', u'normalized_text'],
    'absa_relations': [u'doc_id', u'text_order', u'relation_id',
                       u'opinion_holder', u'restriction', u'sentiment_value',
                       u'is_external', u'surface_string', u'text_span'],
    'absa_relations_entities': [u'doc_id', u'text_order', u'relation_id',
                                u'entity_type', u'entity_name'],
    'absa_evaluations': [u'doc_id', u'text_order', u'evaluation_id',
                         u'sentiment_value', u'surface_string', u'text_span'],
    'absa_evaluations_entities': [u'doc_id', u'text_order', u'evaluation_id',
                                  u'entity_type', u'entity_name'],
}

# `anacode.agg.aggregations.ApiDataset.from_path` depends
# on ordering of files defined in values here
CSV_FILES = {
    'categories': ['categories.csv'],
    'concepts': ['concepts.csv', 'concepts_surface_strings.csv'],
    'sentiments': ['sentiments.csv'],
    'absa': [
        'absa_entities.csv', 'absa_normalized_texts.csv',
        'absa_relations.csv', 'absa_relations_entities.csv',
        'absa_evaluations.csv', 'absa_evaluations_entities.csv'
    ]
}

def categories_to_list(doc_id, analyzed, single_document=False):
    """Converts categories response to flat list with doc_id included.

    :param doc_id: Will be inserted to each row as first element
    :param analyzed: Response json from anacode api for categories call
    :type analyzed: list
    :param single_document: Is analysis describing just one document
    :type single_document: bool
    :return: dict -- Dictionary with one key 'categories' pointing to flat list
     of categories
    """
    cat_list = []
    for order, text_analyzed in enumerate(analyzed):
        for result_dict in text_analyzed:
            row = [doc_id, 0, result_dict.get('label'),
                   result_dict.get('probability')]
            if single_document:
                row[1] += order
            else:
                row[0] += order
            cat_list.append(row)
    return {'categories': cat_list}

def concepts_to_list(doc_id, analyzed, single_document=False):
    """Converts concepts response to flat lists with doc_id included

    :param doc_id: Will be inserted to each row as first element
    :param analyzed: Response json from anacode api for concepts call
    :type analyzed: list
    :param single_document: Is analysis describing just one document
    :type single_document: bool
    :return: dict -- Dictionary with two keys: 'concepts' pointing to flat list
     of found concepts and their metadata and 'concepts_surface_strings'
     pointing to flat list of strings realizing found concepts
    """
    con_list, exp_list = [], []
    for order, text_analyzed in enumerate(analyzed):
        for concept in text_analyzed or []:
            row = [doc_id, 0, concept.get('concept'),
                   concept.get('freq'), concept.get('relevance_score'),
                   concept.get('type')]
            if single_document:
                row[1] += order
            else:
                row[0] += order
            con_list.append(row)
            for string in concept.get('surface', []):
                surface_str, span = string['surface_string'], string['span']
                exp_list.append([row[0], row[1], concept.get('concept'),
                                 surface_str, '-'.join(map(str, span))])
    return {'concepts': con_list, 'concepts_surface_strings': exp_list}

def sentiments_to_list(doc_id, analyzed, single_document=False):
    """Converts sentiments response to flat lists with doc_id included

    :param doc_id: Will be inserted to each row as first element
    :param analyzed: Response json from anacode api for sentiment call
    :type analyzed: list
    :param single_document: Is analysis describing just one document
    :type single_document: bool
    :return: dict -- Dictionary with one key 'sentiments' pointing to flat list
     of sentiment probabilities
    """
    sen_list = []
    for order, sentiment in enumerate(analyzed):
        row = [doc_id, 0, sentiment['sentiment_value']]
        if single_document:
            # this should not happen
            row[1] += order
        else:
            row[0] += order
        sen_list.append(row)
    return {'sentiments': sen_list}

def _absa_entities_to_list(doc_id, order, entities):
    ent_list = []
    for entity_dict in entities:
        text_span = '-'.join(map(str, entity_dict['surface']['span']))
        surface_string = entity_dict['surface']['surface_string']
        for semantics in entity_dict['semantics']:
            row = [doc_id, order, semantics['value'], semantics['type'],
                   surface_string, text_span]
            ent_list.append(row)
    return ent_list

def _absa_normalized_text_to_list(doc_id, order, normalized_text):
    return [[doc_id, order, normalized_text]]

def _absa_relations_to_list(doc_id, order, relations):
    rel_list, ent_list = [], []
    for rel_index, rel in enumerate(relations):
        rel_row = [doc_id, order, rel_index,
                   rel['semantics']['opinion_holder'],
                   rel['semantics']['restriction'],
                   rel['semantics']['sentiment_value'],
                   rel['external_entity'],
                   rel['surface']['surface_string'],
                   '-'.join(map(str, rel['surface']['span']))]
        rel_list.append(rel_row)
        for ent in rel['semantics'].get('entity', []):
            ent_row = [doc_id, order, rel_index, ent['type'], ent['value']]
            ent_list.append(ent_row)
    return rel_list, ent_list

def _absa_evaluations_to_list(doc_id, order, evaluations):
    eval_list, ent_list = [], []
    for eval_index, evaluation in enumerate(evaluations):
        eval_row = [doc_id, order, eval_index,
                    evaluation['semantics']['sentiment_value'],
                    evaluation['surface']['surface_string'],
                    '-'.join(map(str, evaluation['surface']['span']))]
        eval_list.append(eval_row)
        for ent in evaluation['semantics'].get('entity', []):
            ent_row = [doc_id, order, eval_index, ent['type'], ent['value']]
            ent_list.append(ent_row)
    return eval_list, ent_list

def absa_to_list(doc_id, analyzed, single_document=False):
    """Converts ABSA response to flat lists with doc_id included

    :param doc_id: Will be inserted to each row as first element
    :param analyzed: Response json from anacode api for ABSA call
    :type analyzed: list
    :param single_document: Is analysis describing just one document
    :type single_document: bool
    :return: dict -- Dictionary with six keys: 'absa_entities' pointing to flat
     list of found entities with metadata, 'absa_normalized_texts' pointing to
     flat list of normalized chinese texts, 'absa_relations' pointing to found
     entity relations with metadata, 'absa_relations_entities' pointing to flat
     list of entities that belong to absa relations, 'absa_evaluations'
     pointing to flat list of entity evaluations with metadata and
     'absa_evaluations_entities' specifying entities in absa_evaluations
    """
    absa = {
        'absa_entities': [],
        'absa_normalized_texts': [],
        'absa_relations': [],
        'absa_relations_entities': [],
        'absa_evaluations': [],
        'absa_evaluations_entities': []
    }
    for order, text_analyzed in enumerate(analyzed):
        if single_document:
            current_id = doc_id
            text_order = order
        else:
            current_id = doc_id + order
            text_order = 0

        entities = text_analyzed['entities']
        ents = _absa_entities_to_list(current_id, text_order, entities)
        text = text_analyzed['normalized_text']
        texts = _absa_normalized_text_to_list(current_id, text_order, text)
        relations = text_analyzed['relations']
        rels, rel_ents = _absa_relations_to_list(current_id, text_order,
                                                 relations)
        evaluations = text_analyzed['evaluations']
        evals, eval_ents = _absa_evaluations_to_list(current_id, text_order,
                                                     evaluations)
        absa['absa_entities'].extend(ents)
        absa['absa_normalized_texts'].extend(texts)
        absa['absa_relations'].extend(rels)
        absa['absa_relations_entities'].extend(rel_ents)
        absa['absa_evaluations'].extend(evals)
        absa['absa_evaluations_entities'].extend(eval_ents)
    return absa

class Writer(object):
    """Base "abstract" class containing common methods that are
    needed by all implementations of Writer interface.

    The writer interface consists of init, close and write_bulk methods.

    """
    def __init__(self):
        self.ids = {'scrape': 0, 'analyze': 0}

    def write_row(self, call_type, call_result):
        """Decides what kind of data it got and calls appropriate write method.

        :param call_type: Library's ID of anacode call
        :type call_type: int
        :param call_result: JSON response from Anacode API
        :type call_result: list
        """
        if call_type == codes.SCRAPE:
            self.write_scrape(call_result)
        if call_type == codes.ANALYZE:
            self.write_analysis(call_result)

    def _add_new_data_from_dict(self, new_data):
        """Not implemented here!

        Used by write methods to submit new Anacode API response data for storage.

        :param new_data: dict; keys are data sets names and values are
         flat lists of rows
        :type new_data: dict
        """
        pass

    def write_scrape(self, scraped):
        self.ids['scrape'] += 1

    def write_analysis(self, analyzed):
        """Inspects analysis result for performed analysis and delegates
        persisting of results to appropriate write methods.

        :param analyzed: JSON object analysis response
        :type: dict
        """
        single_document = analyzed.get('single_document', False)
        analyzed_length = 1

        if 'categories' in analyzed:
            categories = analyzed['categories']
            self.write_categories(categories, single_document=single_document)
            if not single_document:
                analyzed_length = len(categories)
        if 'concepts' in analyzed:
            concepts = analyzed['concepts']
            self.write_concepts(concepts, single_document=single_document)
            if not single_document:
                analyzed_length = len(concepts)
        if 'sentiment' in analyzed:
            sentiment = analyzed['sentiment']
            self.write_sentiment(sentiment, single_document=single_document)
            if not single_document:
                analyzed_length = len(sentiment)
        if 'absa' in analyzed:
            absa = analyzed['absa']
            self.write_absa(analyzed['absa'], single_document=single_document)
            if not single_document:
                analyzed_length = len(absa)

        self.ids['analyze'] += analyzed_length

    def write_categories(self, analyzed, single_document=False):
        """Converts categories analysis result to flat lists and stores them.

        :param analyzed: JSON categories analysis result
        :type analyzed: list
        :param single_document: Is analysis describing just one document
        :type single_document: bool
        """
        doc_id = self.ids['analyze']
        new_data = categories_to_list(doc_id, analyzed, single_document)
        self._add_new_data_from_dict(new_data)

    def write_concepts(self, analyzed, single_document=False):
        """Converts concepts analysis result to flat lists and stores them.

        :param analyzed: JSON concepts analysis result
        :type analyzed: list
        :param single_document: Is analysis describing just one document
        :type single_document: bool
        """
        doc_id = self.ids['analyze']
        new_data = concepts_to_list(doc_id, analyzed, single_document)
        self._add_new_data_from_dict(new_data)

    def write_sentiment(self, analyzed, single_document=False):
        """Converts sentiment analysis result to flat lists and stores them.

        :param analyzed: JSON sentiment analysis result
        :type analyzed: list
        :param single_document: Is analysis describing just one document
        :type single_document: bool
        """
        doc_id = self.ids['analyze']
        new_data = sentiments_to_list(doc_id, analyzed, single_document)
        self._add_new_data_from_dict(new_data)

    def write_absa(self, analyzed, single_document=False):
        """Converts absa analysis result to flat lists and stores them.

        :param analyzed: JSON absa analysis result
        :type analyzed: list
        :param single_document: Is analysis describing just one document
        :type single_document: bool
        """
        doc_id = self.ids['analyze']
        new_data = absa_to_list(doc_id, analyzed, single_document)
        self._add_new_data_from_dict(new_data)

    def write_bulk(self, results):
        """Stores multiple anacode api's JSON responses marked with call IDs as
        tuples (call_id, call_result). Both scrape and analyze call IDs
        are defined in anacode.codes module.

        :param results: List of anacode responses with IDs of calls used
        :type results: list
        """
        for call_type, call_result in results:
            self.write_row(call_type, call_result)

    def init(self):
        """Not implemented here! Each subclass should decide what to do here."""
        pass

    def close(self):
        """Not implemented here! Each subclass should decide what to do here."""
        pass

    def __enter__(self):
        self.init()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()

class DataFrameWriter(Writer):
    """Writes Anacode API output into pandas.DataFrame instances."""
    def __init__(self, frames=None):
        """Initializes dictionary of result frames. Alternatively uses given
        frames dict for storage.

        :param frames: Might be specified to use this instead of new dict
        :type frames: dict
        """
        super(DataFrameWriter, self).__init__()
        self.frames = {} if frames is None else frames
        self._row_data = {}

    def init(self):
        """Initialized empty lists for each possible data frame."""
        self._row_data = {
            'categories': [],
            'concepts': [],
            'concepts_surface_strings': [],
            'sentiments': [],
            'absa_entities': [],
            'absa_normalized_texts': [],
            'absa_relations': [],
            'absa_relations_entities': [],
            'absa_evaluations': [],
            'absa_evaluations_entities': [],
        }

    def close(self):
        """Creates pandas data frames to self.frames dict and clears internal
        state.
        """
        for name, row in self._row_data.items():
            if len(row) > 0:
                self.frames[name] = pd.DataFrame(row, columns=HEADERS[name])
        self._row_data = {}

    def _add_new_data_from_dict(self, new_data):
        """Stores anacode api result converted to flat lists.

        :param new_data: Anacode api result
        :param new_data: list
        """
        for name, row_list in new_data.items():
            self._row_data[name].extend(row_list)

class CSVWriter(Writer):
    def __init__(self, target_dir='.'):
        """Initializes Writer to store Anacode API analysis results in target_dir in
        csv files.

        :param target_dir: Path to directory where to store csv files
        :type target_dir: str
        """
        super(CSVWriter, self).__init__()
        self.target_dir = os.path.abspath(os.path.expanduser(target_dir))
        self._files = {}
        self.csv = {}

    def _open_csv(self, csv_name):
        path = partial(os.path.join, self.target_dir)
        try:
            return open(path(csv_name), 'w', newline='')
        except TypeError:
            return open(path(csv_name), 'wb')

    def init(self):
        """Opens all csv files for writing and writes headers to them."""
        self.close()
        backup(self.target_dir, chain.from_iterable(CSV_FILES.values()))

        self._files = {
            'categories': self._open_csv('categories.csv'),
            'concepts': self._open_csv('concepts.csv'),
            'concepts_surface_strings': self._open_csv(
                'concepts_surface_strings.csv'
            ),
            'sentiments': self._open_csv('sentiments.csv'),
            'absa_entities': self._open_csv('absa_entities.csv'),
            'absa_normalized_texts': self._open_csv(
                'absa_normalized_texts.csv'
            ),
            'absa_relations': self._open_csv('absa_relations.csv'),
            'absa_relations_entities': self._open_csv(
                'absa_relations_entities.csv'
            ),
            'absa_evaluations': self._open_csv('absa_evaluations.csv'),
            'absa_evaluations_entities': self._open_csv(
                'absa_evaluations_entities.csv'
            ),
        }
        self.csv = {name: csv.writer(fp) for name, fp in self._files.items()}
        for name, writer in self.csv.items():
            writer.writerow(HEADERS[name])

    def _csv_has_content(self, csv_path):
        if not os.path.isfile(csv_path):
            return False

        with open(csv_path) as fp:
            for line_count, line in enumerate(fp):
                if line_count == 1 and len(line.strip()) != '':
                    return True
        return False

    def close(self):
        """Closes all csv files and removes empty ones."""
        for name, file in self._files.items():
            try:
                file.close()
            except (IOError, AttributeError):
                print('Problem closing "{}"'.format(name))

        for file_list in CSV_FILES.values():
            for file_name in file_list:
                path = os.path.join(self.target_dir, file_name)
                if os.path.isfile(path) and not self._csv_has_content(path):
                    os.unlink(path)

        self._files = {}
        self.csv = {}

    def _add_new_data_from_dict(self, new_data):
        """Stores anacode api result converted to flat lists.

        :param new_data: Anacode api result
        :param new_data: list
        """
        for name, row_list in new_data.items():
            self.csv[name].writerows(row_list)


"""
Module taking care of the forward modelling: theoretical dispersion
curve given a 1D crustal model of velocities and densities.
Uses the binaries of the Computer Programs in Seismology, with
must be installed in *COMPUTER_PROGRAMS_IN_SEISMOLOGY_DIR*
"""

import numpy as np
import matplotlib.pyplot as plt
import os
import shutil
import itertools as it
from easyprocess import EasyProcess
import tempfile
import pickle

# getting the dir of the binaries of the Computer Programs in Seismology

# import CONFIG class initalised in ./configs/tmp_config.pickle
config_pickle = 'configs/tmp_config.pickle'
f = open(name=config_pickle, mode='rb')
CONFIG = pickle.load(f)
f.close()

# import variables from initialised CONFIG class.
COMPUTER_PROGRAMS_IN_SEISMOLOGY_DIR=CONFIG.COMPUTER_PROGRAMS_IN_SEISMOLOGY_DIR

# default header of the model file:
# isotropic, 1D, flat Earth with layers of constant velocity
MODEL_HEADER = """MODEL.01
TEST
ISOTROPIC
KGS
FLAT EARTH
1-D
CONSTANT VELOCITY
LINE08
LINE09
LINE10
LINE11
H   VP  VS RHO  QP  QS ETAP ETAS FREFP FREFS"""

class VsModel:
    """
    Class holding a layered model of Vs function of depth,
    with Vp/Vs and rho/Vs ratio fixed.
    """
    def __init__(self, vs, dz, ratio_vp_vs, ratio_rho_vs, name='',
                 store_vg_at_periods=None):
        """
        Initializes model with layers' Vs (vs), layers' thickness (dz),
        and layers' ratio Vp/Vs and rho/Vs (ratio_vp_vs, ratio_rho_vs).
        """
        # checking shapes
        nlayers = np.size(vs)
        if np.size(dz) != nlayers - 1:
            raise Exception("Size of dz should be nb of layers minus 1")
        if not np.size(ratio_vp_vs) in [1, nlayers]:
            raise Exception("Size of ratio_vp_vs should be nb of layers or 1")
        if not np.size(ratio_rho_vs) in [1, nlayers]:
            raise Exception("Size of ratio_rho_vs should be nb of layers or 1")

        self.name = name
        self.vs = np.array(vs)
        self.dz = np.array(dz)
        self.ratio_vp_vs = np.array(ratio_vp_vs)
        self.ratio_rho_vs = np.array(ratio_rho_vs)

        # storing vg model at selected periods if required
        self.stored_vgperiods = store_vg_at_periods
        if not store_vg_at_periods is None:
            self.stored_vg = self.vg_model(store_vg_at_periods)
        else:
            self.stored_vg = None

    def misfit_to_vg(self, periods, vg, sigmavg, squared=True,
                     use_storedvg=True, storevg=False):
        """
        Misfit of modelled vg to observed vg

              [vg_model - vg]**2
        = Sum ------------------  over periods
                2 x sigmavg**2
        """
        # using stored vg model if required and available, else re-calculating it
        if use_storedvg and np.all(periods == self.stored_vgperiods):
            vg_model = self.stored_vg
        else:
            vg_model = self.vg_model(periods, store=storevg)

        misfit = np.sum(((vg_model - vg) / sigmavg)**2) / 2.0
        if squared:
            misfit = np.sqrt(misfit)
        return misfit

    def vg_model(self, periods, store=False):
        """
        Modelled group velocities, vg, function of period
        """
        vs = self.vs
        vp = self.ratio_vp_vs * self.vs
        rho = self.ratio_rho_vs * self.vs
        dz = np.r_[self.dz, 0]  # we append a fake thickness
        vg = Rayleigh_group_velocities(periods, dz=dz, vp=vp, vs=vs, rho=rho)
        if store:
            # storing group velocities if required
            self.stored_vgperiods = periods
            self.stored_vg = vg
        return vg

    def get_vs_at(self, z):
        """
        Returns Vs ad depth(s) *z*
        """
        indices = np.searchsorted(np.r_[0, self.dz.cumsum()], z, side='right') - 1
        if np.any(indices) < 0:
            raise Exception("Depth out of range")
        return self.vs[indices]

    def plot(self, periods, obsvgarrays=None, fig=None, color='r'):
        """
        Plots modelled and observed group velocity function of period (top)
        and the model itself, i.e. Vs vs depth (bottom)
        """
        if not fig:
            fig = plt.figure(figsize=(6.5, 10), tight_layout=True)
            axlist = [fig.add_subplot(211), fig.add_subplot(212)]
            legend = True
        else:
            axlist = fig.get_axes()
            legend = False  # no need to add legend to existing fig

        # 1st subplot: group velocity vs period
        ax = axlist[0]
        self.plot_vg(periods, obsvgarrays=obsvgarrays, ax=ax, legend=legend, color=color)
        ax.set_title(self.name)

        # 2nd subplot: Vs vs depth
        ax = axlist[1]
        self.plot_model(ax=ax, color=color)

        fig.canvas.draw()
        fig.show()
        return fig

    def plot_vg(self, periods, obsvgarrays=None, ax=None, legend=True, color='r'):
        """
        Plots modelled and observed group velocity function of period
        """
        # creating figure if not given as input
        fig = None
        if not ax:
            fig = plt.figure()
            ax = fig.add_subplot(111)

        vg_model = self.vg_model(periods)
        ax.plot(periods, vg_model, lw=1.5, color=color, label=self.name)
        if obsvgarrays:
            for i, vgarray in enumerate(obsvgarrays):
                label = 'Observed dispersion curves' if not i else None
                ax.plot(periods, vgarray, lw=0.5, color='k', label=label)
        ax.set_xlabel('Period (sec)')
        ax.set_ylabel('Group velocity (km/s)')
        if legend:
            ax.legend(loc='best', fontsize=11, framealpha=0.8)
        ax.grid(True)

        if fig:
            fig.show()

    def plot_model(self, ax=None, color='r', format_axes=True):
        """
        Plots the model, i.e. Vs vs depth
        """
        # creating figure if not given as input
        fig = None
        if not ax:
            fig = plt.figure()
            ax = fig.add_subplot(111)

        x = list(it.chain.from_iterable([[v, v] for v in self.vs]))
        y = [0.0] + list(it.chain.from_iterable([[z, z] for z in np.cumsum(self.dz)])) + \
            [self.dz.sum() + 15]
        ax.plot(x, y, lw=1.5, color=color)

        if format_axes:
            ax.set_ylim(sorted(ax.get_ylim(), reverse=True))
            ax.set_xlabel('Vs (km/s)')
            ax.set_ylabel('Depth (km)')
            ax.grid(True)

        if fig:
            fig.show()

def Rayleigh_group_velocities(periods, dz, vp, vs, rho, verbose=False):
    """
    Returns the array of Rayleigh wave group velocities at
    selected periods, from the 1-D layered Earth model
    contained in *dz* (thicknesses), *vp* (P wave velocities),
    *vs* (S wave velocities) and *rho* (densities).

    The Computer Programs in Seismology, located in dir
    *COMPUTER_PROGRAMS_IN_SEISMOLOGY_DIR*,
    are used for the computation.
    """
    if not COMPUTER_PROGRAMS_IN_SEISMOLOGY_DIR:
        raise Exception("Please provide the dir of the Computer Programs in Seismology")

    # making and moving to temporary dir
    current_dir = os.getcwd()
    tmp_dir = tempfile.mkdtemp()
    os.chdir(tmp_dir)

    # preparing input files
    if verbose:
        print 'Preparing model and periods files'
    create_model_file('model', dz, vp, vs, rho)
    f = open('periods', 'w')
    f.write('\n'.join([str(p) for p in periods]))
    f.close()

    # preparing model
    if verbose:
        print "Calling sprep96"
    cmd = os.path.join(COMPUTER_PROGRAMS_IN_SEISMOLOGY_DIR, 'sprep96')
    # Rayleigh wave, fundamental mode
    p = EasyProcess('"{}" -M model -PARR periods -NMOD 1 -R'.format(cmd)).call()
    if verbose:
        print p.stdout

    # phase dispersion curve
    if verbose:
        print "Calling sdisp96"
    cmd = os.path.join(COMPUTER_PROGRAMS_IN_SEISMOLOGY_DIR, 'sdisp96')
    p = EasyProcess('"{}" -v'.format(cmd)).call()
    if verbose:
        print p.stdout

    # group dispersion curve
    if verbose:
        print "Calling sregn96"
    cmd = os.path.join(COMPUTER_PROGRAMS_IN_SEISMOLOGY_DIR, 'sregn96')
    p = EasyProcess('"{}"'.format(cmd)).call()
    if verbose:
        print p.stdout

    # exporting group velocities (-U) of Rayleigh waves (-R) in ascii file
    if verbose:
        print "Calling sdpegn96"
    cmd = os.path.join(COMPUTER_PROGRAMS_IN_SEISMOLOGY_DIR, 'sdpegn96')
    p = EasyProcess('"{}" -R -S -U -XLOG -PER -ASC'.format(cmd)).call()
    if verbose:
        print p.stdout

    # loading group velocities from 6th column of ascii file
    vg = np.loadtxt('SREGN.ASC', skiprows=1, usecols=(5,))

    # removing temp dir
    os.chdir(current_dir)
    shutil.rmtree(tmp_dir)

    return vg

def create_model_file(path, dz, vp, vs, rho):
    """
    Writing the 1D model to ascci file, to be used as input
    by the Computer Programs in Seismology
    """
    qp = np.zeros_like(dz)
    qs = np.zeros_like(dz)
    etap = np.zeros_like(dz)
    etas = np.zeros_like(dz)
    frefp = np.ones_like(dz)
    frefs = np.ones_like(dz)

    f = open(path, mode='w')
    f.write(MODEL_HEADER)

    a = np.vstack((dz, vp, vs, rho, qp, qs, etap, etas, frefp, frefs))
    for col in a.T:
        f.write('\n')
        col.tofile(f, sep=' ')

    f.close()