/*******************************************************************************
*
* McXtrace, x-ray tracing package
*         Copyright, All rights reserved
*         DTU Physics, Kgs. Lyngby, Denmark
*         Synchrotron SOLEIL, Saint-Aubin, France
*
* Component: Lens_parab_Cyl
*
* %Identification
* Written by: Jana Baltser and Erik Knudsen
* Date: April 2011
* Version: 1.0
* Release: McXtrace 0.1
* Origin: NBI
*
* X-ray compound refractive lens (CRL) with a parabolic cylinder shape
*
* %Description
* An X-ray compound refractive lens (CRL) with a parabolic cylinder profile focusing in 1D, i.e. onto a line
* 
* The lens is invariant along the x-axis and has a parabolic profile along the y-axis defined as z/c = y^2/b^2.
* Thus, i.e. it focuses onto a line along the x-axis.
* N>1 means that a stack of lenses is to be simulated. Each lens consists of a pair of two opposing parabolic surfaces
* with a distance d between them. The reference point of the component is at the bottom of the first parabolic
* surface.
*
* Example: Lens_parab_Cyl(r=.5e-3,yheight=1.3e-3,xwidth=1.3e-3,d=.1e-3,N=21,
*     material_datafile="Be.txt")
*
* %Parameters
* Input parameters:
* r:        [m] Radius of curvature (circular approximation at the tip of the profile).
* yheight:  [m] The CRL's aperture along Y.
* xwidth:   [m] The width of the CRL in the invariant direction x.
* d:        [m] Distance between two surfaces of the lens along the propagation axis.
* N:        [1] Number of single lenses in a stack.
* rough_xy: [rad] RMS value of random slope along x and y.
* rough_z:  [rad] RMS value of random slope along z.
* material_datafile: [str]   Datafile containing f1 constants
*
* %End
*******************************************************************************/

DEFINE COMPONENT Lens_parab_Cyl

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

/* 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];
  } data_lens_parab_Cyl;
 
  // 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.
#pragma acc routine
  data_lens_parab_Cyl intersect_parab_Cyl(data_lens_parab_Cyl a, double *b, double rough_xy, double rough_z){
    data_lens_parab_Cyl 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,pos_tmp[3],pos1_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;
    
    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[0];
    yh=b[1];
    xw=b[2];
    dd=b[3];
    M=b[4];
    Sign=b[5];
    
    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*/
        return result;
    }
    if (A==0){ /*incident k-vector is parallel (exactly) to the z-axis. Thus, the eq. becomes linear*/
      if (B==0){
	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++){
	pos_tmp[i]=p[i]+t[0]*knorm[i];
	pos1_tmp[i]=p[i]+t[1]*knorm[i];
      }
      if ( fabs(pos_tmp[1])<=fabs(yh/2) && fabs(pos_tmp[0])<=fabs(xw/2) ){
        for(i=0;i<=2;i++){
          result.coord[i]=pos_tmp[i];
        }
      }	else if ( fabs(pos1_tmp[1])<=fabs(yh/2) && fabs(pos1_tmp[0])<=fabs(xw/2) ){
	for(i=0;i<=2;i++){
	  result.coord[i]=pos1_tmp[i];
	}
      }	else return result;
    }

    /*compute normal vector*/
    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) {
    // 	double d_xy=rough_xy*randnorm();
    //	Ny+=d_xy;
    //}
    //if (rough_z) {
    //	double d_z=rough_z*randnorm();
    //  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) {
       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;
  }
%}

DECLARE
%{
  int Z;
  double Ar;
  double rho;
  t_Table matT;
%}


INITIALIZE
%{
  int status=0;

  if ( (status=Table_Read(&(matT),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(matT.header,"Z","A[r]","rho",NULL);
  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);
%}

TRACE
%{
  // calculation of the parabolic parameters
  double parab_Cyl[8];
  data_lens_parab_Cyl incid,refr,outg;
  
  int i=0,nr;  
  double w;
  double E,mu,f,rhoel,dl,d_total,e,k,delta,beta,Refractive_Index_Re,Refractive_Index_Im;
 
  w=(yheight*yheight)/(8.0*r); //calculation of the "depth" of the profile
  
  parab_Cyl[0]=r;
  parab_Cyl[1]=yheight;
  parab_Cyl[2]=xwidth;
  
  parab_Cyl[6]=rough_xy;
  parab_Cyl[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*/
  mu=Table_Value(matT,e,5)*rho*1e2;/*mu is now in SI, [m^-1]*/;

  f=Table_Value(matT,e,1);

  /*Calculation of Refractive Index */
  rhoel= f*NA*(rho*1e-24)/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*1e-10/(2.0*k); /*mu and k in  A^-1*/

  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;

  d_total=0;
  for (nr=0;nr<=(N-1);nr++){
    parab_Cyl[3]=nr*d+nr*2*w; // d constant
    parab_Cyl[4]=1.0/Refractive_Index_Re; // M constant
    parab_Cyl[5]=-1.0; //Sign constant
    
    refr=intersect_parab_Cyl(incid,parab_Cyl,rough_xy,rough_z);
    
    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[3]=(nr+1)*d+nr*2*w;
    parab_Cyl[4]=Refractive_Index_Re;
    parab_Cyl[5]=1.0;

    outg=intersect_parab_Cyl(refr,parab_Cyl,rough_xy,rough_z);
    
    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);
    d_total+=dl;
    SCATTER;

    kx=outg.k[0]; ky=outg.k[1]; kz=outg.k[2];
    incid=outg;
  }
  /*Add absorption according to the path length inside the lens material*/
  p*=exp(-mu*d_total);

%}

FINALLY
%{
    Table_Free(&(matT));
%}

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
