/****************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright 1997-2003, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Component: Pol_bender_tapering
*
* %I
* Written by: Erik Bergbaeck Knudsen (erkn@fysik.dtu.dk)
* Date: June 2012
* Origin: DTU Physics
*
* Polarising bender.
*
* %D
*
* A component modelling a set of identical blades bent to form a multcihannel (nslits)
* bender. The bender has a cylindrically curved entry plane (Will be extended to also
* allow a flat entry plane).
*
* The bender may also be tapered such that it can have focusing or defocusing porperties.
* This may be specified through the "channel_file"-parameter, which points to a data file
* in which the individual channels are defined by an entry- and an exit-width.
*
* The file format for channel file columns:
* > #d-spacing1 d-spacing2 l_spacer d-coating1 d-coating2
* > 1e-3  0.8e-3 1e-5  1e-9 1e-9
*
* Each blades is considered coated with a reflecting coating, whose thickness is defined in the
* latter two columsn of the channel data file.
*
* Coating reflectivities are read from
* another data file with separate reflectivities for up and down spin. When a neutron ray hits a blade
* the polarization vector associated with the ray is decomposed into spin-up and down
* probablities, which in turn determines the overall reflectivity for this ray on the mirror.
* Furthermore the probabilities are used to also reconstruct the polarization vector of the reflected
* ray.
*
* File format for the reflectivity file:
* > #parameter = m
* > q/m R(up) R(down)
*
* The effect of spacers inside the channels is modelled by absorption only. The depth of the spacers
* in a channel is considered constant for one channel (set in the channel data file), the number of
* them is constant across all channels and is an input parameter to the component.
*
* %P
*
* INPUT PARAMETERS:
*
* xwidth: [m]        Width at the bender entry. Currently unsupported.
* yheight: [m]       Height at the bender entry.
* length: [m]        Length of blades that make up the bender.
* d_substrate: [m]   Thickness of the substrate.
* entry_radius: [m]  Radius of curvature of the entrance window.
* radius: [m]        Curvature of a single plate/polarizer. +:curve left/-:right
* nslit:  [1]        Number of slits in the bender
* channel_file: [ ]  File name of file containing channel data. such as spacer widths etc. (see above for format)
* reflect_file: [ ]  File name of file containing reflectivity data parametrized either by q or m.
* sigma_abs: [barn]  Absorption per unit cell at v=2200 m/s of spacers. Defaults to literature value for Al.
* V0: [AA^3]         Volume of unit cell for spacers. Defaults to Al.
* nspacer: [ ]       Number of spacers per channel.
* G:     [m/s^2]     Magnitude of gravitation.
* debug: [ ]         If > 0 print out some internal parameters.
*
* %L
*
* %E
****************************************************************************/
DEFINE COMPONENT Pol_bender_tapering

SETTING PARAMETERS (string channel_file="channel.dat", xwidth=0, yheight, length,
        d_substrate, entry_radius=10, radius=100, G=9.8, int nslit=1, int debug=1,
        string reflect_file=NULL, sigma_abs=0.231, V0=66.4, nspacer=5)

/* Neutron parameters: (x,y,z,vx,vy,vz,t,sx,sy,sz,p) */

SHARE
%{
  %include "pol-lib"
  %include "ref-lib"
  %include "read_table-lib"
  #ifndef pol_reflect_spin_h
  #define pol_reflect_spin_h 1

  /***************************************************************************
   * Calculate new spin vector after reflection in a surface with reflectivities
   * Rup and Rdown referring to the direction of the vector (mx,my,mz)
   * m is assumed to have unit length, - if zero the default m=(0,1,0) is used.
   * returns the fraction of intensity thus reflected (if Rup+Rdown<1)
   **************************************************************************/
  double
  pol_reflect_spin_ref (double Rup, double Rdown, double* sx, double* sy, double* sz, double mx, double my, double mz) {
    double pm, ps1, ps2, pin;
    double s1x, s1y, s1z, s2x, s2y, s2z;
    if (mx == my && my == mz && mz == 0.0) {
      pm = *sy;
    } else {
      pm = scalar_prod (*sx, *sy, *sz, mx, my, mz);
      /*find two vectors perpendicular to m*/
      vec_prod (s1x, s1y, s1z, mx, my, mz, 1.0, 1.0, 1.0);
      NORM (s1x, s1y, s1z);
      vec_prod (s2x, s2y, s2z, mx, my, mz, s1x, s1y, s1z);
      vec_prod (s1x, s1y, s1z, mx, my, mz, s2x, s2y, s2z);

      ps1 = scalar_prod (*sx, *sy, *sz, s1x, s1y, s1z);
      ps2 = scalar_prod (*sx, *sy, *sz, s2x, s2y, s2z);
    }
    /*the fractions of "up" and "down" neutrons*/
    double phiu = ((pm) + 1) / 2.0;
    double phid = (1 - (pm)) / 2.0;

    /*the relative fractions generate the new fraction*/
    if (phiu * Rup + phid * Rdown) {
      pm = (phiu * Rup - phid * Rdown) / (phiu * Rup + phid * Rdown);
      pin = phiu * Rup + phid * Rdown;
      // printf("pin: %g\n",pin);
    } else {
      pm = 0;
      pin = 0;
    }
    if (mx == my && my == mz && mz == 0.0) {
      *sy = pm;
    } else {
      /*this may be wrong*/
      *sx = pm * mx + ps1 * s1x + ps2 * s2x;
      *sy = pm * my + ps1 * s1y + ps2 * s2y;
      *sz = pm * mz + ps1 * s1z + ps2 * s2z;
      NORM (*sx, *sy, *sz);
    }
    return pin;
  }
  /***************************************************************************
   * Calculate new spin vector after reflection in a surface with reflectivities
   * Rup and Rdown referring to the y-axis (up)
   *****************************************************************/
  double
  pol_reflect_spin (double Rup, double Rdown, double* sx, double* sy, double* sz) {
    return pol_reflect_spin_ref (Rup, Rdown, sx, sy, sz, 0.0, 0.0, 0.0);
  }

  #endif

  #ifndef to_channel_frame
   #define to_channel_frame(rot,offset) \
    do {\
      mccoordschange(offset,rot,&x,&y,&z,&vx,&vy,&vz,&sx,&sy,&sz);\
    }while(0);
  #endif

  #ifndef from_channel_frame
   #define from_channel_frame(rot,offset)\
    do {\
      double cx,cy,cz;\
      Rotation trot;\
      rot_transpose(rot,trot);\
      coords_get(offset,&cx,&cy,&cz);\
      x=x-cx;\
      y=y-cy;\
      z=z-cz;\
      mccoordschange_polarisation(trot,&x,&y,&z);\
      mccoordschange_polarisation(trot,&vx,&vy,&vz);\
      mccoordschange_polarisation(trot,&sx,&sy,&sz);\
    }while(0);
  #endif
%}

DECLARE
%{
  t_Table T;
  t_Table R;
  t_Table C;
  int by_q;
  double rw;
  double rw_q;
  double phi;
  int bounce;
%}

INITIALIZE
%{
  int status, i;

  if (xwidth && entry_radius) {
    fprintf (stderr, "Warning (%s): Both xwidth and entry_radius set. Entry_radius overrides xwidth\n", NAME_CURRENT_COMP);
    xwidth = 0;
  }

  if ((status = Table_Read (&(T), channel_file, 0)) == -1) {
    fprintf (stderr, "Error: Could not parse file \"%s\" in COMP %s\n", channel_file, NAME_CURRENT_COMP);
    exit (-1);
  }

  /*compute total opening and exit face of bender*/
  rw = (T.rows + 1) * d_substrate;   /*the substrate thickness * number of blades*/
  rw_q = (T.rows + 1) * d_substrate; /*the substrate thickness * number of blades*/
  for (i = 0; i < T.rows; i++) {
    rw += T.data[i * T.columns + 0]; /*the channel spacing*/
    rw += T.data[i * T.columns + 3]; /*the positive side coating*/
    rw += T.data[i * T.columns + 4]; /*the negative side coating*/

    rw_q += T.data[i * T.columns + 1]; /*channel exit spacing*/
    rw_q += T.data[i * T.columns + 3]; /*the positive side coating*/
    rw_q += T.data[i * T.columns + 4]; /*the negative side coating*/
  }

  /*angular opening*/
  phi = rw / entry_radius;

  if ((status = Table_Read (&(R), reflect_file, 0)) == -1) {
    fprintf (stderr, "Error: Could not parse file \"%s\" in COMP %s\n", reflect_file ? reflect_file : "", NAME_CURRENT_COMP);
    exit (-1);
  }
  char** header_parsed;
  header_parsed = Table_ParseHeader (R.header, "parameter");
  if (strstr (header_parsed[0], "m")) {
    by_q = 0;
  } else {
    by_q = 1;
  }

  /*Now do computations on the channel edge curvatures and store in a table
   *We need 2 cols for entry position, 2 for q (flat face exit pos), 2 for centre of curvature and 2 for exit pos
   * After this the channel details can be gotten by Table_index(C,coli,channel_no) and Table_Index(C,coli,channel_no+)
   * where the latter is the edge on the positive side.*/
  /*initialize the table*/
  Table_Init (&(C), 8, T.rows);
%}

TRACE
%{
  int status, channel_no;
  double t0, t1;
  double rx, dx1, dx2;
  double xo, yo, zo, vox, voy, voz;
  double outer_radius;
  double dx1_q, dx2_q;
  /*propagate to entry surface (cylinder!)*/
  /*check which channel we're in, if any*/
  bounce = 0;
  channel_no = 0;

  if (xwidth) {
    PROP_Z0;
    if (y < -0.5 * yheight || y > 0.5 * yheight || x < -0.5 * xwidth || x > 0.5 * xwidth) {
      /*Missed flat face bender. Leave neutron be*/
      channel_no = -1;
      RESTORE_NEUTRON (INDEX_CURRENT_COMP, x, y, z, vx, vy, vz, t, sx, sy, sz, p);
    }
    fprintf (stderr, "Error(%s): flat face bender tapering is not supported yet. Please use a large entry_raidus instead. Aborting.\n", NAME_CURRENT_COMP);
    exit (-1);
  } else if (entry_radius) {
    status = cylinder_intersect (&t0, &t1, x, y, z - (-entry_radius), vx, vy, vz, entry_radius, 1000);
    if (entry_radius > 0) {
      PROP_DT (t1);
    } else {
      PROP_DT (t0);
    }
    xo = x;
    yo = y;
    zo = z;
    vox = vx;
    voy = vy;
    voz = vz;
    if (!status || t1 < 0 || y < -0.5 * yheight || y > 0.5 * yheight || x < entry_radius * sin (-0.5 * rw / entry_radius)
        || x > entry_radius * sin (0.5 * rw / entry_radius)) {
      /*Missed curved face bender. leave neutron be*/
      channel_no = -1;
      RESTORE_NEUTRON (INDEX_CURRENT_COMP, x, y, z, vx, vy, vz, t, sx, sy, sz, p);
    }
  }
  if (channel_no != -1) {
    /*So we actually hit the bender face - proceed*/
    double cphi, sphi, d1, d2, p0x, p0z, p1x, p1z, p2x, p2z, c1x, c1z, c2x, c2z, q0x, q0z, q1x, q1z, q2x, q2z, r1x, r1z, r2x, r2z;
    if (entry_radius) {
      channel_no = 0;
      /*offsets of channel entry - initialize
       * outer edge (dx2) : bender end + 1 substrate thickness + coating (outer)
       * inner edge (dx1) : outer edge + channel width*/
      dx2 = -0.5 * rw + d_substrate + T.data[channel_no * T.columns + 3];
      if (x < dx2) {
        /*hit first substrate*/
        ABSORB;
      }
      dx1 = dx2 + T.data[channel_no * T.columns + 0];

      cphi = cos (length / radius);
      sphi = sin (length / radius);

      q0x = radius * (1 - cos (length / radius)); /*exit point of bender centre*/
      q0z = radius * (sin (length / radius));

      /*the plates will end on a cirle of this radius*/
      outer_radius = entry_radius + length; // sqrt(q0x*q0x + (q0z+entry_radius)*(q0z+entry_radius));

      p2x = entry_radius * sin (dx2 / entry_radius); /*channel entry coordinates*/
      p2z = entry_radius * (cos (dx2 / entry_radius) - 1);
      p1x = entry_radius * sin (dx1 / entry_radius);
      p1z = entry_radius * (cos (dx1 / entry_radius) - 1);

      dx2_q = -0.5 * rw_q + d_substrate + T.data[channel_no * T.columns + 3];
      /*add the offset due to blade curvature to dx2_q*/
      // dx2_q += asin(q0x/outer_radius)*outer_radius;
      dx1_q = dx2_q + T.data[channel_no * T.columns + 1];

      q2x = q0x - dx2_q * (-cphi); /*exit point of outer channel edge (without correction for channel blade length)*/
      q2z = q0z - dx2_q * (sphi);
      q1x = q0x - dx1_q * (-cphi); /*exit point of inner channel edge (without correction for channel blade length)*/
      q1z = q0z - dx1_q * (sphi);

      #ifdef MCDEBUG
      printf ("%sPTS: %i %g %g %g %g %g %g %g %g %g %g\n", NAME_CURRENT_COMP, channel_no, p1x, p1z, p2x, p2z, q1x, q1z, q2x, q2z, q0x, q0z);
      #endif

      while (!(x > entry_radius * sin (dx2 / entry_radius) && x < entry_radius * sin (dx1 / entry_radius))) {
        channel_no++;
        if (channel_no > T.rows) {
          // printf("oops I've missed all channels ");
          // printf("%lld %g %g %g %g %g %g %g\n",mcget_run_num(),x,y,z,vx,vy,vz,p);
          ABSORB;
        }
        /*offsets of channel entry - update
         * outer edge (dx2) : previous inner edge + 1 coating (inner) + substrate thickness + coating (outer)
         * inner edge (dx1) : outer edge + channel width*/
        dx2 = dx1 + T.data[channel_no * T.columns + 3] + d_substrate + T.data[channel_no * T.columns + 4];
        /*now check if we've hit the blade*/
        // if ( x< dx2 && x>dx1 ){
        /*hit a substrate*/
        //  ABSORB;
        //}
        dx1 = dx2 + T.data[channel_no * T.columns + 0];

        dx2_q = dx1_q + d_substrate + T.data[channel_no * T.columns + 3] + T.data[channel_no * T.columns + 4];
        dx1_q = dx2_q + T.data[channel_no * T.columns + 1];

        p2x = entry_radius * sin (dx2 / entry_radius);
        p2z = entry_radius * (cos (dx2 / entry_radius) - 1);
        p1x = entry_radius * sin (dx1 / entry_radius);
        p1z = entry_radius * (cos (dx1 / entry_radius) - 1);

        q2x = q0x - dx2_q * (-cphi); /*exit point of outer channel edge (without correction for channel blade length)*/
        q2z = q0z - dx2_q * (sphi);
        q1x = q0x - dx1_q * (-cphi); /*exit point of inner channel edge (without correction for channel blade length)*/
        q1z = q0z - dx1_q * (sphi);

        q1x = outer_radius * sin (dx1_q / outer_radius);
        q1z = outer_radius * (cos (dx1_q / outer_radius)) - entry_radius;
        q2x = outer_radius * sin (dx2_q / outer_radius);
        q2z = outer_radius * (cos (dx2_q / outer_radius)) - entry_radius;
        double h1, D1, h2, D2;
        /*helper variables to to compute intersection points of circles*/
        /*We need to put h1 and h2  to the side the bender is curving to - hence the sign operation*/
        D1 = sqrt ((q1x - p1x) * (q1x - p1x) + (q1z - p1z) * (q1z - p1z));
        h1 = (radius < 0 ? -1 : 1) * sqrt (radius * radius - 0.25 * D1 * D1);
        D2 = sqrt ((q2x - p2x) * (q2x - p2x) + (q2z - p2z) * (q2z - p2z));
        h2 = (radius < 0 ? -1 : 1) * sqrt (radius * radius - 0.25 * D2 * D2);

        c1x = p1x + 0.5 * (q1x - p1x) + h1 / D1 * (q1z - p1z);  /*these are the centers around which the channel edges actually rotate*/
        c1z = p1z + 0.5 * (q1z - p1z) + h1 / D1 * -(q1x - p1x); /*positive*/

        c2x = p2x + 0.5 * (q2x - p2x) + h2 / D2 * (q2z - p2z); /*negative*/
        c2z = p2z + 0.5 * (q2z - p2z) + h2 / D2 * -(q2x - p2x);

        /*now find the exit points to place exit plane*/
        r1x = c1x + cphi * (p1x - c1x) + sphi * (p1z - c1z);
        r1z = c1z - sphi * (p1x - c1x) + cphi * (p1z - c1z);

        r2x = c2x + cphi * (p2x - c2x) + sphi * (p2z - c2z);
        r2z = c2z - sphi * (p2x - c2x) + cphi * (p2z - c2z);
        double nx, ny, nz;
        vec_prod (nx, ny, nz, 0.0, 1.0, 0.0, r2x - r1x, 0, r2z - r1z);
        // printf("DEBUG: %d   %g %g %g %g     %g %g %g %g     %g %g %g %g   %g %g %g %g   %g %g %g %g   %g %g %g %g\n",channel_no,p1x,p1z,p2x,p2z,
        // r1x,r1z,r2x,r2z, q1x,q1z,q2x,q2z,outer_radius, q0x,q0z, asin(q0x/outer_radius)*outer_radius, c1x,c1z,c2x,c2z,dx1,dx2,dx1_q,dx2_q);
      }

      /*set a scatter upon entry*/
      SCATTER;

    } else {
      /*so it is a flat face bender*/
      channel_no = 0;
      dx2 = -0.5 * rw;
      dx1 = dx2 + T.data[channel_no * T.columns + 0] + T.data[channel_no * T.columns + 4] + T.data[channel_no * T.columns + 3];
      while (!(x > dx2 && x < dx1)) {
        channel_no++;
        dx2 = dx1;
        dx1 += T.data[channel_no * T.columns + 0] + T.data[channel_no * T.columns + 4] + T.data[channel_no * T.columns + 3];
      }
      rx = (dx1 + dx2) * 0.5;
      SCATTER;
    }

    /*now the channel number is known. Given the central curvature of bender we may compute the placement of inner and outer cylinders. */
    /*Some helper points and variables:
      q is the end point of the uncurved blade.
      r is the end point of the curved blade
      we get the offset of the curved endpoint from the central blade, and apply it to vectors
      parallel and perpendicular to QP.*/
    q1x = outer_radius * sin (dx1_q / outer_radius);
    q1z = outer_radius * (cos (dx1_q / outer_radius)) - entry_radius;
    q2x = outer_radius * sin (dx2_q / outer_radius);
    q2z = outer_radius * (cos (dx2_q / outer_radius)) - entry_radius;

    double L1, L2;
    L1 = sqrt ((q1x - p1x) * (q1x - p1x) + (q1z - p1z) * (q1z - p1z));
    L2 = sqrt ((q2x - p2x) * (q2x - p2x) + (q2z - p2z) * (q2z - p2z));
    r1x = p1x + q0z * (q1x - p1x) / L1 + q0x * (q1z - p1z) / L1;
    r1z = p1z + q0z * (q1z - p1z) / L1 + q0x * -(q1x - p1x) / L1;
    r2x = p2x + q0z * (q2x - p2x) / L2 + q0x * (q2z - p2z) / L2;
    r2z = p2z + q0z * (q2z - p2z) / L2 + q0x * -(q2x - p2x) / L2;

    double h1, D1, h2, D2;
    /*helper variables to to compute intersection points of circles*/
    /*We need to put h1 and h2  to the side the bender is curving to - hence the sign operation*/
    D1 = sqrt ((r1x - p1x) * (r1x - p1x) + (r1z - p1z) * (r1z - p1z));
    h1 = (radius < 0 ? -1 : 1) * sqrt (radius * radius - 0.25 * D1 * D1);
    D2 = sqrt ((r2x - p2x) * (r2x - p2x) + (r2z - p2z) * (r2z - p2z));
    h2 = (radius < 0 ? -1 : 1) * sqrt (radius * radius - 0.25 * D2 * D2);

    c1x = p1x + 0.5 * (r1x - p1x) + h1 / D1 * (r1z - p1z);  /*these are the centers around which the channel edges actually rotate*/
    c1z = p1z + 0.5 * (r1z - p1z) + h1 / D1 * -(r1x - p1x); /*positive*/

    c2x = p2x + 0.5 * (r2x - p2x) + h2 / D2 * (r2z - p2z); /*negative*/
    c2z = p2z + 0.5 * (r2z - p2z) + h2 / D2 * -(r2x - p2x);

    /*now find the exit points to place exit plane*/
    /*    r1x=c1x + cphi*(p1x-c1x) + sphi*(p1z-c1z);*/
    /*    r1z=c1z - sphi*(p1x-c1x) + cphi*(p1z-c1z);*/
    /**/
    /*    r2x=c2x + cphi*(p2x-c2x) + sphi*(p2z-c2z);*/
    /*    r2z=c2z - sphi*(p2x-c2x) + cphi*(p2z-c2z);*/
    double nx, ny, nz;
    vec_prod (nx, ny, nz, 0.0, 1.0, 0.0, r2x - r1x, 0, r2z - r1z);
    #ifdef MCDEBUG
    printf ("%sPTS2: %d %g %g %g %g\n", NAME_CURRENT_COMP, channel_no, r1x, r1z, r2x, r2z);
    #endif
    // printf("DEBUG: %d   %g %g %g %g     %g %g %g %g     %g %g %g %g   %g %g %g %g   %g %g %g %g\n",channel_no,p1x,p1z,p2x,p2z, r1x,r1z,r2x,r2z,
    // q1x,q1z,q2x,q2z,outer_radius, q0x,q0z, asin(q0x/outer_radius)*outer_radius, c1x,c1z,c2x,c2z);

    int exit = 0;
    int coat1, coat2;                               /*are the blades coated at all*/
    coat1 = (Table_Index (T, channel_no, 3) > 0.0); /*positive side coating*/
    coat2 = (Table_Index (T, channel_no, 4) > 0.0); /*negative side coating*/
    do {
      int i1, i2, ic, ie, cyl;
      double t10, t11, t20, t21, tc, te;

      /*initialize times to seomthing known*/
      t10 = t11 = 0;
      t20 = t21 = 0;
      te = 0;
      i1 = cylinder_intersect (&t10, &t11, x - c1x, y, z - c1z, vx, vy, vz, radius, yheight);
      i2 = cylinder_intersect (&t20, &t21, x - c2x, y, z - c2z, vx, vy, vz, radius, yheight);
      ie = plane_intersect (&te, x, y, z, vx, vy, vz, nx, ny, nz, r1x, 0, r1z);
      /*catch the different cases*/
      if ((!ie) && (!i1 && !i2)) {
        fprintf (stderr, "Pol_bender (%s): Neutron xyz=(%g %g %g), v=(%g %g %g) cannot exit the bender or does not intersect either edge cylinder\n",
                 NAME_CURRENT_COMP, x, y, z, vx, vy, vz);
        ABSORB; /*this should really not happen*/
      }

      /*find the smallest strictly positive intersection time of te, t21 and t10*/
      double tt = FLT_MAX;
      /*first we mask strictly nonpositive times with a very large number (FLT_MAX)*/
      t10 = t10 <= 0 ? FLT_MAX : t10;
      t21 = t21 <= 0 ? FLT_MAX : t21;
      te = te <= 0 ? FLT_MAX : te;

      /*if radius<0 the edge on the positve x side becomes the outer one.
       * This means we chould compare t11 with t20, so in that case swap, and the algorithm should work*/
      if (radius < 0) {
        t10 = t11;
        t21 = t20;
      }

      enum { TOPBOTTOM, ENDFACE, CYL, CYL2, CYL1, UNKNOWN } branch;

      if (i2 && (t21 < t10)) {
        tt = t21;
        cyl = 2;
        branch = CYL;
      } else if (i1) {
        tt = t10;
        cyl = 1;
        branch = CYL;
      }
      if (te < tt) {
        tt = te;
        cyl = 0;
        branch = ENDFACE;
      }
      if (i2 & 24) {
        /* bitwise and with 16+8 to catch exit codes from cylinder_intersect. These mean that
         * neutron _exits_ through the top or bottom of the cylinder. Only cyl. 2 needs to be checked
         * since by design the neutron is always outside cyl. 1.*/
        branch = TOPBOTTOM;
        tt = 0;
        ABSORB;
      }
      if (tt == FLT_MAX) {
        tt = 0;
        branch = UNKNOWN;
        fprintf (stderr, "Cannot determine reflection branch: t21=%g, t10=%g ,te=%g\n", t21, t10, te);
        ABSORB;
      }

      switch (branch) {
      case ENDFACE:
        /*exit through channel end - leave neutron be*/
        exit = 1;
        break;
      case TOPBOTTOM:
        /*exit through top or bottom - leave neutron be*/
        exit = 1;
        break;
      case CYL:
        /*we bounce on bender wall*/
        PROP_DT (tt);
        double nx, ny, nz, s, alpha, Q, Rup, Rdown;
        if (cyl == 1) {
          /*check if coated*/
          if (!coat1) {
            /*not coated on this side*/
            ABSORB;
          }
          double vox = vx;
          double voy = vy;
          double voz = vz;
          double v = sqrt (scalar_prod (vx, vy, vz, vx, vy, vz));
          /*bounce on cylinder 1*/
          nx = x - c1x;
          ny = 0;
          nz = z - c1z;
          NORM (nx, ny, nz);
          s = scalar_prod (vx, vy, vz, nx, ny, nz);
          vx = vx - s * 2 * nx;
          /*vy=vy - s*2*ny;*/
          vz = vz - s * 2 * nz;
          /*compute reflectivity based on coating (m=3.2?)*/
          alpha = acos (scalar_prod (vx, vy, vz, vox, voy, voz) / v / v);
          /* Now compute reflectivity. */
          Q = 2.0 * sin (alpha / 2.0) * sqrt (scalar_prod (vx, vy, vz, vx, vy, vz)) * V2K;

          if (!by_q) {
            Q = Q / 0.0217; /*convert to m-value by conversion factor 2.17 AA^-1*/
          }
          Rup = Table_Value (R, Q, 1);
          Rdown = Table_Value (R, Q, 2);
          p *= pol_reflect_spin (Rup, Rdown, &sx, &sy, &sz);
        } else {
          if (!coat2) {
            /*not coated on this side*/
            ABSORB;
          }
          double vox = vx;
          double voy = vy;
          double voz = vz;
          double v = sqrt (scalar_prod (vx, vy, vz, vx, vy, vz));
          /*bounce on cylinder 2*/
          nx = x - c2x;
          ny = 0;
          nz = z - c2z;
          NORM (nx, ny, nz);
          s = scalar_prod (vx, vy, vz, nx, ny, nz);
          vx = vx - s * 2 * nx;
          /*vy=vy - s*2*ny;*/
          vz = vz - s * 2 * nz;
          /*compute reflectivity based on coating (m=3.2?)*/
          alpha = acos (scalar_prod (vx, vy, vz, vox, voy, voz) / v / v);
          /* Now compute reflectivity. */
          Q = 2.0 * sin (alpha / 2.0) * sqrt (scalar_prod (vx, vy, vz, vx, vy, vz)) * V2K;

          if (!by_q) {
            Q = Q / 0.0217; /*convert to m-value by conversion factor 2.17 AA^-1*/
          }
          Rup = Table_Value (R, Q, 1);
          Rdown = Table_Value (R, Q, 2);
          p *= pol_reflect_spin (Rup, Rdown, &sx, &sy, &sz);
        }
        /*if trace is set - transform back to bender frame, set SCATTER, and transform back to channel frame again*/
        bounce++;
        SCATTER;

        break;
      }
    } while (!exit);

    /*add spacer absorption*/
    double v, pmul;
    v = sqrt (scalar_prod (vx, vy, vz, vx, vy, vz));
    pmul = exp (-(nspacer * Table_Index (T, channel_no, 2)) * (sigma_abs * 2200 * 100 / V0 / v));
    p *= pmul;

    SCATTER;
  }
%}

MCDISPLAY
%{

  /*first draw the outer edges*/
  double yh2 = yheight / 2.0;
  int channel_no = 0;
  int N = 16;
  if (channel_no != -1) {
    /*So we actually hit the bender face - proceed*/
    double cphi, sphi, d1, d2, p0x, p0z, p1x, p1z, p2x, p2z, c1x, c1z, c2x, c2z, q0x, q0z, q1x, q1z, q2x, q2z, r1x, r1z, r2x, r2z;
    double dx1, dx2, dx1_q, dx2_q;
    channel_no = 0;
    dx2 = -0.5 * rw + d_substrate + T.data[channel_no * T.columns + 3];
    dx1 = dx2 + T.data[channel_no * T.columns + 0];

    dx2_q = -0.5 * rw_q + d_substrate + T.data[channel_no * T.columns + 3];
    dx1_q = dx2_q + T.data[channel_no * T.columns + 1];

    for (channel_no = 0; channel_no < T.rows; channel_no++) {
      if (T.rows > 20 && channel_no != 0 && channel_no != T.rows - 1 && channel_no % 100 != 0) {
        /*update the relative coordinates of the channel entry and exits but don't actually draw anything*/
        dx2 = dx1 + T.data[channel_no * T.columns + 3] + d_substrate + T.data[channel_no * T.columns + 4];
        dx1 = dx2 + T.data[channel_no * T.columns + 0];

        dx2_q = dx1_q + d_substrate + T.data[channel_no * T.columns + 3] + T.data[channel_no * T.columns + 4];
        dx1_q = dx2_q + T.data[channel_no * T.columns + 1];
        continue;
      }

      if (entry_radius) {
        /*offsets of channel entry - initialize
         * outer edge (dx2) : bender end + 1 substrate thickness + coating (outer)
         * inner edge (dx1) : outer edge + channel width*/
        p2x = entry_radius * sin (dx2 / entry_radius); /*channel entry coordinates*/
        p2z = entry_radius * (cos (dx2 / entry_radius) - 1);
        p1x = entry_radius * sin (dx1 / entry_radius);
        p1z = entry_radius * (cos (dx1 / entry_radius) - 1);
      }

      q0x = radius * (1 - cos (length / radius)); /*exit point of bender centre*/
      q0z = radius * (sin (length / radius));

      cphi = cos (length / radius);
      sphi = sin (length / radius);

      q2x = q0x - dx2_q * (-cphi); /*exit point of outer channel edge (without correction for channel blade length)*/
      q2z = q0z - dx2_q * (sphi);
      q1x = q0x - dx1_q * (-cphi); /*exit point of inner channel edge (without correction for channel blade length)*/
      q1z = q0z - dx1_q * (sphi);

      double h1, D1, h2, D2; /*helper variables to to compute intersection points of circles*/
      D1 = sqrt ((q1x - p1x) * (q1x - p1x) + (q1z - p1z) * (q1z - p1z));
      h1 = (radius < 0 ? -1 : 1) * sqrt (radius * radius - 0.25 * D1 * D1);
      D2 = sqrt ((q2x - p2x) * (q2x - p2x) + (q2z - p2z) * (q2z - p2z));
      h2 = (radius < 0 ? -1 : 1) * sqrt (radius * radius - 0.25 * D2 * D2);

      c1x = p1x + 0.5 * (q1x - p1x) + h1 / D1 * (q1z - p1z);  /*these are the centers around which the channel edges actually rotate*/
      c1z = p1z + 0.5 * (q1z - p1z) + h1 / D1 * -(q1x - p1x); /*positive*/

      c2x = p2x + 0.5 * (q2x - p2x) + h2 / D2 * (q2z - p2z); /*negative*/
      c2z = p2z + 0.5 * (q2z - p2z) + h2 / D2 * -(q2x - p2x);

      /*now we know what the circles are - draw N circle segments at +h/2 and -h/2 to mark channels*/
      double dl = length / N;
      double ro1x, ro1z, ro2x, ro2z;
      ro1x = p1x;
      ro1z = p1z;
      ro2x = p2x;
      ro2z = p2z;
      multiline (5, ro1x, yh2, ro1z, ro1x, -yh2, ro1z, ro2x, -yh2, ro2z, ro2x, yh2, ro2z, ro1x, yh2, ro1z);

      cphi = cos (dl / radius);
      sphi = sin (dl / radius);
      /*now find the exit points to place exit plane of circle segment*/
      r1x = c1x + cphi * (p1x - c1x) + sphi * (p1z - c1z);
      r1z = c1z - sphi * (p1x - c1x) + cphi * (p1z - c1z);

      r2x = c2x + cphi * (p2x - c2x) + sphi * (p2z - c2z);
      r2z = c2z - sphi * (p2x - c2x) + cphi * (p2z - c2z);
      /*loop until exit*/
      while (dl <= length) {
        line (ro1x, yh2, ro1z, r1x, yh2, r1z);
        line (ro2x, yh2, ro2z, r2x, yh2, r2z);
        line (ro1x, -yh2, ro1z, r1x, -yh2, r1z);
        line (ro2x, -yh2, ro2z, r2x, -yh2, r2z);

        /*update dl*/
        dl += length / N;
        /*store old exit in ro1x etc.*/
        ro1x = r1x;
        ro1z = r1z;
        ro2x = r2x;
        ro2z = r2z;

        cphi = cos (dl / radius);
        sphi = sin (dl / radius);
        /*now find the exit points to place exit plane of circle segment*/
        r1x = c1x + cphi * (p1x - c1x) + sphi * (p1z - c1z);
        r1z = c1z - sphi * (p1x - c1x) + cphi * (p1z - c1z);

        r2x = c2x + cphi * (p2x - c2x) + sphi * (p2z - c2z);
        r2z = c2z - sphi * (p2x - c2x) + cphi * (p2z - c2z);
      }
      /*draw the last segment*/
      line (ro1x, yh2, ro1z, r1x, yh2, r1z);
      line (ro2x, yh2, ro2z, r2x, yh2, r2z);
      line (ro1x, -yh2, ro1z, r1x, -yh2, r1z);
      line (ro2x, -yh2, ro2z, r2x, -yh2, r2z);
      multiline (5, ro1x, yh2, ro1z, ro1x, -yh2, ro1z, ro2x, -yh2, ro2z, ro2x, yh2, ro2z, ro1x, yh2, ro1z);

      /*update the relative coordinates of the channel entry and exits*/
      dx2 = dx1 + T.data[channel_no * T.columns + 3] + d_substrate + T.data[channel_no * T.columns + 4];
      dx1 = dx2 + T.data[channel_no * T.columns + 0];

      dx2_q = dx1_q + d_substrate + T.data[channel_no * T.columns + 3] + T.data[channel_no * T.columns + 4];
      dx1_q = dx2_q + T.data[channel_no * T.columns + 1];
    }
  }
%}

END
