/*******************************************************************************
*
* McXtrace, x-ray tracing package
*         Copyright, All rights reserved
*         DTU Physics, Kgs. Lyngby, Denmark
*         Synchrotron SOLEIL, Saint-Aubin, France
*
* Component: Lens_parab
*
* %Identification
* Written by: Jana Baltser and Erik Knudsen
* Date: August 2010
* Origin: NBI
*
* X-ray compound refractive lens (CRL) with an elliptic profile
*
* %Description
* A simple X-ray compound refractive lens (CRL) with an elliptic profile simulates the photons' movement on passing through it.
* Attenuation coefficient mu is taken from the NIST database and Be.txt
* 
* %Parameters
* Input parameters:
* r1:                 [m]       Radius of the profile along the X axis 
* r2:                 [m]       Radius of the profile along the Y axis 
* w:                  [m]       Parabola parameter, constraining it along the propagation axis
* d:                  [m]       Distance between two surfaces of the lens along the propagation axis
* N:                  [1]       Amount of single lenses in a stack
* material_datafile:  [Be.txt]  File where the material parameters for the filter may be found. Format is similar to what may be found off the NIST website.
* 
* %Link
* material datafile obtained from http://physics.nist.gov/cgi-bin/ffast/ffast.pl
*
* %End
*******************************************************************************/


DEFINE COMPONENT Lens_elliptical
DEFINITION PARAMETERS ()
SETTING PARAMETERS (string material_datafile="Be.txt",r1=0.42e-3,r2=0.8e-3,w=0.46e-3,d=0.2e-4,Transmission=1,N=1)
OUTPUT PARAMETERS ()
/* X-ray parameters: (x,y,z,kx,ky,kz,phi,t,Ex,Ey,Ez,p) */ 

SHARE
%{
  %include "read_table-lib"
  typedef struct {
  double coord[3];
  double k[3];
  } incom;
typedef struct {
  double constants[6];
} lens;
  
  incom intersection(incom a,lens b){
    incom result={a.coord[0],a.coord[1],a.coord[2],a.k[0],a.k[1],a.k[2]};  
    int i;
    double A,B,C,D,r1,r2,w;
    double t[2],p[3],knorm[3],k[3],pos1_tmp[3],pos_tmp[3];
  
    double N[3],Nx,Ny,Nz,Nnorm,Knorm;
    double cos_theta,cos_theta1,Arg,Arg1,s,q,s1,q1,alpha,beta;
    double k_new[3],k_new1[3],M,Sign,d;
     
    for(i=0;i<=2;i++){
      k[i]=a.k[i];
      p[i]=a.coord[i];
    }
    
    Knorm=sqrt(k[0]*k[0]+k[1]*k[1]+k[2]*k[2]);
    knorm[0]=k[0]/Knorm; knorm[1]=k[1]/Knorm; knorm[2]=k[2]/Knorm;
    
    r1=b.constants[0]; r2=b.constants[1]; w=b.constants[2]; d=b.constants[3]; 
    M=b.constants[4]; Sign=b.constants[5];
  
    if (knorm[0]*knorm[0]*w*r2*r2+knorm[1]*knorm[1]*w*r1*r1 == 0){
     //printf("72: Solving linear equation\n");
     t[0]=(-p[0]*p[0]*w*r2*r2-p[1]*p[1]*w*r1*r1+Sign*p[2]*r1*r1*r2*r2-Sign*d*r1*r1*r2*r2)/(2.0*p[0]*knorm[0]*w*r2*r2+2*p[1]*knorm[1]*w*r1*r1-Sign*knorm[2]*r1*r1*r2*r2);
        for(i=0; i<=2; i++){
	  result.coord[i]=p[i]+t[0]*knorm[i];
          }
      }
    else {
      //printf("79:Solving quadratic equation\n");
      A=knorm[0]*knorm[0]*w*r2*r2 + knorm[1]*knorm[1]*w*r1*r1; 
      B=2.0*p[0]*knorm[0]*w*r2*r2 + 2.0*p[1]*knorm[1]*w*r1*r1 - Sign*r1*r1*r2*r2*knorm[2]; 
      C=p[0]*p[0]*w*r2*r2 + p[1]*p[1]*w*r1*r1 - Sign*r1*r1*r2*r2*p[2] + Sign*r1*r1*r2*r2*d; 
      D=B*B-4.0*A*C;      
      if (D<0) {
      //printf("Line does not intersect the parabola\n");
        return result;
      }
      else {
	if(A==0){ fprintf(stderr, "line 89: Division by zero\n"); return; }	  
        double qq;
        if (B<0){
          qq=-0.5*(B-sqrt(D));
        }else{
          qq=-0.5*(B+sqrt(D)); 
        }
        t[0]=qq/A; 
        t[1]=C/qq;
	
       
        for(i=0;i<=2;i++){
	  pos_tmp[i]=p[i]+t[0]*knorm[i]; 
	  pos1_tmp[i]=p[i]+t[1]*knorm[i];
	}
	
	if (((pos_tmp[0]*pos_tmp[0])/(r1*r1)+(pos_tmp[1]*pos_tmp[1])/(r2*r2))<1.0){
	  for(i=0;i<=2;i++){
	  result.coord[i]=pos_tmp[i];
	  }
	}
	
	else if (((pos1_tmp[0]*pos1_tmp[0])/(r1*r1)+(pos1_tmp[1]*pos1_tmp[1])/(r2*r2))<1.0){
	  for(i=0;i<=2;i++){
	    result.coord[i]=pos1_tmp[i];
	  }
	}
	else return result;
      }     
    }
    
         /* Calculating normal */
    Nx=(2.0*result.coord[0])/(r1*r1); Ny=(2.0*result.coord[1])/(r2*r2); Nz=-Sign/w;
    Nnorm=sqrt(Nx*Nx+Ny*Ny+Nz*Nz);
    N[0]=Nx/Nnorm; N[1]=Ny/Nnorm; N[2]=Nz/Nnorm; 
        
      /* Calculating the angle between two vectors, applying Snell's law & getting the new k vector */
    cos_theta=N[0]*knorm[0]+N[1]*knorm[1]+N[2]*knorm[2]; 
    
    Arg=1.0-M*M*(1.0-cos_theta*cos_theta); cos_theta1=sqrt(Arg); // cos-sin transitions
        
    if (cos_theta<0) {
      cos_theta1=-sqrt(Arg);
    }
    // new kvector
    if ((1.0-cos_theta*cos_theta)==0) {
       fprintf(stderr,"line 134: Division by zero\n"); return; }
    Arg1=(1.0-cos_theta1*cos_theta1)/(1.0-cos_theta*cos_theta); s=(1/M)*sqrt(Arg1); q=(Knorm/Nnorm)*((1/M)*cos_theta1-s*cos_theta);
    
    k_new[0]=q*Nx+s*k[0];k_new[1]=q*Ny+s*k[1];k_new[2]=q*Nz+s*k[2];
  
    s1=-(M*sqrt(Arg1)); q1=(Knorm/Nnorm)*(M*cos_theta1-s1*cos_theta); 
    k_new1[0]=q1*Nx+s1*k[0];k_new1[1]=q1*Ny+s1*k[1]; k_new1[2]=q1*Nz+s1*k[2];
      // Choosing one vector, closest to the initial 
    
        alpha=(k[0]*k_new[0]+k[1]*k_new[1]+k[2]*k_new[2])/(M*Knorm*Knorm);
        beta=(k[0]*k_new1[0]+k[1]*k_new1[1]+k[2]*k_new1[2])/(M*Knorm*Knorm); 
	  if (acos(alpha)<acos(beta)) {
	    for(i=0;i<=2;i++) {
	      result.k[i]=k_new[i];
	    }
	  }
	  else if (acos(alpha)>acos(beta)) {
	    for(i=0;i<=2;i++) {
	      result.k[i]=k_new1[i];
	    }
	  }
       return result; 
   }

%}

DECLARE
%{
  int Z;
  double Ar;
  double  rho;
  double *E;
  double *Mu;
  double *F;
%} 


INITIALIZE 
%{
  t_Table T;
  if (Table_Read(&T, material_datafile,0) <= 0)
    exit(fprintf(stderr, "Error (%s): Could not read %s. Aborting.\n",NAME_CURRENT_COMP, material_datafile));
  char **header_parsed;
  header_parsed=Table_ParseHeader(T.header,"Z","A[r]","rho");
  if (!Z) Z=strtol(header_parsed[0],NULL,10);  
  if (!Ar) Ar=strtod(header_parsed[1],NULL);
  if (!rho) rho=strtod(header_parsed[2],NULL);
  E=malloc(sizeof(double)*(T.rows+1));
  Mu=malloc(sizeof(double)*(T.rows+1));
  F=malloc(sizeof(double)*(T.rows+1));
  int i;
  for(i=0;i<T.rows;i++){
      E[i]=T.data[i*T.columns]; 
      Mu[i]=T.data[1+i*T.columns]*1e-2;
      F[i]=T.data[2+i*T.columns];
  }
  Table_Free(&T);
%}




TRACE
%{
  incom incid,refr,outg;
  lens ellip;
  double k,n,delta,dl,e,beta,mu,EE,f;
  int i=0,nr;
  
  ellip.constants[0]=r1;
  ellip.constants[1]=r2;
  ellip.constants[2]=w;
  
  k=sqrt(kx*kx+ky*ky+kz*kz);
  e=K2E*k*1e-3; 
  while (e>E[i]){
    i++;
    if (E[i]==-1){
      fprintf(stderr,"Photon energy (%g keV) is outside of the lens' material data\n",k); ABSORB;
    }
  }
  EE=(e-E[i-1])/(E[i]-E[i-1]);
  mu=(1-EE)*Mu[i-1]+EE*Mu[i];
  f=(1-EE)*F[i-1]+EE*F[i];
  
  delta=((2.0*M_PI*RE)/pow(k,2))*((NA/Ar)*rho*(Z+f)*1e-24);
  beta=mu/(2.0*k*1e10);
  n=1.0-delta;
  
  incid.k[0]=kx; incid.k[1]=ky; incid.k[2]=kz; 
  incid.coord[0]=x; incid.coord[1]=y; incid.coord[2]=z;
  
  for (nr=0;nr<=(N-1);nr++){
    ellip.constants[3]=nr*d+nr*2*w;
    ellip.constants[4]=1.0/n;
    ellip.constants[5]=-1.0;
    
    refr=intersection(incid,ellip);
 	if(refr.k[0]==0 && refr.k[1]==0 && refr.k[2]==0) continue;   
    dl=sqrt( (refr.coord[0]-x)*(refr.coord[0]-x) + (refr.coord[1]-y)*(refr.coord[1]-y) + (refr.coord[2]-z)*(refr.coord[2]-z) );
    PROP_DL(dl);
    SCATTER;	
    //alter parabolic input to match second parabola
    
    ellip.constants[3]=(nr+1)*d+nr*2*w;
    ellip.constants[4]=n;
    ellip.constants[5]=1.0; 
    outg=intersection(refr,ellip);
    dl=sqrt( (outg.coord[0]-x)*(outg.coord[0]-x) + (outg.coord[1]-y)*(outg.coord[1]-y) + (outg.coord[2]-z)*(outg.coord[2]-z) );
    PROP_DL(dl);
    SCATTER;

    kx=outg.k[0]; ky=outg.k[1]; kz=outg.k[2];
    incid=outg;
  }
  // transmission calculation
  p*=exp(-mu*d*N);
%}

MCDISPLAY
%{
  /* A bit ugly; hard-coded dimensions. */
  
  line(0,0,0,0.2,0,0);
  line(0,0,0,0,0.2,0);
  line(0,0,0,0,0,0.2);
%}

END
