/*******************************************************************************
*
* McXtrace, X-ray tracing package
*         Copyright, All rights reserved
*         DTU Physics, Kgs. Lyngby, Denmark
*         Synchrotron SOLEIL, Saint-Aubin, France
*
* Component: Source_div
*
* %Identification
* Written by: Erik Knudsen 
* Date: November 11, 2009
* Origin: Risoe
* Release: McXtrace 0.1
*
* X-ray source with Gaussian or uniform divergence
*
* %Description
* A flat rectangular surface source with uniform or Gaussian divergence profile and focussing.
* If the parametere gauss is not set (the default) the divergence profile is flat
* in the range [-focus_ax,focus_ay]. If gauss is set, the focux_ax,focus_ay is considered
* the standard deviation of the gaussian profile.
* Currently focussing is only active for flat profile. The "focus window" is defined by focus_xw,focus_yh and dist.
* The spectral intensity profile is uniformly distributed in the energy interval defined by e0+-dE/2 or 
* by wavelength lambda0+-dlambda/2
* 
* Example: Source_div(xwidth=0.1, yheight=0.1, focus_aw=2, focus_ah=2, E0=14, dE=2, gauss=0)
*
* %Parameters
* xwidth:   [m]   Width of source.
* yheight:  [m]   Height of source.
* focus_aw: [rad] Standard deviation (Gaussian) or maximal (uniform) horz. width divergence.
* focus_ah: [rad] Standard deviation (Gaussian) or maximal (uniform) vert. height divergence.
* focus_xw: [m]   Width of sampling window
* focus_yh: [m]   Height of sampling window
* dist:     [m]   Downstream distance to place sampling target window
* E0:       [keV] Mean energy of X-rays.
* dE:       [keV] Energy half spread of X-rays. If gauss==0 dE is the half-spread, i.e. E\in[E0-dE,E0+dE], if gauss!=0 it's interpreted as the standard dev. 
* lambda0:  [AA]  Mean wavelength of X-rays (only relevant for E0=0).
* dlambda:  [AA]  Wavelength half spread of X-rays.
* gauss:    [1]   Criterion: 0: uniform, 1: Gaussian distribution of energy/wavelength.
* gauss_a:  [1]   Criterion: 0: uniform, 1: Gaussian divergence distribution.
* flux:     [1/(s * mm**2 *mrad**2 * energy unit)] flux per energy unit, Angs or keV.
* randomphase: [1] If !=0 the photon phase is chosen randomly.
* phase:    [1]   Value of the photon phase (if randomphase==0).
* spectrum_file: [string] File from which to read the spectral intensity profile
* focus_ar: [rad] Standard deviation (Gaussian) or maximal (uniform) radial divergence.
* radius:   [m]   Radius of circular source
* verbose:  [0/1] Show more information
*
* %End
*******************************************************************************/

DEFINE COMPONENT Source_div
DEFINITION PARAMETERS ()
SETTING PARAMETERS (string spectrum_file="NULL", xwidth=0, yheight=0, dist=0,
    focus_xw=0, focus_yh=0,focus_aw=0, focus_ah=0, focus_ar=0, radius=0,
    E0=0, dE=0, lambda0=0, dlambda=0, flux=0, gauss=0, gauss_a=0, int randomphase=1, phase=0, int verbose=0)
OUTPUT PARAMETERS ()
/* X-ray parameters: (x,y,z,kx,ky,kz,phi,t,Ex,Ey,Ez,p) */ 

SHARE
%{
  %include "read_table-lib"
%}

DECLARE
%{
  double p_init;
  double K;
  double dK;
  double xmin;
  double xmax;
  double xw_2;
  double focus_xw_2;
  double ymin;
  double ymax;
  double yh_2;
  double focus_yh_2;
  double pmul;
  double pint;
  t_Table T;
  int spectrum_from_file;
%}

INITIALIZE
%{
  focus_xw_2=focus_xw/2.0;
  focus_yh_2=focus_yh/2.0;
  xmin=-xwidth/2.0;
  ymin=-yheight/2.0;
  xmax=xwidth/2.0;
  ymax=yheight/2.0;

  if(radius==0 && ( xmax==xmin && ymin==xmax) ){
    fprintf(stderr,"ERROR (%s): No meaningful source area set. Either set radius or xwidth and yheight.\n",NAME_CURRENT_COMP);
    exit(-1);
  }

  /*flag if we are using a datafile*/
  spectrum_from_file=(spectrum_file && strcmp(spectrum_file,"NULL") && strlen(spectrum_file));

  if (spectrum_from_file){
    /*read spectrum from file*/
    int status=0;
    if ( (status=Table_Read(&(T),spectrum_file,0))==-1){
      fprintf(stderr,"ERROR (%s): Could not parse file \"%s\"\n",NAME_CURRENT_COMP,spectrum_file?spectrum_file:"");
      exit(-1);
    }
    /*data is now in table t*/
    /*integrate to get total flux, assuming raw numbers have been corrected for measuring aperture*/
    int i;
    pint=0;
    for (i=0;i<T.rows-1;i++){
      pint+=((T.data[i*T.columns+1]+T.data[(i+1)*T.columns+1])/2.0)*(T.data[(i+1)*T.columns]-T.data[i*T.columns]);
    }
    if (verbose){
      printf("INFO (%s): Integrated intensity radiated is %g pht/s\n",NAME_CURRENT_COMP,pint);
      if(E0) printf("INFO (%s):, E0!=0 -> assuming intensity spectrum is parametrized by energy [keV]\n",NAME_CURRENT_COMP);
    }
  }else if (!E0 && !lambda0){
    fprintf(stderr,"ERROR (%s): Error: Must specify either wavelength or energy distribution\n",NAME_CURRENT_COMP);
    exit(-1);
  }  

  /*calculate the X-ray weight from the flux*/
  if (flux){
    pmul=flux;
  }else{
    pmul=1;
  }
  pmul*=1.0/((double) mcget_ncount());

  if( dist==0 && ( focus_xw!=0 || focus_yh!=0 )){
    fprintf(stderr,"ERROR (%s): Cannot have focus sampling window (focus_xw x focus_yh) = (%g x %g) with dist=0.\n",NAME_CURRENT_COMP);
    exit(-1);
  }

  /*check if divergence limits are compatible with focus_xw, focus_yh*/
  double maxdivh,maxdivv;
  if(focus_xw!=0){
    maxdivh=atan((xwidth+focus_xw)/dist);
    if (focus_aw>maxdivh){
      focus_aw=maxdivh;
      if (verbose){
        fprintf(stderr,"WARNING (%s): sampling width does not support full divergence. Adjusting to focus_aw=%g rad\n",NAME_CURRENT_COMP,focus_aw);
      }
    }
  }
  if(focus_yh!=0){
    printf("I am here\n");
    maxdivv=atan((yheight+focus_yh)/dist);
    if (focus_ah>maxdivv){
      focus_ah=maxdivv;
      if (verbose){
        fprintf(stderr,"WARNING (%s): sampling height does not support full divergence. Adjusting to focus_ah=%g rad\n",NAME_CURRENT_COMP,focus_ah);
      }
    }
  }
%}

TRACE
%{
  double kk,theta_x,theta_y,l,e,k;
  p=pmul;

  if (spectrum_from_file){
    double pp=0;
    while (pp<=0){ 
      l=T.data[0]+ (T.data[(T.rows-1)*T.columns] -T.data[0])*rand01();
      pp=Table_Value(T,l,1);
    }
    p*=pp;
    /*if E0!=0 the tabled value is assumed to be energy in keV*/
    if (E0!=0){
      k=E2K*l;
    }else{
      k=(2*M_PI/l);
    }
  }else if (E0){
    if(!dE){
      e=E0;
    }else if (gauss){
      e=E0+dE*randnorm();
    }else{
      e=randpm1()*dE + E0;
    }
    k=E2K*e;
  }else if (lambda0){
    if (!dlambda){
      l=lambda0;
    }else if (gauss){
      l=lambda0+dlambda*randnorm();
    }else{
      l=randpm1()*dlambda*0.5 + lambda0;
    }
    k=(2*M_PI/l);
  }
  /*pick a point of origin*/
  if(!radius){
    x=xmin+rand01()*xwidth;
    y=ymin+rand01()*yheight;
    z=0;
  }else{
    double r=radius*sqrt(rand01());
    double th=rand01()*2*M_PI;
    x=r*cos(th);
    y=r*sin(th);
    z=0;
  }
  /*pick a direction*/
  if (focus_aw!=0 || focus_ah!=0){
    if (!gauss_a){
      /*find limits of uniform sampling scheme for vertical divergence.
        thetav should be acos(1-2*U) for U\in[0,1]. for theta measured from vertical axis
        we only use a sub-interval for U and measure from horizontal plane.*/
      double sample_lim1,u2;
      sample_lim1=(1-cos(M_PI_2 - focus_ah/2.0))*0.5;
      u2=randpm1()*(sample_lim1-0.5) + 0.5;
      theta_x = randpm1()*focus_aw/2.0;
      theta_y = acos(1-2*u2) - M_PI_2;
    }else{
      theta_x=randnorm()*focus_aw;
      theta_y=randnorm()*focus_ah;
    }

    kx=tan(theta_x);
    ky=tan(theta_y);
    kz=1.0;
    NORM(kx,ky,kz);
    kz*=k;
    kx*=k;
    ky*=k;
  }else if (focus_ar>0){
    /*radial divergence profile*/
    double theta,psi,kp;
    if(!gauss_a){
      double sample_lim1,u2;
      sample_lim1=(1-cos(focus_ar/2.0))*0.5;
      u2=rand01()*(sample_lim1);
      psi=acos(1-2*u2);
    }else{
      do{
        psi=abs(randnorm()*focus_ar);
      } while (psi>M_PI);
    }
    theta=rand01()*2*M_PI;

    kz=k*cos(psi);
    kp=sqrt(k*k-kz*kz);
    kx=kp*sin(theta);
    ky=kp*cos(theta);
  }else{
    /*0 dviergence - source is mathematically collimated*/
    kz=k;
    kx=ky=0;
  }

  /*set polarization and phase.*/
  Ex=0;Ey=0;Ez=0;
  if (!randomphase){
    phi=phase;
  }else{
    phi=rand01()*M_2_PI;
  }
%}

MCDISPLAY
%{
  double dist_display=1;
  if (dist_display>dist){
    dist_display=dist;
  }
  multiline(5, -xwidth/2.0, -yheight/2.0, 0.0,
                xwidth/2.0, -yheight/2.0, 0.0,
                xwidth/2.0,  yheight/2.0, 0.0,
               -xwidth/2.0,  yheight/2.0, 0.0,
               -xwidth/2.0, -yheight/2.0, 0.0);
  if (focus_aw){
    dashed_line(0,0,0, tan(focus_aw/2.0)*dist_display,0,dist_display,4);
    dashed_line(0,0,0,-tan(focus_aw/2.0)*dist_display,0,dist_display,4);
  }
  if (focus_ah){
    dashed_line(0,0,0,0, tan(focus_ah/2.0)*dist_display,dist_display,4);
    dashed_line(0,0,0,0,-tan(focus_ah/2.0)*dist_display,dist_display,4);
  }
%}

END
