/*******************************************************************************
*
* McXtrace, X-ray tracing package
*         Copyright, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*         University of Copenhagen, Copenhagen, Denmark
*
* Component: Lens_parab_Cyl
*
* %I
* Written by: Jana Baltser and Erik Knudsen
* 
* Date: April 2011
* Version: 1.0
* Release: McXtrace 0.1
* Origin: NBI
*
* %D
* A simple X-ray compound refractive lens (CRL) with a parabolic cylinder profile, 
* it focuses in 1D.
*
* %P
* Input parameters:
* r: [m] radius of curvature (circular approximation at the tip of the profile)
 yheight: [m] the CRL's dimensions along Y, aka aperture
* xwidth: [m] the CRL's dimensions along X
* d: [m] distance between two surfaces of the lens along the propagation axis
* N: [1] amount of single lenses in a stack.
* rough_xy: [rms] waviness along x and y
* rough_z: [rms] waviness along z
* 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. 
* 
* %L
* material datafile obtained from http://physics.nist.gov/cgi-bin/ffast/ffast.pl
*
* %E
*******************************************************************************/

DEFINE COMPONENT Lens_parab_Cyl_rough

SETTING PARAMETERS (string material_datafile="Be.txt", r=.5e-3,yheight=1.2e-3,xwidth=1.2e-3,d=.1e-3,T=.99,N=1,rough_z=0, rough_xy=0)

SHARE
%{
  %include "read_table-lib"
  struct datastruct{
    int Z;
    double Ar, rho;
    double *E, *mu, *f;
  }; 
  typedef struct {
  double coord[3];
  double k[3];
  } data;
  typedef struct {
  double constants[8];
  } param;
 
  // intersect_Parab_Cyl function calculates the intersection point on a surface of a parabolic cylinder with the photon's trajectory, estimates two points of intersection (if only) and chooses the one that lies within the interval (-yheight/2<=y=<yheight/2 && -xwidth/2<=x=<xwidth/2) and returns it.
   
  data intersect_Parab_Cyl_rough(data a, param b){
    data 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,rr;
    double t[2],p[3],knorm[3],k[3],Knorm,p_tmp[3],p1_tmp[3];
    double Sign,dd,M;
    
    double N[3],Nx,Ny,Nz,Nnorm;
    double Arg,s,q,alpha,beta;
    double k_new[3];
    double cos_theta,cos_theta1;
    double yh,xw;
    double rough_xy,rough_z;
    
    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;
       
    rr=b.constants[0];
    yh=b.constants[1];
    xw=b.constants[2];
    dd=b.constants[3];
    M=b.constants[4];
    Sign=b.constants[5];
    
    rough_xy=b.constants[6];
    rough_z=b.constants[7];
    
    A=knorm[1]*knorm[1];
    B=2*(p[1]*knorm[1]-Sign*rr*knorm[2]);
    C=p[1]*p[1]-Sign*2*rr*p[2]+Sign*dd*2*rr;
    D=B*B-4.0*A*C;
 
    if (D<0) {
        /*ray does not intersect the parabola*/
	fprintf(stderr,"line 89: D<0 %s\n",NAME_CURRENT_COMP);
        return result;
      }
    if (A==0){
      /*incident k-vector is parallel (exactly) to the z-axis. Thus, the eq. becomes linear*/
      if (B==0){ fprintf(stderr, "line 92: Division by zero in %s\n",NAME_CURRENT_COMP); return result;}
      t[0]=-C/B; 
      for (i=0;i<=2;i++){
	result.coord[i]=p[i]+t[0]*knorm[i]; 
      }
    } else {
      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++){
	p_tmp[i]=p[i]+t[0]*knorm[i];
	p1_tmp[i]=p[i]+t[1]*knorm[i];
      }
  
      if ( fabs(p_tmp[1])<=fabs(yh/2) && fabs(p_tmp[0])<=fabs(xw/2) ){
	for(i=0;i<=2;i++){
	  result.coord[i]=p_tmp[i];
	}
      }	else if ( fabs(p1_tmp[1])<=fabs(yh/2) && fabs(p1_tmp[0])<=fabs(xw/2) ){
	for(i=0;i<=2;i++){
	  result.coord[i]=p1_tmp[i];
	}
      }	else return result; 
      }
     
    // introducing waviness into the code
    double d_xy, d_z;
    
    d_xy=rough_xy*randnorm(); 
    d_z=rough_z*randnorm(); 
    
    Nx=0;
    if (result.coord[1]==0){		Ny=1;	Nz=0; 
    } else if (result.coord[1]!=0) {	Ny=Sign*rr/result.coord[1]; Nz=1;
    }
    if (rough_xy) {
	  Ny+=d_xy; 
    } 
    if (rough_z) {
	  Nz+=d_z;
    }  
   
    Nnorm=sqrt(Nx*Nx+Ny*Ny+Nz*Nz);
    N[0]=Nx/Nnorm; 
    N[1]=Ny/Nnorm; 
    N[2]=Nz/Nnorm; 
    
    cos_theta=N[0]*knorm[0]+N[1]*knorm[1]+N[2]*knorm[2];
    cos_theta1=M*cos_theta;   // Snell's law
 
    /* new k vector */
    if ((1.0-cos_theta*cos_theta)==0) {
       fprintf(stderr,"line 134: Division by zero\n"); return result; }
       
    Arg=(1.0-cos_theta1*cos_theta1)/(1.0-cos_theta*cos_theta);
    s=(1/M)*sqrt(Arg);
    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];
    
    for(i=0;i<3;i++) { 
      result.k[i]=k_new[i]; 
    }   
    
   return result;
  }
  const double Re=2.8179402894e-5;     /* Thomson Scattering length [Angstrom] */
  const double Na=6.02214179e23;       /* Avogadro's number [atoms per gram-mole]*/ 
%}

DECLARE
%{
  struct datastruct *prms;
%}   
  
INITIALIZE
%{
    int status=0;
  t_Table T;
  if ( (status=Table_Read(&T,material_datafile,0))==-1){
    fprintf(stderr,"Error: Could not parse file \"%s\" in COMP %s\n",material_datafile,NAME_CURRENT_COMP);
    exit(-1);
  }
  char **header_parsed;
  header_parsed=Table_ParseHeader(T.header,"Z","A[r]","rho",NULL);
  prms=calloc(1,sizeof(struct datastruct));
  if (!prms->Z) prms->Z=strtol(header_parsed[0],NULL,10);
  if (!prms->Ar) prms->Ar=strtod(header_parsed[1],NULL);
  if (!prms->rho) prms->rho=strtod(header_parsed[2],NULL);
  prms->E=malloc(sizeof(double)*(T.rows+1));
  prms->f=malloc(sizeof(double)*(T.rows+1));
  prms->mu=malloc(sizeof(double)*(T.rows+1));
  int i;
  for(i=0;i<T.rows;i++){
      prms->E[i]=T.data[i*T.columns]; 
      prms->mu[i]=T.data[5+i*T.columns]*prms->rho*1e2;     /*mu is now in SI, [m^-1]*/          
      prms->f[i]=T.data[1+i*T.columns];
  }
  Table_Free(&T);
  
%}

TRACE
%{
   // calculation of the parabolic parameters
  param parab_Cyl;
  data incid,refr,outg;
  
  int i=0,nr;  
  double w;
  double E,mu,f,rhoel,dl,e,k,delta,beta,Refractive_Index_Re,Refractive_Index_Im;
 
  w=(yheight*yheight)/(8.0*r); //calculation of the "depth" of the profile
  
  if ((yheight/2)<r){
    fprintf(stderr,"Error: the aperture must be greater than radius of curvature in the %s\n",NAME_CURRENT_COMP);
    exit(-1);
  }
 
  parab_Cyl.constants[0]=r;
  parab_Cyl.constants[1]=yheight;
  parab_Cyl.constants[2]=xwidth;
  
  parab_Cyl.constants[6]=rough_xy;
  parab_Cyl.constants[7]=rough_z;
  
  k=sqrt(kx*kx+ky*ky+kz*kz);
  e=K2E*k;  /*Energy in KeV, same unit as datafile */

  /*Interpolation of Table Values*/

  while (e>prms->E[i]){
    i++;
    if (prms->E[i]==-1){
      fprintf(stderr,"Photon energy (%g keV) is outside of the lens' material data\n",k); ABSORB;
    }
  }
  E=(e-prms->E[i-1])/(prms->E[i]-prms->E[i-1]);
  mu=(1-E)*prms->mu[i-1]+E*prms->mu[i];
  //mu= 1e-10*mu;  /*factor conversion from m^-1 to A^-1*/
  f=(1-E)*prms->f[i-1]+E*prms->f[i]; 
  
  /*Calculation of Refractive Index */

  rhoel= f*Na*(prms->rho*1e-24)/prms->Ar;    /*Material's Number Density of Electrons [e/A^3] incl f' scattering length correction*/
  delta= 2.0*M_PI*Re*rhoel/(k*k);
  //beta=mu/(2.0*k);          /*mu and k in  A^-1*/
  //delta=3.40754082e-6; Oleg's value for Be at 10 keV
  
  Refractive_Index_Re = 1.0-delta; 
  //Refractive_Index_Im = beta; 

  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++){
    parab_Cyl.constants[3]=nr*d+nr*2*w; // d constant
    parab_Cyl.constants[4]=1.0/Refractive_Index_Re; // M constant
    parab_Cyl.constants[5]=-1.0; //Sign constant
    
    refr=intersect_Parab_Cyl_rough(incid,parab_Cyl);
    
    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;	
    
    kx=refr.k[0]; 
    ky=refr.k[1]; 
    kz=refr.k[2];
    //alter parabolic input to match second parabola
    
    parab_Cyl.constants[3]=(nr+1)*d+nr*2*w;
    parab_Cyl.constants[4]=Refractive_Index_Re;
    parab_Cyl.constants[5]=1.0; 
 
    outg=intersect_Parab_Cyl_rough(refr,parab_Cyl);
    
    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
    double mu_rho, ap;
    
    mu_rho=mu/(prms->rho*1e2); // mass absorption coefficient [cm2]
  
    ap=mu_rho*((parab_Cyl.constants[0]*N*delta*prms->Ar*k*k)/(M_PI*Na*Re*1e-10*(prms->Z+f)))*1e16; //1e16 -dimension coefficient
    // ap - effective aperture
    
   if (T==0)
      ABSORB;
   else 
   T=exp(-mu*N*d)*(1/(2*ap))*(1-exp(-2*ap)); 
   
   p*=T;

%}

MCDISPLAY
%{
  
  double z_c,zdepth,w;
  w=(yheight*yheight)/(8*r);
  zdepth=N*(2*w+d);
  z_c=(zdepth/2.0)-w;
  box(0,0,z_c,yheight/2,yheight/2,zdepth,0, 0, 1, 0);
%}

END
