/****************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright 1997-2003, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Component: SupermirrorFlat
*
* %I
* Written by: Wai Tung Lee
* Date: September 2024
* Origin: ESS
*
* Model of flat supermirror with parallel mirror surfaces
*
* %D
* Calculate the neutron reflection and transmission of a flat supermirror with parallel mirror surfaces.
* The following can be specified: 
* 1. reflection from either mirror coating or substrate surface, mirror coating can be single-side, double-side, 
* 2. absorber coating:  beneath mirror coating, single-side coated, double-side coated, or uncoated,
* 3. refraction and total reflection at substrate surface,
* 4. attenuation and internal reflection inside substrate. 
*
* note: supermirror name is case-sensitive 
*       text entries of parameters below are not sensitive to case
*        default regional spelling is UK, but some paramters accept other reginal spelling
*
*
* INITIALISATION parameters:
*
* 
* STEP 1: Specify supermirror shape, supermirror coating, absorber and substrate material
*         In this step, supermirror is standing vertically, mirror coating = yz plane with long side along +z.
*
* SUPERMIRROR SHAPE -------------------------------------------------
* 1. +x: "top" mirror surface normal, horizontal transverse to beam; 
*    +y: vertical up, parallel to  mirror surface; 
*    +z: longitudinal to beam, parallel to mirror surface; 
* 2. top and bottom mirrors' surface normals are +x and -x, respectively; 
* 3. entrance-side edge normal is -z, exit-side edge normal is +z (beam direction);
* 3. normals of and points on the remaining edge surfaces at +y and -y are user-specified, 
*
* Mirror shape is specified by length, thickness, and the normals of and points on the edge surfaces at +y and -y sides. 
* 
* The two side-edges at +y and -y are taken to be symmetric about the xz-plane, only one needs to be specified. 
* Use InitialiseStdSupermirrorFlat_detail if not symmetric.
* 
* SUPERMIRROR COATING -------------------------------------------------
* Mirror reflectivity parameters are specified by name to look up 6 parameters {R0, Qc, alpha, m, W, beta}. 
*
* If name = SubstrateSurface, reflectivity is calculated by 
*                   R = R0 (when q<=Qc), R0*( (q - (q^2 - Qc^2)^1/2) / (q + (q^2 - Qc^2)^1/2) )^2 (when q>Qc), with Qc=sqrt(16 Pi SLD). 
* with Qc = Qc_sub defined in SubstrateSurfaceParameters and SLD defined in SubstrateParameters,  
* otherwise reflectivity is calculated by 
*                   R = R0 (when q<=Qc), R = R0*0.5*(1-tanh((q - m*Qc)/W))*(1-alpha*(q-Qc)+beta*(q-Qc)*(q-Qc)) (when q>Qc).
* 
* specified mirror material name matching one of those defined in "Supermirror_reflective_coating_materials.txt". 
* If the mirror is non-polarising, specify the same name and parameters for spin plus and spin minus.
*
* ABSORBER LAYER -------------------------------------------------
* Absorber parameters are either specified by name or by parameters L_abs, L_inc and the coating thickness. 
* 
*
* SUBSTRATE -------------------------------------------------
* Substrate parameters are specified by name to look up 3 parameters {L_abs, L_inc, SLD}. 
* 
* specify substrate material name matching one of those defined in "Supermirror_substrate_materials.txt". 
*
*
*
* STEP 2: Orient and position the as-defined supermirror in the McStas module XYZ coordinates. 
*
* 1. Specify initially a location on the front side of the mirror (see parameter "initial_placement_at_origin" below). 
*    The supermirror is shifted so that the selected point coincides with the origin of the McStas module XYZ coordinates
* 2. Then the orientation and position of the supermirror are specified by the movements in sequence as
*    1st, tilting about an axis in +y direction at a selected location (see parameters "tilt_y_axis_location" and "tilt_about_y_first_in_degree" below), 
*    2nd, translation, 
*    3rd, rotation about the z-axis of the McStas module XYZ coordinates.
*
*
*
* OUTPUT: 
* Supermirror struct with all parameter values entered and initialised
* User declares "Supermirror supermirror;" or its equivalence, 
* then passes pointer "&supermirror" to function.
*
* 
*
* End INITIALISATION parameters
*
*
*
*
* RAY-TRACING parameters
*
* FUNCTION IntersectStdSupermirrorFlat 
* INPUT: 
* 	neutron parameters: w_sm=p, t_sm=t, p_sm=coords_set(x,y,z), v_sm=coords_set(vx,vy,vz), s_sm=coords_set(sx,sy,sz)
* 	last_exit_time			[s]       time of last exit from a supermirror, use F_INDETERMINED if not determined. (F_INDETERMINED defined in this file)
* 	last_exit_point			[m,m,m]   position of last exit from a supermirror, use coords_set(F_INDETERMINED,F_INDETERMINED,F_INDETERMINED) if not determined.
* 	last_exit_plane			[1]       plane of last exit from a supermirror, use I_INDETERMINEDif not determined.
* 	sm:						[struct]  Supermirror structure
* OUTPUT: 
* 	num_intersect:          [1]		number of intersects through supermirror
* 	First intersect time, point, plane if there is intersect.
* 	User declare one or more parameters, e.g. "int num_intersect; double first_intersect_time; Coords first_intersect_point; int first_intersect_plane;", 
* 	then passes pointers "&num_intersect, &first_intersect_time, &first_intersect_point, &first_intersect_plane" to function.
* 	Pass 0 as pointer if not needed.
* 	first_intersect_dtime:  [s]		time difference from neutron to first intersect
* 	first_intersect_dpoint: [m,m,m]	position difference from neutron to point of first intersect
* 	first_intersect_time:   [s]		time of first intersect
* 	first_intersect_point:  [m,m,m]	point of first intersect
* 	first_intersect_plane:  [1]		plane of first intersect
* RETRUN: 
* 	sm_Intersected, sm_Missed. sm_Error
* 
* 
* FUNCTION StdSupermirrorFlat
* INPUT:
* 	sm						[struct]  Supermirror structure
* INPUT & OUTPUT: 
* 	neutron parameters: w_sm=p, t_sm=t, p_sm=coords_set(x,y,z), v_sm=coords_set(vx,vy,vz), s_sm=coords_set(sx,sy,sz)
* 	last_exit_time			[s]       time of last exit from a supermirror, use F_INDETERMINED if not determined. (F_INDETERMINED defined in this file)
* 	last_exit_point			[m,m,m]   position of last exit from a supermirror, use coords_set(F_INDETERMINED,F_INDETERMINED,F_INDETERMINED) if not determined.
* 	last_exit_plane			[1]       plane of last exit from a supermirror, use I_INDETERMINEDif not determined.
* RETRUN: 
* 	sm_Exited, sm_Absorbed. sm_Error
* sm:	[struct Supermirror]  Supermirror structure
* 
*
* End RAY-TRACING parameters 
*
* %Parameters: 
* length:             		 [m]        Supermirror length 
* thickness_in_mm: [mm]       Substrate thickness in mm
* side_edge_normal:   		 [1,1,1]    Normal vector of one of the two side-edge surface, use Coords structure, doesn't need to be normalised
* side_edge_point:    		 [m,m,m]    A point on one of the two side-edge surface with its normal specified above, use Coords structure
* R0:      [1]      reflectivity below critical scattering vector
* Qc:      [AA^-1]   magnetude of critical scattering vector (at m=1)
* alpha:   [AA]     slope of reflectivity
* m:       [1]      m-value of material. Zero means no reflection
* W:       [AA^-1]   width of reflectivity cut-off
* beta:    [AA^2]    curvature of reflectivity
* mirror_coated_side:              [string]  Sequential keywords combinations of position and surface property. position: "Both", "Top", "Bottom"; surface property: "Coated", "SubstrateSurface", "NoReflection"; Note: "NotCoated"="Empty"="SubstrateSurface", e.g. "BothCoated", "BottomCoatedTopSubstrate" (case-insensitive.)
* mirror_spin_plus_material_name:  [string]  mirror spin+ material name in "Supermirror_reflective_coating_materials.txt". (case-insensitive)
* mirror_spin_plus_m:              [1]       mirror spin+ m-value, -1=use default value 
* mirror_spin_minus_material_name: [string]  mirror spin- material name in "Supermirror_reflective_coating_materials.txt". (case-insensitive)
* mirror_spin_minus_m:             [1]       mirror spin- m-value, -1=use default value (useful for spin-)
* L_abs:                         [cm]          absorption 1/e attenuation length for 1 Å neutrons (complete & immediate absorption: 0, no absorption: -1)
* L_inc:                         [cm]          incoherent scattering 1/e attenuation length (complete & immediate attenuation: 0, no attenuation: -1)
* absorber_thickness_in_micron:  [micrometer]  absorber coating thickness in micrometer
* absorber_coated_side:         [string]      "BothNotCoated", "BothCoated", "TopCoated", "BottomCoated".
* absorber_material_name:       [string]      absorber material name in "Supermirror_absorber_coating_materials.txt" or "Empty", 0="Empty",
* absorber_thickness_in_micron: [micrometer]  absorber coating thickness in micrometer
* L_abs:   [cm]     absorption 1/e attenuation length for 1 Å neutrons (complete & immediate absorption: 0, no absorption: -1, Si: 210.460, SiO2: 1046.742, Boron:0.018, Gd:0.002, Borofloat:0.940)
* L_inc:   [cm]     spin-incoherent scattering 1/e attenuation length (complete & immediate attenuation: 0, no attenuation: -1, Si: 9927.652, SiO2: 441.380, Boron:4.505, Gd:1e9, Borofloat: 146.33
* SLD:     [AA^-2]   Coherent scattering length density
* substrate_material_name: [string]  substrate material name in "Supermirror_substrate_materials.txt". (Material name is case-insensitive) 
* initial_placement_at_origin:	[string] "TopFrontEdgeCentre","FrontSubstrateCentre","BottomFrontEdgeCentre" (insensitive to case and regional English spelling)
* tilt_y_axis_location:					[string] "TopFrontEdge","TopMirrorCentre","TopBackEdge", "FrontSubstrateCentre","SubstrateCentre","BackSubstrateCentre", "BottomFrontEdge","BottomMirrorCentre","BottomBackEdge"  (insensitive to case and regional English spelling)
* tilt_about_y_first_in_degree:			[deg]		 First: tilt about the x-axis at the selected location
* translation_second_x:					[m]    Second: translate along x
* translation_second_y: [m] Second: translate along y
* translation_second_z: [m] Second: translate along z
* rot_about_z_third_in_degree:		[deg]   Third: rotate about z-axis (beam axis) of the McStas module XYZ coordinates
* tracking: [str] "DetailTracking" records the detailed neutron path inside the mirror; any other value disables this
*
* %P
*
* %L
*
* %E
*******************************************************************************/

DEFINE COMPONENT SupermirrorFlat


SETTING PARAMETERS (vector side_edge_normal={0,+1,0}, vector side_edge_point={0,+0.1,0},
	length=0.5, thickness_in_mm=0.3,
	string mirror_coated_side="BothCoated",
	string mirror_spin_plus_material_name="FeSiPlus",
	       mirror_spin_plus_m=3.5, 
	string mirror_spin_minus_material_name="FeSiMinus", 
	       mirror_spin_minus_m=-1, 
	string substrate_material_name="Glass", 
	string absorber_coated_side="BothNotCoated", 
	string absorber_material_name="Gd", 
	       absorber_thickness_in_micron=100, 
	string initial_placement_at_origin="FrontSubstrateCentre",
	string tilt_y_axis_location="FrontSubstrateCentre", 
	tilt_about_y_first_in_degree=1.5, 
	translation_second_x=0, 
	translation_second_y=0, 
	translation_second_z=0, 
	rot_about_z_third_in_degree=0, 
	string tracking="DetailTracking", 
	int keep_if_missed=1)

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

  #ifndef SUPERMIRROR
  %include "supermirror-lib"
  #define SUPERMIRROR
  #endif
%}
DECLARE
%{

  Coords translation_second_coords;
  Coords side_edge_normal_coords;
  Coords side_edge_point_coords;

  Supermirror sm;

  int initialised;

  int i;
  int j;
  int n;
  int m;
  double xx;
  double yy;
  double zz;

  double w_sm;
  double t_sm;
  Coords p_sm;
  Coords v_sm;
  Coords s_sm;
  double last_exit_time;
  Coords last_exit_point;
  int last_exit_plane;

  int num_intersect;
  double first_intersect_dtime;
  Coords first_intersect_dpoint;
  double first_intersect_time;
  Coords first_intersect_point;
  int is_tracking;
  int first_intersect_plane;
  int outcome1;
  int outcome2;

  NeutronRecord* neutron_record;
  int number_of_neutron_records;
%}
INITIALIZE
%{
  printf ("\tSupermirrorFlat.comp: INITIALIZE \n");
  printf ("\tname=%s\n", NAME_CURRENT_COMP);
  printf ("\tlength = %g m, thickness_in_mm = %g mm\n", length, thickness_in_mm);
  printf ("\tside_edge normal,point = (%g,%g,%g), (%g,%g,%g) m\n", side_edge_normal[0], side_edge_normal[1], side_edge_normal[2], side_edge_point[0],
          side_edge_point[1], side_edge_point[2]);
  printf ("\tmirror_coated_side = %s\n", mirror_coated_side);
  printf ("\tmirror_spin_plus_material  name,m = %s, %g\n", mirror_spin_plus_material_name, mirror_spin_plus_m);
  printf ("\tmirror_spin_minus_material name,m = %s, %g\n", mirror_spin_minus_material_name, mirror_spin_minus_m);
  printf ("\tsubstrate_material name = %s\n", substrate_material_name);
  printf ("\tabsorber_coated_side = %s\n", absorber_coated_side);
  printf ("\tabsorber_material name,thickness in micron = %s, %g\n", absorber_material_name, absorber_thickness_in_micron);
  printf ("\tinitial_placement_at_origin = %s\n", initial_placement_at_origin);
  printf ("\ttilt_y_axis_location = %s\n", tilt_y_axis_location);
  printf ("\ttilt_about_y_first_in_degree, translation_second, rot_about_z_third_in_degree = %g deg, (%g,%g,%g) m, %g deg\n", tilt_about_y_first_in_degree,
          translation_second_x, translation_second_y, translation_second_z, rot_about_z_third_in_degree);

  side_edge_normal_coords = coords_set (side_edge_normal[0], side_edge_normal[1], side_edge_normal[2]);
  side_edge_point_coords = coords_set (side_edge_point[0], side_edge_point[1], side_edge_point[2]);
  translation_second_coords = coords_set (translation_second_x, translation_second_y, translation_second_z);

  if (strcmp (tracking, "DetailTracking") == 0) {
    is_tracking = 1;
  } else {
    is_tracking = 0;
  }

  // Initialise using simplified initialise function
  initialised = InitialiseStdSupermirrorFlat (

      NAME_CURRENT_COMP,

      length,          // m supermirror length projection along z-axis
      thickness_in_mm, // mm
      side_edge_normal_coords, side_edge_point_coords,

      mirror_coated_side, // Sequential combination of keywords of
                          // position: "Both", "Top", "Bottom";
                          // surface property: "Coated", "Substrate", "NoReflection";
                          // e.g. "BothCoated", "BottomCoatedTopSubstrate",
                          // case-insensitive.
      mirror_spin_plus_material_name, mirror_spin_plus_m, mirror_spin_minus_material_name, mirror_spin_minus_m,
      absorber_coated_side, //"BothCoated", "TopCoated", "BottomCoated", "BothNotCoated"
      absorber_material_name, absorber_thickness_in_micron, substrate_material_name,

      initial_placement_at_origin,  //"TopFrontEdgeCentre","FrontSubstrateCentre","BottomFrontEdgeCentre"
      tilt_y_axis_location,         //"TopFrontEdge","TopMirrorCentre","TopBackEdge"
                                    //"FrontSubstrateCentre","SubstrateCentre","BackSubstrateCentre",
                                    //"BottomFrontEdge","BottomMirrorCentre","BottomBackEdge"
      tilt_about_y_first_in_degree, // first, tile about x-axis at selected location
      translation_second_coords,    // second, translate reference point
      rot_about_z_third_in_degree,  // third, rotate about global z-axis

      is_tracking,

      &sm);

  if (is_tracking == 1) {
    neutron_record = (sm.proc).nr;
  }
%}
TRACE
%{

  if (initialised != 0) {

    w_sm = p;
    t_sm = t;
    p_sm = coords_set (x, y, z);
    v_sm = coords_set (vx, vy, vz);
    s_sm = coords_set (sx, sy, sz);

    last_exit_time = F_INDETERMINED;
    last_exit_point = coords_set (F_INDETERMINED, F_INDETERMINED, F_INDETERMINED);
    last_exit_plane = I_INDETERMINED;

    // First check if neutron intersect supermirror
    // IntersectStdSupermirrorFlat
    // outcome: sm_Intersected, sm_Missed, sm_Error
    outcome1 = IntersectStdSupermirrorFlat (t_sm, p_sm, v_sm, last_exit_time, last_exit_point, last_exit_plane, &sm, &num_intersect, &first_intersect_dtime,
                                            &first_intersect_dpoint, &first_intersect_time, &first_intersect_point, &first_intersect_plane);

    switch (outcome1) {
    case sm_Intersected: {
      // update neutron parameters
      t = first_intersect_time;
      coords_get (first_intersect_point, &x, &y, &z);

      SCATTER;

      // StdSupermirrorFlat
      // outcome2: sm_Exited, sm_Absorbed, sm_Error
      outcome2 = StdSupermirrorFlat (&w_sm, &t_sm, &p_sm, &v_sm, &s_sm, &last_exit_time, &last_exit_point, &last_exit_plane, &sm);

      if (is_tracking) {
        number_of_neutron_records = (sm.proc).n_nr;

        // first output detail tracking
        if (number_of_neutron_records > 1) {
          for (i = 0; i < number_of_neutron_records; i++) {
            p = (((sm.proc).nr)[i]).nr_w;
            t = (((sm.proc).nr)[i]).nr_t;
            coords_get ((((sm.proc).nr)[i]).nr_p, &x, &y, &z);
            coords_get ((((sm.proc).nr)[i]).nr_v, &vx, &vy, &vz);
            coords_get ((((sm.proc).nr)[i]).nr_s, &sx, &sy, &sz);

            SCATTER;
          }
        }
      }

      // get the final neutron parameters
      p = w_sm;
      t = t_sm;
      coords_get (p_sm, &x, &y, &z);
      coords_get (v_sm, &vx, &vy, &vz);
      coords_get (s_sm, &sx, &sy, &sz);

      // outcome: sm_Exited, sm_Absorbed, sm_Error
      switch (outcome2) {
      case sm_Exited:
        SCATTER;
        break;
      case sm_Absorbed:
        ABSORB;
        break;
      case sm_Error:
        p = 0;
        printf ("SupermirrorFlat.comp: StdSupermirrorFlat: outcome2=%s(%d), something's wrong.\n", ((sm.proc).event)[outcome2], outcome2);
        break; // something's wrong
      default:
        p = 0;
        printf ("SupermirrorFlat.comp: StdSupermirrorFlat returns outcome2=%d, something's wrong.\n", outcome2);
        break;
      }
      break;
    }
    case sm_Missed:
      if (keep_if_missed == 0)
        p = 0;
      break;
    case sm_Error:
      p = 0; // something's wrong
      printf ("SupermirrorFlat.comp: IntersectStdSupermirrorFlat returns outcome1=%s(%d), something's wrong.\n", ((sm.proc).event)[outcome1], outcome1);
      break;
    default:
      p = 0;
      printf ("SupermirrorFlat.comp: IntersectStdSupermirrorFlat returns outcome1=%d, something's wrong.\n", outcome1);
      break;
    }
  }
%}
FINALLY
%{
  EmptySupermirrorFlatData (&sm);
%}
MCDISPLAY
%{
  Coords* vp1;
  int* ifvi1;
  int k1, k2;
  n = sm.geo.nf;
  vp1 = sm.geo.vp;
  for (i = 0; i < n; i++) {
    m = sm.geo.afvi[i].nfvi;
    ifvi1 = sm.geo.afvi[i].ifvi;
    if (m == 3) {
      polygon (3, vp1[ifvi1[0]].x, vp1[ifvi1[0]].y, vp1[ifvi1[0]].z, vp1[ifvi1[1]].x, vp1[ifvi1[1]].y, vp1[ifvi1[1]].z, vp1[ifvi1[2]].x, vp1[ifvi1[2]].y,
               vp1[ifvi1[2]].z);
      continue;
    }
    if (m == 4) {
      polygon (4, vp1[ifvi1[0]].x, vp1[ifvi1[0]].y, vp1[ifvi1[0]].z, vp1[ifvi1[1]].x, vp1[ifvi1[1]].y, vp1[ifvi1[1]].z, vp1[ifvi1[2]].x, vp1[ifvi1[2]].y,
               vp1[ifvi1[2]].z, vp1[ifvi1[3]].x, vp1[ifvi1[3]].y, vp1[ifvi1[3]].z);
      continue;
    }
    k1 = ifvi1[0];
    for (j = 1; j < m; j++) {
      k2 = ifvi1[j];
      line (vp1[k1].x, vp1[k1].y, vp1[k1].z, vp1[k2].x, vp1[k2].y, vp1[k2].z);
      k1 = k2;
    }
    k2 = ifvi1[0];
    line (vp1[k1].x, vp1[k1].y, vp1[k1].z, vp1[k2].x, vp1[k2].y, vp1[k2].z);
  }
%}
END
