/*
   This file belongs to Aeneas. Aeneas is a GNU package released under GPL 3 license.
   This code is a simulator for Submicron 3D Semiconductor Devices. 
   It implements the Monte Carlo transport in 3D tetrahedra meshes
   for the simulation of the semiclassical Boltzmann equation for both electrons.
   It also includes all the relevant quantum effects for nanodevices.

   Copyright (C) 2007 Jean Michel Sellier <sellier@dmi.unict.it>
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

// Created on : 11 june 2007, Siracusa, Jean Michel Sellier
// Last modified : 16 august 2007, Siracusa, Jean Michel Sellier

// drift step of the EMC algorithm

void drift(double tau)
{
 register int j;
 int n=4;
 int p=0;
 int ifr[3]={0.,0.,0.};
 int indx[5];
 double d;
 double a[5][5],b[5];
 double dkx,dky,dkz;
 double hmt, x, y, z;
 double ksquared,gk,thesquareroot;

 if(IV==9) return;
// if(EL==-1) return; // just in case...
 if(i_dom[EL]==SIO2) return;

// Valid for every material
 dkx=-QH*0.25*(Ex[noeud_geo[0][EL]-1]+Ex[noeud_geo[1][EL]-1]
              +Ex[noeud_geo[2][EL]-1]+Ex[noeud_geo[3][EL]-1])*tau;
 dky=-QH*0.25*(Ey[noeud_geo[0][EL]-1]+Ey[noeud_geo[1][EL]-1]
              +Ey[noeud_geo[2][EL]-1]+Ey[noeud_geo[3][EL]-1])*tau;
 dkz=-QH*0.25*(Ez[noeud_geo[0][EL]-1]+Ez[noeud_geo[1][EL]-1]
              +Ez[noeud_geo[2][EL]-1]+Ez[noeud_geo[3][EL]-1])*tau;
// printf("KX=%g KY=%g KZ=%g\n",KX,KY,KZ);
// printf("dkx=%g dky=%g dkz=%g\n",dkx,dky,dkz);
 
 x=y=z=0.;
 for(j=0;j<4;j++){
  x+=L[j]*coord[0][noeud_geo[j][EL]-1];
  y+=L[j]*coord[1][noeud_geo[j][EL]-1];
  z+=L[j]*coord[2][noeud_geo[j][EL]-1];
 }
 x/=4.; y/=4.; z/=4.;
//if(z<-1.){
//   printf("%g %g %g %g %g\n",L[0],L[1],L[2],L[3],L[0]+L[1]+L[2]+L[3]); 
//   for(j=0;j<4;j++) printf("%d %g %g %g\n",j,coord[0][noeud_geo[j][EL]-1],coord[1][noeud_geo[j][EL]-1],coord[2][noeud_geo[j][EL]-1]);
//   printf("%d %d x = %g y = %g z = %g\n",EL,noeud_geo[3][EL]-1,x,y,z);
//}

// x*=1.e-6; y*=1.e-6; z*=1.e-6;
  
 if(i_dom[EL]!=SIO2){
// Electron drift process
   hmt=HM[i_dom[EL]][IV]*tau;
   ksquared=KX*KX+KY*KY+KZ*KZ;
   gk=HHM[i_dom[EL]][IV]*ksquared;
   thesquareroot=sqrt(1.+4.*alphaK[i_dom[EL]][IV]*gk);
//   printf("!!! %g\n",thesquareroot);
 }
// updating the position and pseudo-wave vector
// (second order Runge-Kutta method)
//printf("hmt = %g\n",hmt);
// printf("X=%g Y=%g Z=%g\n",x,y,z);
// printf("KX=%g KY=%g KZ=%g\n",KX,KY,KZ);
// printf("dkx=%g dky=%g dkz=%g\n",dkx,dky,dkz);
// printf("hmt = %g sq= %g\n",hmt,thesquareroot);
// printf("%g %g %g\n",hmt*(KX+0.5*dkx)/thesquareroot,hmt*(KY+0.5*dky)/thesquareroot,
//                     hmt*(KZ+0.5*dkz)/thesquareroot);
   x+=hmt*(KX+0.5*dkx)/thesquareroot;
   y+=hmt*(KY+0.5*dky)/thesquareroot;
   z+=hmt*(KZ+0.5*dkz)/thesquareroot;
   KX+=dkx;
   KY+=dky;
   KZ+=dkz;
// printf("X=%g Y=%g Z=%g\n\n",x,y,z);

// updating the barycentric coordinates
//   x*=1.e6; y*=1.e6; z*=1.e6;
//if(z>1.5) printf("x = %g y = %g z = %g\n",x,y,z);
//   memset(&a,0,sizeof(a));
//   memset(&b,0,sizeof(b));
   {int k;
    for(k=0;k<4;k++){
      a[1][k+1]=coord[0][noeud_geo[k][EL]-1];
      a[2][k+1]=coord[1][noeud_geo[k][EL]-1];
      a[3][k+1]=coord[2][noeud_geo[k][EL]-1];
      a[4][k+1]=1.;
   }}
   b[1]=4.*x;
   b[2]=4.*y;
   b[3]=4.*z;
   b[4]=4.;
//printf("%g %g %g\n",coord[0][noeud_geo[1][EL]-1],coord[1][noeud_geo[1][EL]-1],
//                    coord[2][noeud_geo[1][EL]-1]);
//printf("%g %g %g\n",x,y,z);
//printf("EL = %d\n",EL);
//     {int ii,jj;
//     for(ii=1;ii<=4;ii++){
//        for(jj=1;jj<=4;jj++) printf("a[%d][%d] = %g   ",ii,jj,a[ii][jj]);
//        printf("\n");
//     }}
// LU decomposition solver A.x=b
#include "../linear_solver/ludcmp.h"
#include "../linear_solver/lubksb.h"
//   printf("before %g %g %g %g\n",L[0],L[1],L[2],L[3]);
   for(j=0;j<4;j++) L[j]=b[j+1];
//   printf("** %g %g %g %g %g\n",L[0],L[1],L[2],L[3],L[0]+L[1]+L[2]+L[3]);
   if(L[0]<0. || L[1]<0. || L[2]<0. || L[3]<0.) goto label1;
   else return;
   label1:
//    printf("EL = %d %g %g %g %g\n",EL,L[0],L[1],L[2],L[3]);
//   printf("star %g %g %g %g %g\n",L[0],L[1],L[2],L[3],L[0]+L[1]+L[2]+L[3]);
    for(j=0;j<4;j++) if(L[j]<0.) p=j;
// on the boundaries
// =================
    if(p==0){
      ifr[0]=i_front[noeud_geo[1][EL]-1];
      ifr[1]=i_front[noeud_geo[2][EL]-1];
      ifr[2]=i_front[noeud_geo[3][EL]-1];
    }
    if(p==1){
      ifr[0]=i_front[noeud_geo[0][EL]-1];
      ifr[1]=i_front[noeud_geo[2][EL]-1];
      ifr[2]=i_front[noeud_geo[3][EL]-1];
    }
    if(p==2){
      ifr[0]=i_front[noeud_geo[0][EL]-1];
      ifr[1]=i_front[noeud_geo[1][EL]-1];
      ifr[2]=i_front[noeud_geo[3][EL]-1];
    }
    if(p==3){
      ifr[0]=i_front[noeud_geo[0][EL]-1];
      ifr[1]=i_front[noeud_geo[1][EL]-1];
      ifr[2]=i_front[noeud_geo[2][EL]-1];
    }
// Schottky contact
    if(vois[p][EL]==-1 &&
       (ifr[0]==SCHOTTKY &&
        ifr[1]==SCHOTTKY &&
        ifr[2]==SCHOTTKY)){
            IV=9;
            return;
        }
// ohmic contacts
    if(vois[p][EL]==-1 &&
       (ifr[0]==OHMIC &&
        ifr[1]==OHMIC &&
        ifr[2]==OHMIC)){
            IV=9;
            return;
        }
// insulator contacts
    if(vois[p][EL]==-1 || i_dom[vois[p][EL]]==SIO2/* &&
       (ifr[0]==INSULATOR ||
        ifr[1]==INSULATOR ||
        ifr[2]==INSULATOR)*/){
        reflection:;
        double max=-4.;
        double x[2],y[2],z[2];
// initial position
        x[0]=(L[0]*coord[0][noeud_geo[0][EL]-1]+
              L[1]*coord[0][noeud_geo[1][EL]-1]+
              L[2]*coord[0][noeud_geo[2][EL]-1]+
              L[3]*coord[0][noeud_geo[3][EL]-1])/(L[0]+L[1]+L[2]+L[3]);
        y[0]=(L[0]*coord[1][noeud_geo[0][EL]-1]+
              L[1]*coord[1][noeud_geo[1][EL]-1]+
              L[2]*coord[1][noeud_geo[2][EL]-1]+
              L[3]*coord[1][noeud_geo[3][EL]-1])/(L[0]+L[1]+L[2]+L[3]);
        z[0]=(L[0]*coord[2][noeud_geo[0][EL]-1]+
              L[1]*coord[2][noeud_geo[1][EL]-1]+
              L[2]*coord[2][noeud_geo[2][EL]-1]+
              L[3]*coord[2][noeud_geo[3][EL]-1])/(L[0]+L[1]+L[2]+L[3]);
        if(L[0]>L[1] && L[0]>L[2]) max=L[0];
        if(L[1]>L[0] && L[1]>L[2]) max=L[1];
        if(L[2]>L[1] && L[2]>L[0]) max=L[2];
        for(j=0;j<3;j++){
          L[j]/=max;
          if(L[j]<0.) L[j]=-L[j];
//          L[j]=rnd();
        }
// this is an elastic scattering
       L[3]=4.-(L[0]+L[1]+L[2]);
// final position
        x[1]=(L[0]*coord[0][noeud_geo[0][EL]-1]+
              L[1]*coord[0][noeud_geo[1][EL]-1]+
              L[2]*coord[0][noeud_geo[2][EL]-1]+
              L[3]*coord[0][noeud_geo[3][EL]-1])/(L[0]+L[1]+L[2]+L[3]);
        y[1]=(L[0]*coord[1][noeud_geo[0][EL]-1]+
              L[1]*coord[1][noeud_geo[1][EL]-1]+
              L[2]*coord[1][noeud_geo[2][EL]-1]+
              L[3]*coord[1][noeud_geo[3][EL]-1])/(L[0]+L[1]+L[2]+L[3]);
        z[1]=(L[0]*coord[2][noeud_geo[0][EL]-1]+
              L[1]*coord[2][noeud_geo[1][EL]-1]+
              L[2]*coord[2][noeud_geo[2][EL]-1]+
              L[3]*coord[2][noeud_geo[3][EL]-1])/(L[0]+L[1]+L[2]+L[3]);
// update the pseudo-wave vector
       KX=(x[1]-x[0])/tau;
       KY=(y[1]-y[0])/tau;
       KZ=(z[1]-z[0])/tau;
       return;
    }
// scattering on SiO2
/*    if(i_dom[vois[p][EL]]==SIO2){
        double max;
        if(L[0]>L[1] && L[0]>L[2]) max=L[0];
        if(L[1]>L[0] && L[1]>L[2]) max=L[1];
        if(L[2]>L[1] && L[2]>L[0]) max=L[2];
        for(j=0;j<3;j++){
          L[j]/=max;
          if(L[j]<0.) L[j]=-L[j];
        }
// this is an elastic scattering
       L[3]=4.-(L[0]+L[1]+L[2]);
       return;
    }*/
// in case of abrupt heterojunction, one has
// to calculate the reflection and transmission
// probability in order to see jow the particle
// behaves.
   if(i_dom[EL]!=i_dom[vois[p][EL]] && i_dom[vois[p][EL]]!=SIO2
                                    && i_dom[vois[p][EL]]!=-1){
//     printf("############### %d %d\n",EL,vois[p][EL]);
//     printf("*************** %d %d\n\n",i_dom[EL],i_dom[vois[p][EL]]);
     double k1,k2,T,R;
     double ksquared,thesquareroot,energy;
// calculates the particle energy
     ksquared=P[n][1]*P[n][1]+P[n][2]*P[n][2]+P[n][3]*P[n][3];
     thesquareroot=sqrt(1.+4.*alphaK[i_dom[EL]][IV]*HHM[i_dom[EL]][IV]*ksquared);
     energy=(thesquareroot-1.)/(2.*alphaK[i_dom[EL]][IV]);
     if(IV==2) energy+=EG[i_dom[EL]];
     energy*=Q; // <--- energy in Joule
// calculates the Transmission probability
     k1=sqrt(2.*MSTAR[i_dom[EL]][IV]*M*energy)/HBAR;
     if(IV==2 && NOVALLEY[i_dom[vois[p][EL]]]==1){
      if((energy+(EMIN[i_dom[EL]][IV]-EMIN[i_dom[vois[p][EL]]][1])*Q)<0.) goto reflection;
      k2=sqrt(2.*MSTAR[i_dom[vois[p][EL]]][1]*M*
             (energy-(EMIN[i_dom[EL]][IV]-EMIN[i_dom[vois[p][EL]]][IV])*Q))/HBAR;
     }
     else{
      if((energy+(EMIN[i_dom[EL]][IV]-EMIN[i_dom[vois[p][EL]]][IV])*Q)<0.) goto reflection;
      k2=sqrt(2.*MSTAR[i_dom[vois[p][EL]]][IV]*M*
             (energy+(EMIN[i_dom[EL]][IV]-EMIN[i_dom[vois[p][EL]]][IV])*Q))/HBAR;
     }
     if(IV==2 && NOVALLEY[i_dom[vois[p][EL]]]==1){
       T=pow(2.*(k1/(MSTAR[i_dom[EL]][IV]*M))
         /(k1/(MSTAR[i_dom[EL]][IV]*M)+k2/(MSTAR[i_dom[vois[p][EL]]][1]*M)),2.);
       R=pow((k1/(MSTAR[i_dom[EL]][IV]*M)-k2/(MSTAR[i_dom[vois[p][EL]]][1]*M))
         /(k1/(MSTAR[i_dom[EL]][IV]*M)+k2/(MSTAR[i_dom[vois[p][EL]]][1]*M)),2.);
     }
     else{
       T=pow(2.*(k1/(MSTAR[i_dom[EL]][IV]*M))
         /(k1/(MSTAR[i_dom[EL]][IV]*M)+k2/(MSTAR[i_dom[vois[p][EL]]][IV]*M)),2.);
       R=pow((k1/(MSTAR[i_dom[EL]][IV]*M)-k2/(MSTAR[i_dom[vois[p][EL]]][IV]*M))
            /(k1/(MSTAR[i_dom[EL]][IV]*M)+k2/(MSTAR[i_dom[vois[p][EL]]][IV]*M)),2.);
     }
//     printf("k1 = %g     k2 = %g   R = %g  T = %g\n",k1,k2,R*R/(R*R+T*T),T*T/(R*R+T*T)); 
     if(rnd()<R*R/(R*R+T*T)) goto reflection;
   }

//   transmission:;
// inside the device (between two different tetrahedra of the same material)
   EL=vois[p][EL];
//   printf("new EL=%d\n",EL);
//   printf("%g %g %g %g %g\n",L[0],L[1],L[2],L[3],L[0]+L[1]+L[2]+L[3]);
      memset(&a,0,sizeof(a));
      memset(&b,0,sizeof(b));
      {int k;
       for(k=0;k<4;k++){
         a[1][k+1]=coord[0][noeud_geo[k][EL]-1];
         a[2][k+1]=coord[1][noeud_geo[k][EL]-1];
         a[3][k+1]=coord[2][noeud_geo[k][EL]-1];
         a[4][k+1]=1.;
      }}
      b[1]=4.*x;
      b[2]=4.*y;
      b[3]=4.*z;
      b[4]=4.;
// LU decomposition solver A.x=b
#include "../linear_solver/ludcmp.h"
#include "../linear_solver/lubksb.h"
      for(j=0;j<4;j++) L[j]=b[j+1];
      if(L[0]>=0. && L[1]>=0. && L[2]>=0. && L[3]>=0.){
//         printf("final %g %g %g %g %g\n",L[0],L[1],L[2],L[3],L[0]+L[1]+L[2]+L[3]);
         return;
      }
      else goto label1;
}
