/*******************************************************************************
*
* McXtrace, x-ray tracing package
*         Copyright, All rights reserved
*         DTU Physics, Kgs. Lyngby, Denmark
*         Synchrotron SOLEIL, Saint-Aubin, France
*
* Component: Grating_trans
*
* %Identification
*
* Written by: Erik B Knudsen (erkn@fysik.dtu.dk) 
* Date: December 2016
* Version: 1.0
* Release: McXtrace 1.4
* Origin: DTU Physics
*
* Transmission grating
*
* %Description
* Model of a 1D rectangular transmission grating based on the theory developed in Schnopper et. al., Applied Optics, 1977.
* The grating lines are assumed to be vertical. Within each period a fraction gamma is the "open" fraction. 
* (I.e. 1 is completely open). At present only absorption in the substrate (modelled by the thickness sdepth) 
* is included.
*
* This  component is currently undergoing validation.
*
* Example: Grating_trans(
*   xwidth=25e-3, yheight=25e-3, gamma=0.4, period=2000e-10, zdepth=5100e-10, max_order=3, material="Au.txt")
*
* %Parameters
* Input parameters:
* period:     [m]   Distance between grating grooves.
* zdepth:     [m]   Depth of grooves.
* gamma:      [0-1] Ratio between groove and period  aka duty cycle. 1 means fully open.
* sdepth:     [m]   Thickness of substrate. The default is to have no substrate - i.e. rods. 
* xwidth:     [m]   Width of the grating. Defines how many lines there are in total.
* yheight:    [m]   Height of the grating.
* material:   [str] Data file containing the material from which the grating is made.
* substrate:  [str] Data file containing material data for the substrate.
* fixed_delta:[0/1] Set delta to the given constant. Useful for debugging.
* max_order:  [1]   Maximum order to diffract
* 
* %End
*******************************************************************************/

DEFINE COMPONENT Grating_trans
SETTING PARAMETERS (xwidth=1e-3, yheight=1e-3, period=1e-6,gamma=0.5,zdepth=1e-6,sdepth=0, string material="Au.txt",
      string substrate="",int max_order=2,fixed_delta=0)

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


DECLARE
%{
  int Z;
  int mu_c;
  double Ar;
  double rho;
  double delta_prefactor;
  t_Table table;
  double srho;
  int smu_c;
  t_Table stable;
  int order;
%}



INITIALIZE
%{
  int status;

  if (xwidth == 0 || yheight == 0) {
    fprintf (stderr, "Error (%s): Grating has 0 area.\n", NAME_CURRENT_COMP);
    exit (-1);
  }
  if (zdepth == 0) {
    fprintf (stderr, "Error (%s): Grating has no grooves (zdepth==0).\n", NAME_CURRENT_COMP);
    exit (-1);
  }

  /*check if material datafiles are present - if so load them*/
  if ((status = Table_Read (&(table), material, 0)) == -1) {
    fprintf (stderr, "Error: Could not parse file \"%s\" in COMP %s\n", material, NAME_CURRENT_COMP);
    exit (-1);
  }
  char** header_parsed;
  header_parsed = Table_ParseHeader (table.header, "Z", "A[r]", "rho", "Z/A", "sigma[a]", NULL);
  if (header_parsed[0]) {
    Z = strtol (header_parsed[0], NULL, 10);
  }
  if (header_parsed[1]) {
    Ar = strtod (header_parsed[1], NULL);
  }
  if (header_parsed[2]) {
    rho = strtod (header_parsed[2], NULL);
  } else {
    fprintf (stderr, "Warning(%s): %s not found in header of %s, set to 1\n", NAME_CURRENT_COMP, "rho", material);
    rho = 1;
  }
  /*which columns holds the mus*/
  mu_c = 5;
  if (table.columns == 3)
    mu_c = 1;

  delta_prefactor = NA * (rho * 1e-24) / Ar * 2.0 * M_PI * RE;

  /*read substrate datafile if present*/
  if (substrate && strlen (substrate)) {
    if ((status = Table_Read (&(stable), substrate, 0)) == -1) {
      fprintf (stderr, "Error(%s): Could not parse file \"%s\".\n", NAME_CURRENT_COMP, material);
      exit (-1);
    }
    header_parsed = Table_ParseHeader (stable.header, "Z", "A[r]", "rho", "Z/A", "sigma[a]", NULL);
    if (header_parsed[2]) {
      srho = strtod (header_parsed[2], NULL);
    }

    smu_c = 5;
    if (stable.columns == 3)
      smu_c = 1;
  }
%}


TRACE
%{
  double e, k, f, delta, beta, mu0, smu0, pmul, order_factor, theta, order_likelihood;
  double thx, thy, period_eff, zdepth_eff;
  int M;
  // order;

  PROP_Z0;
  if (x < -xwidth * 0.5 || x > xwidth * 0.5 || y < -yheight * 0.5 || y > yheight * 0.5) {
    RESTORE_XRAY (INDEX_CURRENT_COMP, x, y, z, kx, ky, kz, phi, t, Ex, Ey, Ez, p);
  } else {
    /*get the index of refraction*/
    k = sqrt (kx * kx + ky * ky + kz * kz);
    e = k * K2E;
    /*Material's Number Density of Electrons [e/A^3] incl f' scattering length correction*/
    /*We have reparametrized e as log(e) for a more practical constant step table.*/
    f = Table_Value (table, e, 1);
    delta = (fixed_delta ? fixed_delta : f / (k * k) * delta_prefactor);
    mu0 = Table_Value (table, e, mu_c) * rho * 1e2;
    beta = mu0 / (2 * k * 1e10);

    /*pick an order according the relative intensity as given by Schnopper*/

    /* correct the period for horizontal angle*/
    thx = atan (fabs (kx / kz));
    period_eff = cos (thx) * period;

    M = xwidth / period_eff;

    order = floor (rand01 () * (max_order * 2 + 1)) - max_order;
    /* n_m(q)/N_0(q)=([sin(Mmpi)/Msin(mpi)]^2 [sin( (a/d)mpi)/mpi]^2 [1+exp(-2qzk)-2exp(-qzk)cos(qzdelta)]*/
    /* M=number of wires in the grating,
     * d grating spacing (period)
     * a width of grating opening (duty-cycle)
     * z the thickness of grating wire
     * k imaginary part of refr index
     * n=1-delta real part of refr index
     * q wavenumber.
     */

    /* correct the period for horizontal angle*/
    thx = atan (fabs (kx / kz));
    period_eff = cos (thx) * period;

    M = xwidth / period_eff;

    double tot = 0;
    int i;
    for (i = 0; i < max_order; i++) {
      if (i) {
        order_factor = pow (sin (gamma * i * M_PI) / (i * M_PI), 2.0);
        order_likelihood = order_factor * (1 + exp (-2.0 * k * 1e10 * zdepth * beta) - 2.0 * exp (-k * 1e10 * zdepth * beta) * cos (k * 1e10 * zdepth * delta));
      } else {
        order_likelihood = gamma * gamma + (1 - gamma) * (1 - gamma) * exp (-2.0 * k * 1e10 * zdepth * beta)
                           - 2.0 * gamma * (1 - gamma) * exp (-k * 1e10 * zdepth * beta) * cos (k * 1e10 * zdepth * delta);
      }
      tot += order_likelihood;
    }

    /* correct zdepth for vertical angle*/
    thy = atan (fabs (ky / kz));
    zdepth_eff = zdepth / cos (thy);
    if (order) {
      order_factor = pow (sin (gamma * order * M_PI) / (order * M_PI), 2.0);
      order_likelihood
          = order_factor * (1 + exp (-2.0 * k * 1e10 * zdepth_eff * beta) - 2.0 * exp (-k * 1e10 * zdepth_eff * beta) * cos (k * 1e10 * zdepth_eff * delta));
    } else {
      order_likelihood = gamma * gamma + (1 - gamma) * (1 - gamma) * exp (-2.0 * k * 1e10 * zdepth_eff * beta)
                         - 2.0 * gamma * (1 - gamma) * exp (-k * 1e10 * zdepth_eff * beta) * cos (k * 1e10 * zdepth_eff * delta);
    }

    double fmc;
    fmc = 1.0 / (max_order * 2 + 1);
    pmul = order_likelihood / fmc;

    /*Pick an angle centered on the order*/
    theta = order * 2 * M_PI / (k * 1e10 * period_eff); /*the 1e10 factor because k is in AA^-1 and period is in m*/

    /* we should rotate around an axis perpendicular to k and in yz-plane*/
    /*i.e. (ay * ky + az*kz ) = 0 && ay>0 => Without loss of gen. ay=1 => az= -ky/kz*/
    double az;
    az = -ky / kz;

    /*change direction*/
    rotate (kx, ky, kz, kx, ky, kz, theta, 0, 1, az);

    /*do absorption according to mean absorption coefficient in the "slab",
      remembering we can be at an angle. Just use the angle theta.*/
    smu0 = Table_Value (stable, e, smu_c) * srho * 1e2;

    p *= pmul * exp (-smu0 * (sdepth) / cos (theta)) * exp (-mu0 * (zdepth * (1 - gamma)));
    SCATTER;
  }
%}

MCDISPLAY
%{
  int i;
  rectangle ("xy", 0, 0, -(sdepth) / 2.0, xwidth, yheight);
  rectangle ("xy", 0, 0, (sdepth) / 2.0, xwidth, yheight);
  for (i = -1; i < 2; i++) {
    box (period / 4.0 + i * period, 0, sdepth / 2.0 + zdepth / 2.0, period / 2.0, yheight, zdepth, 0, 0, 1, 0);
    box (-3 * period / 4.0 + i * period, 0, sdepth / 2.0 + zdepth / 2.0, period / 2.0, yheight, zdepth, 0, 0, 1, 0);
  }
%}

END
