/****************************************************************************
*
* 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
*
* %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. 
* 
* Parameters: 
* length:             		 [m]        Supermirror length 
* thickness_in_mm: [mm]       Substrate thickness in mm
* 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.
* 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
* 
* 
* SUPERMIRROR COATING -------------------------------------------------
* Mirror reflectivity parameters are specified by name to look up 6 parameters {R0, Qc, alpha, m, W, beta}. 
* 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:    [AA2]    curvature of reflectivity
* 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. 
* Parameters: 
* 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-)
*
*
* ABSORBER LAYER -------------------------------------------------
* Absorber parameters are either specified by name or by parameters L_abs, L_inc and the coating thickness. 
* 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
* 
* Parameters: 
* 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
* 
*
* SUBSTRATE -------------------------------------------------
* Substrate parameters are specified by name to look up 3 parameters {L_abs, L_inc, SLD}. 
* 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
* 
* specify substrate material name matching one of those defined in "Supermirror_substrate_materials.txt". 
* Parameter: 
* substrate_material_name: [string]  substrate material name in "Supermirror_substrate_materials.txt". (Material name is case-insensitive) 
*
*
*
* 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.
* Parameters: 
* initial_placement_at_module_origin:	[string] "TopFrontEdgeCentre","FrontSubstrateCentre","BottomFrontEdgeCentre" 
*												 (insensitive to case and reginal English spelling)
* tilt_y_axis_location:					[string] "TopFrontEdge","TopMirrorCentre","TopBackEdge"
*												 "FrontSubstrateCentre","SubstrateCentre","BackSubstrateCentre", 
*												 "BottomFrontEdge","BottomMirrorCentre","BottomBackEdge" 
*												 (insensitive to case and reginal English spelling)
* tilt_about_y_first_in_degree:			[°]		 First: tilt about the x-axis at the selected location
* translation_second:					[m,m,m]	 Second: translate, use Coords structure
* rot_about_z_third_in_degree			[°]		 Third: rotate about z-axis (beam 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.
* Parameter: 
* sm:	[struct Supermirror]  Supermirror structure
*
* 
*
* 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
* 
* 
*
* End RAY-TRACING parameters 
*
* %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;
				}
			}
			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;
		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
