/*******************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright 1997-2002, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Component: TOFRes_sample
*
* %I
* Modified from Res_sample, written by: Kristian Nielsen
* Date: 1999
* Written by: KL, 10 October 2004
* Version: $Revision: 1.2 $
* Origin: Risoe
* Release: McStas 1.6
*
* Sample for TOF resolution function calculation.
*
* %D
* An inelastic sample with completely uniform scattering in both solid angle
* and energy. This sample is used together with the TOFRes_monitor component
* and (optionally) the mcresplot front-end to compute the resolution
* function of all time-of-flight instruments.
* The method of time focusing is used to optimize the simulations.
*
* The shape of the sample is either a hollow cylinder or a rectangular box. The
* hollow cylinder shape is specified with an inner and outer radius.
* The box is specified with dimensions xwidth, yheight, zthick.
*
* The scattered neutrons will have directions towards a given target and
* energies between E0-dE and E0+dE, where E0 is calculated from the position
* and width of the time bin.
* This target area is default disk shaped, but may also be rectangular
* if specified focus_xw and focus_yh
* or focus_aw and focus_ah, respectively in meters and degrees.
* The target itself is either situated according to given coordinates (x,y,z),
* or setting the relative target_index of the component to focus at
* (next is +1). This target position will be set to its AT position.
* When targeting to centered components, such as spheres or cylinders,
* define an Arm component where to focus at.
*
* Example: TOFRes_sample(radius_i=0.001, radius_o=0.01, h=0.04,
      focus_xw=0.025, focus_yh=0.025,
*     E0=14.6,dE=2, target_x=0, target_y=0, target_z=1)
*
* %P
* INPUT PARAMETERS:
*
* radius_i: Inner radius of hollow cylinder in (x,z) plane, or width of
*             box along X (m)
* radius_o: Outer radius of hollow cylinder (m)
* h:        Height of box or cylinder along Y (m)
* focus_r:  Radius of sphere containing target. (m)
* target_x:
* target_y: position of target to focus at (m)
* target_z:
* time_bin: position of time bin (us)
* time_width: width of time bin (us)
*
* Optional parameters
* xwidth:   horiz. dimension of sample, as a width (m)
* yheight:  vert.  dimension of sample, as a height (m)
* zthick:   thickness of sample (m)
* focus_xw: horiz. dimension of a rectangular area (m)
* focus_yh: vert.  dimension of a rectangular area (m)
* focus_aw: horiz. angular dimension of a rectangular area (deg)
* focus_ah: vert.  angular dimension of a rectangular area (deg)
* target_index: relative index of component to focus at, e.g. next is +1 (1)
*
* %E
*******************************************************************************/

DEFINE COMPONENT TOFRes_sample
DEFINITION PARAMETERS ()
SETTING PARAMETERS (radius_i=0,radius_o=0.01,h=0.05,focus_r=0.05,
    time_bin=20000, time_width=10,
    target_x=0, target_y=0, target_z=.5, focus_xw=0, focus_yh=0,
    focus_aw=0, focus_ah=0, xwidth=0, yheight=0, zthick=0, int target_index=0)
OUTPUT PARAMETERS (res_struct)
STATE PARAMETERS (x,y,z,vx,vy,vz,t,s1,s2,p)

DECLARE
%{
  double f, E0, dE;
  struct Res_sample_struct
    {
      double ki_x,ki_y,ki_z,kf_x,kf_y,kf_z;
      double rx,ry,rz,pi;
      char   isrect;      /* true when sample is a box */
      double distance;    /* when non zero, gives rect target distance */
      double aw,ah;       /* rectangular angular dimensions */
      double xw,yh;       /* rectangular metrical dimensions */
      double tx,ty,tz;    /* target coords */
    } res_struct;
%}

INITIALIZE
%{

  f = 50;
  if (!radius_o || !h) {
    if (!xwidth || !yheight || !zthick) exit(fprintf(stderr,"Res_sample: %s: sample has no volume (zero dimensions)\n", NAME_CURRENT_COMP));
    else res_struct.isrect=1; }
  else res_struct.isrect=0;

  /* now compute target coords if a component index is supplied */
  if (target_index)
  {
    Coords ToTarget;
    ToTarget = coords_sub(POS_A_COMP_INDEX(INDEX_CURRENT_COMP+target_index),POS_A_CURRENT_COMP);
    ToTarget = rot_apply(ROT_A_CURRENT_COMP, ToTarget);
    coords_get(ToTarget, &res_struct.tx, &res_struct.ty, &res_struct.tz);
  }
  else
  { res_struct.tx = target_x; res_struct.ty = target_y; res_struct.tz = target_z; }

  res_struct.distance=sqrt(res_struct.tx*res_struct.tx
      +res_struct.ty*res_struct.ty+res_struct.tz*res_struct.tz);
  /* different ways of setting rectangular area */
  res_struct.aw  = res_struct.ah = 0;
  if (focus_xw) {
    res_struct.xw = focus_xw;
  }
  if (focus_yh) {
    res_struct.yh = focus_yh;
  }
  if (focus_aw) {
    res_struct.aw = DEG2RAD*focus_aw;
  }
  if (focus_ah) {
    res_struct.ah = DEG2RAD*focus_ah;
  }
%}

TRACE
%{
  double t0, t3;                /* Entry/exit time for outer cylinder */
  double t1, t2;                /* Entry/exit time for inner cylinder */
  double v;                     /* Neutron velocity */
  double E;
  double l_full;                /* Flight path length for non-scattered neutron */
  double flight_time;           /* Calculated time-of-flight from source to target (detector) */
  double dt0, dt1, dt2, dt;     /* Flight times through sample */
  double solid_angle=0;         /* Solid angle of target as seen from scattering point */
  double aim_x, aim_y, aim_z, aim_length;
                                /* Position of target relative to scattering point */
  double scat_factor;           /* Simple cross-section model */
  int    intersect=0;


  if(res_struct.isrect)
    intersect = box_intersect(&t0, &t3, x, y, z, vx, vy, vz, xwidth, yheight, zthick);
  else
    intersect = cylinder_intersect(&t0, &t3, x, y, z, vx, vy, vz, radius_o, h);

  if(intersect)
  {
    if(t0 < 0) ABSORB;
    if(res_struct.isrect) { t1 = t2 = t3; scat_factor = 2*zthick; } /* box sample */
    else {  /* Hollow cylinder sample */
      /* Neutron enters at t=t0. */
      if(!cylinder_intersect(&t1, &t2, x, y, z, vx, vy, vz, radius_i, h))
        t1 = t2 = t3;
      scat_factor = 2*radius_o; }
    dt0 = t1-t0;                  /* Time in sample, ingoing */
    dt1 = t2-t1;                  /* Time in hole */
    dt2 = t3-t2;                  /* Time in sample, outgoing */
    v = sqrt(vx*vx + vy*vy + vz*vz);
    l_full = v * (dt0 + dt2);     /* Length of full path through sample */
    p *= l_full/scat_factor;      /* Scattering probability */
    dt = rand01()*(dt0+dt2);      /* Time of scattering (relative to t0) */
    if (dt > dt0)
      dt += dt1;

    PROP_DT(dt+t0);             /* Point of scattering */

    /* Store initial neutron state. */
    if(p == 0) ABSORB;
    res_struct.pi = p;
    res_struct.ki_x = V2K*vx;
    res_struct.ki_y = V2K*vy;
    res_struct.ki_z = V2K*vz;
    res_struct.rx = x;
    res_struct.ry = y;
    res_struct.rz = z;

    aim_x = res_struct.tx-x;       /* Vector pointing at target (anal./det.) */
    aim_y = res_struct.ty-y;
    aim_z = res_struct.tz-z;
    aim_length = sqrt(aim_x*aim_x+aim_y*aim_y+aim_z*aim_z);
    if(res_struct.aw && res_struct.ah) {
      randvec_target_rect_angular(&vx, &vy, &vz, &solid_angle,
        aim_x, aim_y, aim_z, res_struct.aw, res_struct.ah, ROT_A_CURRENT_COMP);
    } else if(res_struct.xw && res_struct.yh) {
      randvec_target_rect(&vx, &vy, &vz, &solid_angle,
        aim_x, aim_y, aim_z, res_struct.xw, res_struct.yh, ROT_A_CURRENT_COMP);
    } else {
        randvec_target_circle(&vx, &vy, &vz, &solid_angle,
          aim_x, aim_y, aim_z, focus_r);
    }
    NORM(vx, vy, vz);
    flight_time = -t + 1e-6*(time_bin + time_width * randpm1());
/* Correct for too large or negative flight_times */
/* Should perhaps be done more cleanly (more user choices) ?! */
    for (; flight_time<0; flight_time += 1/f);
    for (; flight_time>1/f; flight_time -= 1/f);
    v = aim_length / flight_time;
/* !! Remember later to correct for Jacobian in MC choice, t to V !! */

/*    E=E0+dE*randpm1();
    v=sqrt(E)*SE2V; */

    vx *= v;
    vy *= v;
    vz *= v;
    SCATTER;

      /* Store final neutron state. */
    res_struct.kf_x = V2K*vx;
    res_struct.kf_y = V2K*vy;
    res_struct.kf_z = V2K*vz;

/* printf("vf= (%g,%g,%g), kf=(%g,%g,%g) \n",vx,vy,vz,
  res_struct.kf_x,res_struct.kf_y,res_struct.kf_z); */
  }
%}

MCDISPLAY
%{
  magnify("xyz");
  if(res_struct.isrect)
  {                             /* Flat sample. */
    double xmin = -0.5*xwidth;
    double xmax =  0.5*xwidth;
    double ymin = -0.5*yheight;
    double ymax =  0.5*yheight;
    double len = zthick/2;
    multiline(5, xmin, ymin, -len,
                 xmax, ymin, -len,
                 xmax, ymax, -len,
                 xmin, ymax, -len,
                 xmin, ymin, -len);
    multiline(5, xmin, ymin, len,
                 xmax, ymin, len,
                 xmax, ymax, len,
                 xmin, ymax, len,
                 xmin, ymin, len);
    line(xmin, ymin, -len, xmin, ymin, len);
    line(xmax, ymin, -len, xmax, ymin, len);
    line(xmin, ymax, -len, xmin, ymax, len);
    line(xmax, ymax, -len, xmax, ymax, len);
  }
  else
  {
    circle("xz", 0,  h/2.0, 0, radius_i);
    circle("xz", 0,  h/2.0, 0, radius_o);
    circle("xz", 0, -h/2.0, 0, radius_i);
    circle("xz", 0, -h/2.0, 0, radius_o);
    line(-radius_i, -h/2.0, 0, -radius_i, +h/2.0, 0);
    line(+radius_i, -h/2.0, 0, +radius_i, +h/2.0, 0);
    line(0, -h/2.0, -radius_i, 0, +h/2.0, -radius_i);
    line(0, -h/2.0, +radius_i, 0, +h/2.0, +radius_i);
    line(-radius_o, -h/2.0, 0, -radius_o, +h/2.0, 0);
    line(+radius_o, -h/2.0, 0, +radius_o, +h/2.0, 0);
    line(0, -h/2.0, -radius_o, 0, +h/2.0, -radius_o);
    line(0, -h/2.0, +radius_o, 0, +h/2.0, +radius_o);
  }
%}

END
