/******************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright 1997-2002, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Component: Monochromator_bent
*
* %I
* Written by: Daniel Lomholt Christensen <dlc@math.ku.dk> with help from Jan Šaroun
* Based on the model implemented by Jan Šaroun in the paper published in
* Nuclear Instruments and Methods in Physics Research Section A: Accelerators, Spectrometers, Detectors and Associated Equipment
* Volume 529, Issues 1 through 3, 21 August 2004, Pages 162-165
* 
*
* Date: 24 August 2023
* Origin: ILL / Niels Bohr Institute, University of Copenhagen.
*
* A bent crystal monochromator. Mosacity and bending radius can be set.
*
* %D
* This monochromator is a number of lamella of a perfect crystal. 
* The lamella are placed in the horizontal plane, behind each other, centered at 0.
* The crystal is bent, so that it follows a curve on a cylinder of radius_x.
* The monochromator lies along the z plane, so when a diffraction angle of theta 
* is desired, it should just be inserted in the ROTATED parameter around
* the y-axis.
* Instruments that showcase the use of this component is the 
* "Test_monochromator_bent.instr", and the "ILL_SALSA.instr" under the examples folder.
* SALSA showcases its complex use in a real instrument, while Test_monochromator_bent
* makes a simple show of its capabilities.
*
*
* %P
* INPUT PARAMETERS:
*
* zwidth: [m]               			Width of each lamella without bending.
* yheight: [m]              			Height of each lamella without bending.
* xthickness: [m]           			Thickness of each lamella without bending.
* radius_x: [m]             			Radius of the circle the monochromator bends on in the plane.
* lamella_slabs = 1						Amount of horizontal lamella in you monochromator.
* lamella_gap_size = 0					Gap between said horizontal lamella.
* plane_of_reflection: ["Si400"] 		The plane of reflection from the material. The list of possible reflections can 
* 										can be seen in the source code.
* angle_to_cut_horizontal [degrees]  	Angle between cut and normal of crystal slab, horizontally
* angle_to_cut_vertical [degrees] 		Angle between cut and normal of crystal slab, vertically ! NOTE: This may not work as intended.
* mosaicity[arc minutes]                Gaussian mosaicity of the crystal. Always the horizontal mosaicity
* mosaic_anisotropy                     Anisotropy of the mosaicity, changes vertical mosaicity to be mosaic_anisotropy*mosaicity
* domainthickness [micro meter]			Thickness of the crystal domains.
* temperature: [K]          			Temperature of the monochromator in Kelvin.
* verbose: [0]							Verbosity of the monochromator. Used for debugging. 
*
* %E
******************************************************************************/
DEFINE COMPONENT Monochromator_bent

SETTING PARAMETERS (zwidth = 0.2, yheight = 0.1,
				  xthickness = 0.0005,
				  radius_x = 2,
				  lamella_slabs = 1,
				  lamella_gap_size = 0,
				  string plane_of_reflection = "Si400",
				  angle_to_cut_horizontal = 0,
				  angle_to_cut_vertical = 0,
				  mosaicity = 30,
				  mosaic_anisotropy = 1,
				  domainthickness = 10,
				  temperature = 300,
				  int verbose = 0)

NOACC
// The component is currently "NOACC" only, there are thread race-conditions on GPU 

SHARE
%{
	#include <string.h>
	///////////////////////////////////////////////////////////////////////////
	/////////////// Structs and simple functions specific for this component
	///////////////////////////////////////////////////////////////////////////

	struct Monochromator_values{
		double length, height, thickness;
		double mosaicity_horizontal, mosaicity_vertical;
		int type;
		double radius_horizontal;
		double Debye_Waller_factor;
		double lattice_spacing;
		double Maier_Leibnitz_reflectivity;
		double poisson_ratio;
		double bound_atom_scattering_cross_section;
		double absorption_for_1AA_Neutrons;
		double incoherent_scattering_cross_section;
		double volume;
		double Constant_from_Freund_paper;
		double debye_temperature;
		double atomic_number;
		double temperature_mono;
		double B0;
		double BT;
		double single_phonon_absorption;
		double multiple_phonon_absorption;
		double nuclear_capture_absorption;
		double total_absorption;
		double tau[3];
		double perp_to_tau[3];
		double lattice_spacing_gradient_field[3][3];
		double gradient_of_bragg_angle;
		int lamellas;
		double gap_size;
		double domain_thickness;
		double max_angle;
		double min_angle;
	};

	struct neutron_values {
		// Statically allocate vectors that are always 3 
		double ki[3]; // Incoming wavevector
		double kf[3]; // outgoig wavevector
		double r[3];
		double v[3]; // velocity of neutron
		double tau[3]; //Reciprocal lattice vector
		double ki_size; // size of incoming wavevector
		double v_size; // speed
		double tau_size; // size of reciprocal lattice vector
		double tau_size_zero; // basic of reciprocal lattice vector
		double kf_size; // size of outgoing wavevector
		double* vert_angle; // Angle of deviation by the mosaic crystal vertically
		double* horiz_angle; // Angle of deviation by the mosaic crystal in x-z plane
		double* beta; // Gradient of deviation from bragg condition
		double* eps_zero; // Angular deviation from bragg angle
		double absorption; // Absorption factor
		double path; // Length of the path the neutron follows
		double wavelength; // De Broglie wavelength of neutron
		double kinematic_reflectivity; // The Q value from the paper this code is based on.
		double* path_length; // The time spent in crystals, to add to path for attenuation 
		double* entry_time; // Time from start of crystal, to entrance of each lamella 
		double* exit_time; // Time from start of crystal, to exit of each lamella 
		double* probabilities; // Probability of reflection in each lamella 
		double* accu_probs; // Accumulating probability in each lamella 
		double* TOR; // The time in s from crystal edge to reflection 
	};

	enum crystal_type {flat, bent, mosaic, bent_mosaic};
	/////////////// Define sign function

	double sign(double x){
		if (x >= 0) return 1;
		if (x < 0) return -1;
	}
	
	double square(double x){
		return x*x;
	}
	// Function to generate numbers in a uniform distribution
	double random_normal_distribution(double* sigma, _class_particle* _particle){
		double u1, u2;
		u1 = rand01();
		u2 = rand01();
		double r = sqrt(-2 * log(u1));
		double theta = 2 * M_PI * u2;
		return *sigma * r * cos(theta);
	}

	// The following two function returns, respectively, the Gaussian cumulative distribution function,
	// And the inverse gaussian cumulative distribution function.
	double normalCDF(double x, double sigma) {
		return 0.5 * (1 + erf( x * M_SQRT1_2));
	}
	// Inspired by https://gist.github.com/kmpm/1211922/6b7fcd0155b23c3dc71e6f4969f2c48785371292
	double inverseNormalCDF(double p, double sigma){
		if (p <= 0 || p >= 1) return sign(p)*6;

		double mu = 0;
		double r, val;
		double q = p - 0.5;

		if (fabs(q) <= .425) {
			r = .180625 - q * q;
			val =
				q * (((((((r * 2509.0809287301226727 +
					33430.575583588128105) * r + 67265.770927008700853) * r +
					45921.953931549871457) * r + 13731.693765509461125) * r +
					1971.5909503065514427) * r + 133.14166789178437745) * r +
					3.387132872796366608)
				/ (((((((r * 5226.495278852854561 +
					28729.085735721942674) * r + 39307.89580009271061) * r +
					21213.794301586595867) * r + 5394.1960214247511077) * r +
					687.1870074920579083) * r + 42.313330701600911252) * r + 1);
		}
		else {
			if (q > 0) {
				r = 1 - p;
			}
			else {
				r = p;
			}

			r = sqrt(-log(r));

			if (r <= 5) 
			{
				r += -1.6;
				val = (((((((r * 7.7454501427834140764e-4 +
					.0227238449892691845833) * r + .24178072517745061177) *
					r + 1.27045825245236838258) * r +
					3.64784832476320460504) * r + 5.7694972214606914055) *
					r + 4.6303378461565452959) * r +
					1.42343711074968357734)
					/ (((((((r *
						1.05075007164441684324e-9 + 5.475938084995344946e-4) *
						r + .0151986665636164571966) * r +
						.14810397642748007459) * r + .68976733498510000455) *
						r + 1.6763848301838038494) * r +
						2.05319162663775882187) * r + 1);
			}
			else { /* very close to  0 or 1 */
				r += -5;
				val = (((((((r * 2.01033439929228813265e-7 +
					2.71155556874348757815e-5) * r +
					.0012426609473880784386) * r + .026532189526576123093) *
					r + .29656057182850489123) * r +
					1.7848265399172913358) * r + 5.4637849111641143699) *
					r + 6.6579046435011037772)
					/ (((((((r *
						2.04426310338993978564e-15 + 1.4215117583164458887e-7) *
						r + 1.8463183175100546818e-5) * r +
						7.868691311456132591e-4) * r + .0148753612908506148525)
						* r + .13692988092273580531) * r +
						.59983220655588793769) * r + 1);
			}

			if (q < 0.0) {
				val = -val;
			}
		}

		return mu + sigma * val;
	}


	//===========================================================================================
	//================= Functions for choosing the right crystal for reflections ================
	//===========================================================================================
	enum crystal_plane {Cu111, Cu200, Cu220, Cu311, Cu400, Cu331, Cu420, Cu440, Ge111, Ge220, Ge311,
				 Ge400, Ge331, Ge422, Ge511, Ge533, Ge711, Ge551, Si111, Si220, Si311, Si400, Si331, 
				Si422, Si333, Si511, Si440, Si711, Si551, Be10, Be100, Be102, Be103, Be110, Be112, Be200, 
				Be00_2, Be10_1, PG00_2,PG00_4,PG00_6, Fe110, HS111,HS222,HS111star,Di111,Di220, Di311, Di400, 
				Di331, Di422, Di333, Di511, Di440};

	// An array containing all the possible strings that will be accepted if given as an 
	// argument to the parameter plane_of_reflection 
	const char* crystal_planeStrings[] = {
		"Cu111", "Cu200", "Cu220", "Cu311", "Cu400", "Cu331", "Cu420", "Cu440", "Ge111",
		"Ge220", "Ge311", "Ge400", "Ge331", "Ge422", "Ge511", "Ge533", "Ge711", "Ge551", 
		"Si111", "Si220", "Si311", "Si400", "Si331", "Si422", "Si333", "Si511", "Si440", 
		"Si711", "Si551"," Be10", "Be100", "Be102", "Be103", "Be110", "Be112", "Be200", 
		"Be00_2", "Be10_1", "PG00_2","PG00_4","PG00_6", "Fe110", "HS111","HS222","HS111star",
		"Di111","Di220", "Di311", "Di400", "Di331", "Di422", "Di333", "Di511", "Di440"};

	// Function to convert a string to an enum value
	enum crystal_plane stringToEnum(const char* plane) {
		for (int i = 0; i < sizeof(crystal_planeStrings) / sizeof(crystal_planeStrings[0]); ++i) {
			if (strcmp(plane, crystal_planeStrings[i]) == 0) {
				return (enum crystal_plane)i;
			}
		}
	}
	/* TITLE Crystal table for perfect crystal bent monochromator
	Table copied from SIMRES, current url: https://github.com/saroun/simres
	Contents: dhkl, QML,sigmab,sigmaa,V0,A,thetaD,C2,poi
	dhkl ... Lattice spacing of crystal plane.
	QML = 4*PI*(F*dhkl/V0)**2 [ A^-1 cm^-1]
	sigmab ... bound-atom scattering cross-section [barn]
	sigmaa ... absorption for 1A neutrons [barn*A^-1]
	sigmai ... incoherent scattering cross-section [barn]
	V0 .... volume [A^3]/atom
	A  .... atomic number
	thetaD .... Debye temperature (K)
	C2 .... constant from the Freund's paper  [A^-2 eV^-1]
	poi .... Poisson elastic constant */


	double crystal_table[56][10] = {{ 2.087063,  0.23391E+00 ,7.485,  2.094,  0.55,	11.81,  63.54,  315,  12.00,  0.30000E+00},
								{ 1.80745 , 0.17544E+00  ,7.485,  2.094,  0.55,	11.81,  63.54,  315,  12.00,  0.30000E+00},
								{ 1.27806 , 0.87718E-01  ,7.485,  2.094,  0.55,	11.81,  63.54,  315,  12.00,  0.30000E+00},
								{ 1.089933,  0.63795E-01 ,7.485,  2.094,  0.55,	11.81,  63.54,  315,  12.00,  0.30000E+00},
								{ 0.903725,  0.43859E-01 ,7.485,  2.094,  0.55,	11.81,  63.54,  315,  12.00,  0.30000E+00},
								{ 0.829315,  0.36934E-01 ,7.485,  2.094,  0.55,	11.81,  63.54,  315,  12.00,  0.30000E+00},
								{ 0.808316,  0.35087E-01 ,7.485,  2.094,  0.55,	11.81,  63.54,  315,  12.00,  0.30000E+00},
								{ 0.63903 , 0.21930E-01  ,7.485,  2.094,  0.55,	11.81,  63.54,  315,  12.00,  0.30000E+00},
								{ 3.26665 , 0.87700E-01  ,8.42 , 1.216,  0.18,  22.63,  72.6,  290,  9.0,  0.15450E+00},
								{ 2.00041 , 0.65760E-01  ,8.42 , 1.216,  0.18,  22.63,  72.6,  290,  9.0,  0.30000E+00},
								{ 1.70595 , 0.23920E-01  ,8.42 , 1.216,  0.18,  22.63,  72.6,  290,  9.0,  0.15430E+00},
								{ 1.41450 , 0.32880E-01  ,8.42 , 1.216,  0.18,  22.63,  72.6,  290,  9.0,  0.27300E+00},
								{ 1.29803 , 0.13850E-01  ,8.42 , 1.216,  0.18,  22.63,  72.6,  290,  9.0,  0.15430E+00},
								{ 1.15493 , 0.21925E-01  ,8.42 , 1.216,  0.18,  22.63,  72.6,  290,  9.0,  0.27270E+00},
								{ 1.08888 , 0.97400E-02  ,8.42 , 1.216,  0.18,  22.63,  72.6,  290,  9.0,  0.27270E+00},
								{ 0.86284 , 0.61200E-02  ,8.42 , 1.216,  0.18,  22.63,  72.6,  290,  9.0,  0.27270E+00},
								{ 0.79228 , 0.51588E-02  ,8.42 , 1.216,  0.18,  22.63,  72.6,  290,  9.0,  0.27270E+00},
								{ 0.79228 , 0.51600E-02  ,8.42 , 1.216,  0.18,  22.63,  72.6,  290,  9.0,  0.27270E+00},
								{ 3.13536 , 0.25970E-01  ,2.18 , 0.0889,	0.0,  20.02,  28.09,  420,  6.36,  0.18080E+00},
								{ 1.92001 , 0.19480E-01  ,2.18 , 0.0889,	0.0,  20.02,  28.09,  420,  6.36,  0.30000E+00},
								{ 1.63739 , 0.70800E-02  ,2.18 , 0.0889,	0.0,  20.02,  28.09,  420,  6.36,  0.28000E+00},
								{ 1.35765 , 0.97400E-02  ,2.18 , 0.0889,	0.0,  20.02,  28.09,  420,  6.36,  0.28000E+00},
								{ 1.24587 , 0.41000E-02  ,2.18 , 0.0889,	0.0,  20.02,  28.09,  420,  6.36,  0.18080E+00},
								{ 1.10852 , 0.64930E-02  ,2.18 , 0.0889,	0.0,  20.02,  28.09,  420,  6.36,  0.28000E+00},
								{ 1.04512 , 0.28900E-02  ,2.18 , 0.0889,	0.0,  20.02,  28.09,  420,  6.36,  0.28000E+00},
								{ 1.04512 , 0.28900E-02  ,2.18 , 0.0889,	0.0,  20.02,  28.09,  420,  6.36,  0.28000E+00},
								{ 0.96000 , 0.48700E-02  ,2.18 , 0.0889,	0.0,  20.02,  28.09,  420,  6.36,  0.28000E+00},
								{ 0.76044 , 0.15277E-02  ,2.18 , 0.0889,	0.0,  20.02,  28.09,  420,  6.36,  0.28000E+00},
								{ 0.76044 , 0.15277E-02  ,2.18 , 0.0889,	0.0,  20.02,  28.09,  420,  6.36,  0.28000E+00},
								{ 1.97956 , 0.11361      ,7.62579,  0.00422655,	0.002,  8.10926,  9.012,  1100,  7.62,  0.30000E+00},
								{ 1.97956 , 0.11361      ,7.62579,  0.00422655,	0.002,  8.10926,  9.012,  1100,  7.62,  0.28000E+00},
								{ 1.32857 , 0.05117      ,7.62579,  0.00422655,	0.002,  8.10926,  9.012,  1100,  7.62,  0.28000E+00},
								{ 1.02290 , 0.091        ,7.62579,  0.00422655,	0.002,  8.10926,  9.012,  1100,  7.62,  0.28000E+00},
								{ 1.14290 , 0.15147      ,7.62579,  0.00422655,	0.002,  8.10926,  9.012,  1100,  7.62,  0.28000E+00},
								{ 0.96363 , 0.10768      ,7.62579,  0.00422655,	0.002,  8.10926,  9.012,  1100,  7.62,  0.28000E+00},
								{ 0.98978 , 0.0284       ,7.62579,  0.00422655,	0.002,  8.10926,  9.012,  1100,  7.62,  0.28000E+00},
								{ 1.79215 , 0.37245      ,7.62579,  0.00422655,	0.002,  8.10926,  9.012,  1100,  7.62,  0.30000E+00},
								{ 1.73285 , 0.26116      ,7.62579,  0.00422655,	0.002,  8.10926,  9.012,  1100,  7.62,  0.30000E+00},
								{ 3.35500 , 0.79500E+00  ,5.555,  0.0019,	0.0,  8.80,  12.01,  1050,  20.00,  0.30000E+00},
								{ 1.67750 , 0.18000E+00  ,5.555,  0.0019,	0.0,  8.80,  12.01,  1050,  20.00,  0.30000E+00},
								{ 1.11830 , 0.08833E+00  ,5.555,  0.0019,	0.0,  8.80,  12.01,  1050,  20.00,  0.30000E+00},
								{ 2.02660 , 0.34031E+00  ,11.43,  2.53,	0.4 , 11.75 , 55.85,  411,  10.67 , 0.30000E+00},
								{ 3.43500 , 0.11020E+00  ,1.79,  2.88,	0.55,  13.16,  48.0,  300,  12.00 , 0.30000E+00},
								{ 1.71750 , 0.13130E+00  ,1.79,  2.88,	0.55,  13.16,  48.0,  300,  12.00 , 0.30000E+00},
								{ 3.43500 , 0.55100E-01  ,1.79,  2.88,	0.55,  13.16,  48.0,  300,  12.00 , 0.30000E+00},
								{ 2.05929 , 0.36606      ,5.55449  ,0.00194444,	0.0,  5.67213,  12.01,  1860,  3.00,  0.30000E+00},
								{ 1.26105 , 0.27455      ,5.55449  ,0.00194444,	0.0,  5.67213,  12.01,  1860,  3.00,  0.30000E+00},
								{ 1.07543 , 0.09984      ,5.55449  ,0.00194444,	0.0,  5.67213,  12.01,  1860,  3.00,  0.30000E+00},
								{ 0.89170 , 0.13727      ,5.55449  ,0.00194444,	0.0,  5.67213,  12.01,  1860,  3.00,  0.30000E+00},
								{ 0.81828 , 0.0578       ,5.55449  ,0.00194444,	0.0,  5.67213,  12.01,  1860,  3.00,  0.30000E+00},
								{ 0.72807 , 0.09152      ,5.55449  ,0.00194444,	0.0,  5.67213,  12.01,  1860,  3.00,  0.30000E+00},
								{ 0.68643 , 0.04067      ,5.55449  ,0.00194444,	0.0,  5.67213,  12.01,  1860,  3.00,  0.30000E+00},
								{ 0.68643 , 0.04067      ,5.55449  ,0.00194444,	0.0,  5.67213,  12.01,  1860,  3.00,  0.30000E+00},
								{ 0.63053 , 0.06864      ,5.55449  ,0.00194444,	0.0,  5.67213,  12.01,  1860,  3.00,  0.30000E+00},
								{ 0.63053 , 0.06864      ,5.55449  ,0.00194444,	0.0,  5.67213,  12.01,  1860,  3.00,  0.30000E+00}
	};

	///////////////////////////////////////////////////////////////////////////
	/////////////// Testing function
	///////////////////////////////////////////////////////////////////////////
	void print_neutron_state(struct neutron_values* neutron){
		printf("Neutron state:\nki %g, %g, %g\ntau %g, %g, %g\nkf %g, %g, %g\nv %g, %g, %g\nr %g, %g, %g\nki size %g, tau size %g, kf size %g, v size %g\n\n", 
			neutron->ki[0], neutron->ki[1], neutron->ki[2],
			neutron->tau[0], neutron->tau[1], neutron->tau[2],
			neutron->kf[0], neutron->kf[1], neutron->kf[2],
			neutron->v[0], neutron->v[1], neutron->v[2],
			neutron->r[0], neutron->r[1], neutron->r[2],
			neutron->ki_size, neutron->tau_size, neutron->kf_size, neutron->v_size
			);
	}
	
	///////////////////////////////////////////////////////////////////////////
	/////////////// Calculations for absorption factor
	/////////////// Based on the cross sections from 
	/////////////// A. K. Freund in Nuclear Instruments and Methods 213 (1983) 495-501
	///////////////////////////////////////////////////////////////////////////
	
	// Integral needed for debye factor

	double calculate_phi_integral(double x){
	// Asymptotic approximation
		if (x > 5) return PI * PI / 6 - exp(-x)/(x+1);
	// Integate with Simpson/3. I dont know what this means
		double z = 1 + x/(exp(x)-1);
		double dx = x/100;
		double ksi;
		for (int i = 2; i <= 100; i++) {
			ksi = (i-1)*dx;
			switch (i%2){
				case 1:
					z = z + 4 * ksi/(exp(ksi)-1);
					break;
				case 0:
					z = z + 2 * ksi/(exp(ksi)-1);
					break;
			}
		}
		return z*dx/3;
	}

	///////////////////////////////////////////////////////////////////////////
	/////////////// Function for checking if the neutron is inside the
	/////////////// monochromator
	///////////////////////////////////////////////////////////////////////////
	int neutron_is_inside_monochromator(struct Monochromator_values* monochromator,
											struct neutron_values* neutron, int lamella){
		double t1, t2;
		if (neutron->TOR[lamella] <= 0) {
			return 0;}
		// Check that the neutron is not beyond 
		cylinder_intersect(&t1, &t2, neutron->r[0]-monochromator->radius_horizontal + monochromator->thickness/2,
		neutron->r[1],neutron->r[2],neutron->v[0],neutron->v[1],neutron->v[2],
		monochromator->radius_horizontal, monochromator->height);			
		if (t2 <= 0) {
			return 0;
		}

		// Check that the neutron is with the angle ranges
		double angle_on_inner_cylinder = PI - asin(neutron->r[2]/monochromator->radius_horizontal);
		if (monochromator-> max_angle + 0.0001 <= angle_on_inner_cylinder || angle_on_inner_cylinder <= monochromator->min_angle - 0.0001){
			return 0;
		} 
		
		return 1;
	}

	///////////////////////////////////////////////////////////////////////////
	/////////////// B0 and BT are values used for the Debye factor	
	//TODO: Find out where this algorithm comes from and cite it
	///////////////////////////////////////////////////////////////////////////
	void calculate_B0_and_BT(struct Monochromator_values *monochromator){
		double x;
		monochromator->B0 = 2872.556/monochromator->atomic_number
								/monochromator->debye_temperature;
		
		if (monochromator->temperature_mono>0.1) x = monochromator->debye_temperature/monochromator->temperature_mono;
		else x =monochromator->debye_temperature/0.1;
		double phi = calculate_phi_integral(x);

		monochromator->BT = 4 * monochromator->B0 * phi / square(x);
	}

	///////////////////////////////////////////////////////////////////////////
	/////////////// The kinematic reflectivity is calculated as in 
	/////////////// Zachariasen
	///////////////////////////////////////////////////////////////////////////
	double calculate_kinematic_reflectivity(struct Monochromator_values* monochromator, 
											struct neutron_values* neutron){
		double sine_of_bragg_angle = neutron->wavelength/2/monochromator->lattice_spacing;
		double cosine_of_bragg_angle = sqrt(1-square(sine_of_bragg_angle));
		double extinction_length =  monochromator->lattice_spacing/neutron->wavelength*sqrt(4*PI/monochromator->Maier_Leibnitz_reflectivity*100);
		// Kinenatic reflectivity = QML*DHKL*sin(theta_B)**2/PI/cos(theta_B) [m⁻1]
		double kinematic_reflectivity = monochromator->Maier_Leibnitz_reflectivity;
		kinematic_reflectivity *= monochromator->lattice_spacing;
		kinematic_reflectivity *= square(sine_of_bragg_angle);
		kinematic_reflectivity *= 1/PI/cosine_of_bragg_angle;
		kinematic_reflectivity *= monochromator->Debye_Waller_factor;
		// Primary extinction factor, using the approximation in G.E Bacon and R.D. Lowde, Acta Cryst. (1948). 1, 303
		kinematic_reflectivity *= tanh(monochromator->domain_thickness/extinction_length)/monochromator->domain_thickness*extinction_length;
		return kinematic_reflectivity;
	}

	///////////////////////////////////////////////////////////////////////////
	/////////////// The actual calculations for the att coefficient
	/////////////// See the citation for Freund higher up.
	///////////////////////////////////////////////////////////////////////////
	double calculate_attenuation_coefficient(struct Monochromator_values* monochromator,
										struct neutron_values* neutron){
		double E = square(neutron->v_size)*VS2E; // Neutron energy in meV
		// Get factor for single phonon cross section
		
		double Bernoulli_sequence[31] = {1,-0.5,0.166667,0,-0.033333,0,0.0238095,0,-0.033333,
										0,0.0757576,0,-0.253114,0,1.16667,0,-7.09216,0,54.9712,
										0,-529.124,0,6192.12,0,-86580.3,0,1.42551717e6,0,-2.7298231e7,
										0,6.01580874e8};
		double x;
		if (monochromator->temperature_mono - 0.1 <= 0){
			x = monochromator->debye_temperature/0.1;
		}
		else{
			x = monochromator->debye_temperature/monochromator->temperature_mono;
		} 
		double R, Ifact, Xn;
		if (x<6){
			R = 0;
			Ifact = 1;
			Xn = 1/x;
			//JS: TODO, R may converge quickly, then the loop could be terminated sooner than after 31 steps
			for (int i=0; i<30; i++){
				R += Bernoulli_sequence[i]*Xn/Ifact/(i + 2.5);
				Xn *= x;
				Ifact *= i + 1;
			}
		}
		else R = 3.3/sqrt(x*x*x*x*x*x*x);

	// Define boltzmann_constant in units of (meV/K)
		double boltzmann_constant = 0.08617333262;
		double DWMF =  1-exp(-(monochromator->B0+monochromator->BT)*monochromator->Constant_from_Freund_paper*E/1000); // Factor 1000 is to convert Freund constant to meV
	// Set the cross sections, as written in freunds paper
		monochromator->nuclear_capture_absorption = monochromator->incoherent_scattering_cross_section
													+monochromator->absorption_for_1AA_Neutrons*neutron->wavelength;

		monochromator->multiple_phonon_absorption = monochromator->bound_atom_scattering_cross_section
											*square(monochromator->atomic_number/(monochromator->atomic_number + 1))
											*DWMF;

		monochromator->single_phonon_absorption = 3*monochromator->bound_atom_scattering_cross_section/monochromator->atomic_number
											* sqrt(boltzmann_constant * monochromator->debye_temperature/E) * R;

		double attenuation_coefficient =  (monochromator->nuclear_capture_absorption
										+ monochromator->single_phonon_absorption
										+ monochromator->multiple_phonon_absorption)
										/monochromator->volume; // [10^-28m^2/10^-30m^3]
		attenuation_coefficient *= 100; // [m^-1]
		return attenuation_coefficient;
	}
	///////////////////////////////////////////////////////////////////////////
	/////////////// Function that retrieves local scattering vector G or tau.
	///////////////////////////////////////////////////////////////////////////
	void calculate_local_scattering_vector(struct Monochromator_values* monochromator,
											struct neutron_values* neutron, int direction, int* lamella){
		double tau_temp[3] = {monochromator->tau[0], monochromator->tau[1], monochromator->tau[2]};
		//TODO: Needs appropriate naming. GII Is not explanatory
		double GII = sqrt(square(monochromator->tau[0])
									+ square(monochromator->tau[2]));
		for (int i=0 ; i<3; i++) {
			tau_temp[i] += monochromator->lattice_spacing_gradient_field[i][0]*neutron->r[0]
									+monochromator->lattice_spacing_gradient_field[i][1]*neutron->r[1]
									+monochromator->lattice_spacing_gradient_field[i][2]*neutron->r[2];
		}
		
		double tau_size = sqrt(square(tau_temp[0]) + square(tau_temp[1]) + square(tau_temp[2]));
		
		// Add the angles of the mosaic block to the scattering vector
 		neutron->tau[0] = tau_temp[0] 
							+ tau_temp[2]*neutron->horiz_angle[*lamella]
							- monochromator->tau[1]*monochromator->tau[0]/GII * neutron->vert_angle[*lamella];
		neutron->tau[1] = tau_temp[1] + GII*neutron->vert_angle[*lamella];
		neutron->tau[2] = tau_temp[2] 
							- tau_temp[0]*neutron->horiz_angle[*lamella]
							- monochromator->tau[1]*monochromator->tau[2]/GII * neutron->vert_angle[*lamella];

		// Renormalize local scat vect
		double normalization_factor = tau_size
									/sqrt(square(neutron->tau[0]) + square(neutron->tau[1]) + square(neutron->tau[2]));
		
		neutron->tau[0] *= direction*normalization_factor;
		neutron->tau[1] *= direction*normalization_factor;
		neutron->tau[2] *= direction*normalization_factor;
	}
	///////////////////////////////////////////////////////////////////////////
	/////////////// Function that sets the neutron structs values at a point
	///////////////////////////////////////////////////////////////////////////
	void set_neutron_values(
		struct neutron_values* neutron,
		double x, double y, double z,
		double vx, double vy, double vz){
			neutron->r[0] = x;
			neutron->r[1] = y;
			neutron->r[2] = z;
			neutron->v[0] = vx;
			neutron->v[1] = vy;
			neutron->v[2] = vz;
			neutron->v_size = 0;
			neutron->ki_size = 0;
			neutron->tau_size = 0;
			neutron->kf_size = 0;
			for (int i =0; i<3; i++){ 
				neutron->ki[i] = neutron->v[i]*V2K;
				neutron->ki_size += square(neutron->ki[i]);
				neutron->v_size += square(neutron->v[i]);
			}        
			neutron->v_size = sqrt(neutron->v_size);
			neutron->ki_size = sqrt(neutron->ki_size);
			neutron->wavelength = 3956/neutron->v_size;// Wavelength in Angstrom.
	}
	///////////////////////////////////////////////////////////////////////////
	/////////////// Functions that find epsilon zero and beta.
	///////////////////////////////////////////////////////////////////////////
	void calculate_epszero_and_beta(struct Monochromator_values* monochromator, 
                                        struct neutron_values* neutron, int direction, int lamella){
        // Update the final wavevector, as well as the size of the reciprocal lattice vector
		neutron->tau_size = 0;
		neutron->kf_size = 0;
		for (int i=0; i<3; i++){
			neutron->kf[i] = neutron->ki[i] + neutron->tau[i];
			neutron->tau_size += square(neutron->tau[i]); 
			neutron->kf_size += square(neutron->kf[i]);
		}
		
		neutron->tau_size = sqrt(neutron->tau_size);
		neutron->kf_size = sqrt(neutron->kf_size);
		double a = 0;
        double b = 0;
		// a is the numerator for the angular deviation of the bragg angle.
		// a = (ki + tau_0 + tau*gamma)^2 - ki^2
		a = square(neutron->kf_size) - square(neutron->ki_size);
		// b is the angle between k_i and tau, muktiplied by the size of each.
		// b = tau*(ki + tau_0 + delta nabla tau * ki + k*gamma) But only the part that is along the 
		// direction of the mosaic angle, and therefore it becomes tau*k*cos(theta_b) in the paper.
		b = direction*neutron->tau_size*(neutron->kf[0]*monochromator->perp_to_tau[0]
                                +neutron->kf[1]*monochromator->perp_to_tau[1]
                                +neutron->kf[2]*monochromator->perp_to_tau[2]);
		
		// Calculate the angular deviation from the Bragg condition
		// eps_zero = 
		neutron->eps_zero[lamella] = -a/(2*b);
		// Calculate gradient of the angular deviation
		neutron->beta[lamella] = 0;
		
		for (int i = 0; i<3; i++){
			double z = 0;
			for (int j = 0; j<3; j++){
				z += direction*monochromator->lattice_spacing_gradient_field[i][j]*neutron->ki[j];
			}
			// TODO: This should also be possible to write as += neutron->kf[i]*z. Check
			neutron->beta[lamella] += (neutron->ki[i]+ monochromator->tau[i])*z;
		}
		neutron->beta[lamella] *= -1/b/neutron->ki_size;
		// These definitions of beta and eps_zero exactly correspond to eq.4 of NIMA paper
    }	
	///////////////////////////////////////////////////////////////////////////
	/////////////// Function that finds the probability 
	/////////////// that a neutron will reflect
	///////////////////////////////////////////////////////////////////////////

	void find_propability_of_reflection(struct Monochromator_values* mono, 
                                            struct neutron_values* neutron, int direction, int i){
        double kinematic_reflectivity = calculate_kinematic_reflectivity(mono, neutron);
		if (mono->type==bent){
			// P = 1 - exp(-Q/(beta)) 	
            neutron->probabilities[i] = 1 -  exp(-kinematic_reflectivity/fabs(neutron->beta[i]));
        }
        else if (mono->type==bent_mosaic){
			// P=1-e^[-Q/beta*(Phi[eps_0/eta + beta k delta/eta]  - Phi[eps_0/eta])]
			// arg1 = [eps_0/eta + beta k delta/eta]
			double arg1 = (neutron->eps_zero[i] + 
							neutron->beta[i]*neutron->v_size*neutron->path_length[i])/
							mono->mosaicity_horizontal;
			// arg2 = [eps_0/eta]
			double arg2 = neutron->eps_zero[i]/mono->mosaicity_horizontal;
			neutron->probabilities[i] = 1-exp(-kinematic_reflectivity/neutron->beta[i]*
										(normalCDF(arg1, 1) - normalCDF(arg2, 1)));
		}

    }	
	///////////////////////////////////////////////////////////////////////////
	/////////////// Function that finds at what time the neutron reflects
	///////////////////////////////////////////////////////////////////////////
    void find_time_of_reflection(struct Monochromator_values* mono, 
				 struct neutron_values* neutron, int direction, int i, _class_particle* _particle){
        if (mono->type==bent){
			// Note: This equation can also be solved precisely as a quadratic equation in Bragg's law.
            neutron->TOR[i] = -neutron->eps_zero[i]/(neutron->ki_size*neutron->beta[i]);
        }
        else if (mono->type==bent_mosaic){
			// TODO: Make kin refl a quantity for the neutron. it is used too often not to.
			double kinematic_reflectivity = calculate_kinematic_reflectivity(mono, neutron);
			// TOR = eta/k/beta * Phi^-1 [Phi(eps_0/eta) - beta/Q * ln(1-ksi*P(delta_n))] - eps_0/k/beta
			// arg1 = eps_0/eta
			double arg1 = neutron->eps_zero[i]/mono->mosaicity_horizontal;
			// log_result = ln(1-ksi*P(delta_n)) Done like this to ensure type safety
			double log_arg = 1-rand01()*neutron->probabilities[i];
			double log_result = (double) log((double) log_arg);
			// arg2 = beta/Q * ln(1-ksi*P(delta_n))
			double arg2 = neutron->beta[i]/kinematic_reflectivity*log_result;
			neutron->TOR[i] = inverseNormalCDF(normalCDF(arg1, 1) - arg2, 1);
			neutron->TOR[i] *= mono->mosaicity_horizontal;
			neutron->TOR[i] -= neutron->eps_zero[i];
			neutron->TOR[i] *= 1/neutron->beta[i]/neutron->ki_size;
        }
        neutron->TOR[i] *= neutron->ki_size/neutron->v_size;
    }
	///////////////////////////////////////////////////////////////////////////
	/////////////// Simple function to reflect a neutron
	/////////////////////////////////////////////////////////////////////////// 
	void reflect_neutron(struct neutron_values* neutron, double* vx, double* vy, double* vz){
		*vx = (neutron->ki[0] + neutron->tau[0]);
		*vy = (neutron->ki[1] + neutron->tau[1]);
		*vz = (neutron->ki[2] + neutron->tau[2]);
		// Renormalize the neutron as we are adding a reciprocal lattice vector with a changing lattice spacing across the crystal

		double v_size = sqrt(square(*vx) + square(*vy) + square(*vz));
		*vx *= neutron->ki_size/v_size*K2V;
		*vy *= neutron->ki_size/v_size*K2V;
		*vz *= neutron->ki_size/v_size*K2V;
	}
	///////////////////////////////////////////////////////////////////////////
	/////////////// Simple function to choose the random angle of the mosaic
	/////////////// block
	///////////////////////////////////////////////////////////////////////////
	void choose_mosaic_block_angle(struct Monochromator_values* mono, struct neutron_values* neutron,
				       int direction, int i, _class_particle* _particle){
		if (mono->type==bent_mosaic){
		         neutron->vert_angle[i] = random_normal_distribution(&mono->mosaicity_vertical, _particle);
		         neutron->horiz_angle[i] = random_normal_distribution(&mono->mosaicity_horizontal, _particle);
		} 
		else {
			neutron->vert_angle[i] = 0;
			neutron->horiz_angle[i] = 0;
		}
	}
	///////////////////////////////////////////////////////////////////////////
	/////////////// Function that scans the neutron path, and finds 
	/////////////// the times that the neutron will enter and exit 
	/////////////// each individual lamella.
	/////////////////////////////////////////////////////////////////////////// 
	void get_crossing_times_of_lamellas(struct Monochromator_values* monochromator, struct neutron_values* neutron,
							int direction, int lamella){
		double transposed_x;
		double inner_t0;
		double outer_t0;
		double inner_t1;
		double outer_t1;
		double entry_time, exit_time;
		transposed_x = neutron->r[0]-monochromator->radius_horizontal
							-(monochromator->lamellas)*monochromator->thickness/2
							- (monochromator->lamellas-1)*monochromator->gap_size/2
							+ monochromator->thickness * lamella + monochromator->gap_size * lamella;
		cylinder_intersect(&inner_t0,&inner_t1,
							transposed_x,neutron->r[1],neutron->r[2],
							neutron->v[0],neutron->v[1],neutron->v[2],
							monochromator->radius_horizontal,
							monochromator->height);
		cylinder_intersect(&outer_t0,&outer_t1,
							transposed_x + monochromator->thickness,neutron->r[1],neutron->r[2],
							neutron->v[0],neutron->v[1],neutron->v[2],
							monochromator->radius_horizontal,
							monochromator->height);
		if (direction == 1){
			neutron->entry_time[lamella] = inner_t1;
			neutron->exit_time[lamella] = outer_t1;
		} else {
			neutron->entry_time[lamella] = outer_t0;
			neutron->exit_time[lamella] = inner_t0;
		}
		neutron->entry_time[lamella] = neutron->entry_time[lamella]>0 ? neutron->entry_time[lamella] : 0; 
		neutron->path_length[lamella] = neutron->exit_time[lamella] - entry_time;
	}
	//===================================================================
	//===== FUNCTIONS TO MOVE NEUTRON IN MONOCHROMATOR COORDINATES ======
	//===================================================================
	void transport_neutron_to_lamella_coordinates(struct Monochromator_values* monochromator, struct neutron_values* neutron,
													int direction, int lamella){
 		neutron->r[0] += neutron->v[0]*neutron->entry_time[lamella];
		neutron->r[1] += neutron->v[1]*neutron->entry_time[lamella];
		neutron->r[2] += neutron->v[2]*neutron->entry_time[lamella];
		neutron->r[0] += monochromator->thickness * lamella + monochromator->gap_size * lamella
						-(monochromator->lamellas-1)*monochromator->thickness/2
							- (monochromator->lamellas-1)*monochromator->gap_size/2;
	}
	void transport_neutron_back_to_entry(struct Monochromator_values* monochromator, struct neutron_values* neutron,
													int direction, int lamella){
		neutron->r[0] -= neutron->v[0]*neutron->entry_time[lamella];
		neutron->r[1] -= neutron->v[1]*neutron->entry_time[lamella];
		neutron->r[2] -= neutron->v[2]*neutron->entry_time[lamella];
		neutron->r[0] -= monochromator->thickness * lamella + monochromator->gap_size * lamella
						 -(monochromator->lamellas-1)*monochromator->thickness/2
						- (monochromator->lamellas-1)*monochromator->gap_size/2;
	}
	void propagate_neutrons_to_point_of_reflection(struct neutron_values* neutron, int lamella){
		neutron->r[0] += neutron->v[0]*neutron->TOR[lamella];
		neutron->r[1] += neutron->v[1]*neutron->TOR[lamella];
		neutron->r[2] += neutron->v[2]*neutron->TOR[lamella];
	}
	void propagate_neutrons_to_lamella_entry(struct neutron_values* neutron, int lamella){
		neutron->r[0] -= neutron->v[0]*neutron->TOR[lamella];
		neutron->r[1] -= neutron->v[1]*neutron->TOR[lamella];
		neutron->r[2] -= neutron->v[2]*neutron->TOR[lamella];
	}
	///////////////////////////////////////////////////////////////////////////
	/////////////// scan_lamellas calculates time of reflection,
	/////////////// entry/exit times, probability of reflection,
	/////////////// accumulating_propability of reflection, 
	/////////////// as well as the Bragg angle gradient for each lamella 
	///////////////////////////////////////////////////////////////////////////
	void scan_lamellas(struct Monochromator_values* monochromator, struct neutron_values* neutron,
			   int current_lamella, int direction, int neutron_just_reflected, _class_particle* _particle){
		double t0, inner_t1, outer_t1;
		double kinematic_reflectivity;
		double r[3] = {neutron->r[0], neutron->r[1], neutron->r[2]};
		int intersected;
		double transposed_x;
		double mos_temp;
		for (int i = current_lamella; i < monochromator->lamellas && i >= 0;){
			get_crossing_times_of_lamellas(monochromator, neutron, direction, i);
			transport_neutron_to_lamella_coordinates(monochromator, neutron, direction, i);
			choose_mosaic_block_angle(monochromator, neutron, direction, i, _particle);
			mos_temp = neutron->horiz_angle[i];
			neutron->horiz_angle[i] = 0;
			calculate_local_scattering_vector(monochromator, neutron, direction, &i);
			calculate_epszero_and_beta(monochromator, neutron, direction, i);
			neutron->horiz_angle[i] = mos_temp;
			find_propability_of_reflection(monochromator, neutron, direction, i);
			find_time_of_reflection(monochromator, neutron, direction, i, _particle);
			propagate_neutrons_to_point_of_reflection(neutron, i);
			// Assign probabilities in arrays 
			if (!neutron_is_inside_monochromator(monochromator, neutron, i) || 
				((i == current_lamella && neutron_just_reflected) && monochromator->type==bent)) {
				neutron->probabilities[i] = 0;
			}

			// Assigning accumulative probabilities.
			if (i == current_lamella){
				neutron->accu_probs[i] =  neutron->probabilities[i];
			} else {
				neutron->accu_probs[i] =  1 - (1-neutron->accu_probs[i-direction])*(1-neutron->probabilities[i]);
			}
			// Move neutron back to starting position 
			propagate_neutrons_to_lamella_entry(neutron, i);
			transport_neutron_back_to_entry(monochromator, neutron, i, direction);
			i += direction;
		}
	}
	
%}

DECLARE
%{
	double angle_range;
	double neutron_counter;
	double changed_neutrons_counter;
	double curvature;
	double Y[1024];
	double Y1[1024];

    struct neutron_values neutron;
	struct Monochromator_values monochromator;
%}

INITIALIZE
%{
	///////////////////////////////////////////////////////////////////////////
	/////////////// ERROR FUNCTIONS
	///////////////////////////////////////////////////////////////////////////
	if (radius_x <= 0)
		exit(printf("tau_approach_bent_perfect_crystal: %s: incorrect radius_x=%g\n", NAME_CURRENT_COMP, radius_x));
	if (xthickness <= 0)
		exit(printf("tau_approach_bent_perfect_crystal: %s: invalid monochromator xthickness=%g\n", NAME_CURRENT_COMP, xthickness));
	if (zwidth <= 0)
		exit(printf("tau_approach_bent_perfect_crystal: %s: invalid monochromator zwidth=%g\n", NAME_CURRENT_COMP, zwidth));

	///////////////////////////////////////////////////////////////////////////
	/////////////// INITIALIZING PARAMETERS
	///////////////////////////////////////////////////////////////////////////
	neutron_counter = 0;
	changed_neutrons_counter = 0;
	// Initialize angles of the monochromator 
	angle_range = zwidth/radius_x;
	monochromator.max_angle = angle_range/2 + PI;
	monochromator.min_angle = -angle_range/2 + PI; 
	// Figure out the type of monochromator
	if (radius_x) monochromator.type=bent;
	if (mosaicity) monochromator.type = mosaic;
	if (mosaicity && radius_x) monochromator.type = bent_mosaic;
	if (!radius_x && !mosaicity) monochromator.type = flat;
	// Read the designated plane of reflection, for use in the monochromator.
	enum crystal_plane plane = stringToEnum((const char *)&plane_of_reflection);
	// Set Monochromator values
	monochromator.length = zwidth; // [m]
	monochromator.height = yheight; // [m]
	monochromator.thickness = xthickness; // [m]
	monochromator.radius_horizontal = radius_x; // [m]
	monochromator.lamellas = lamella_slabs; // [#]
	monochromator.gap_size = lamella_gap_size; // [m]
	monochromator.domain_thickness = domainthickness; // []
	monochromator.temperature_mono = temperature; // [T]
	monochromator.lattice_spacing = crystal_table[plane][0]; // [A]
	monochromator.Maier_Leibnitz_reflectivity = crystal_table[plane][1]*100; // [A^-1 m^-1]
	monochromator.bound_atom_scattering_cross_section = crystal_table[plane][2]; // [barn]
	monochromator.absorption_for_1AA_Neutrons = crystal_table[plane][3];// [barn*A^-1]
	monochromator.incoherent_scattering_cross_section = crystal_table[plane][4];// [barn]
	monochromator.volume = crystal_table[plane][5]; // [A^-3]
	monochromator.atomic_number = crystal_table[plane][6]; // [#]
	monochromator.debye_temperature = crystal_table[plane][7]; // [K]
	monochromator.Constant_from_Freund_paper = crystal_table[plane][8]; //[A^-2 eV^-1]
	monochromator.poisson_ratio = crystal_table[plane][9]; // []
	calculate_B0_and_BT(&monochromator);
	monochromator.Debye_Waller_factor = exp(-(monochromator.B0 + monochromator.BT)/2/square(monochromator.lattice_spacing));
	//Set the mosaicity if relevant
	if (monochromator.type == mosaic || monochromator.type == bent_mosaic){
		//Input mosaicity is in arc min. Convert to Degrees and then to radians (And multiply with R8LN2 which I don't know what is)
		double R8LN2 = 2.354820045;
		monochromator.mosaicity_horizontal = mosaicity/60*DEG2RAD/R8LN2; 
		monochromator.mosaicity_vertical = monochromator.mosaicity_horizontal*mosaic_anisotropy;
	}
	// Initialize reciprocal lattice vector G or tau in some texts, and perp_to_tau. 

	angle_to_cut_horizontal *= DEG2RAD;
	angle_to_cut_vertical *= DEG2RAD;

	neutron.tau_size_zero = 2*PI/monochromator.lattice_spacing;

	monochromator.tau[0] = neutron.tau_size_zero*cos(angle_to_cut_horizontal)*cos(angle_to_cut_vertical);
	monochromator.tau[1] = neutron.tau_size_zero*sin(angle_to_cut_vertical);
	monochromator.tau[2] = neutron.tau_size_zero*sin(angle_to_cut_horizontal)*cos(angle_to_cut_vertical);
//JS: perp_to_tau is not perpendicular to tau ! But the code may not work for angle_to_cut_vertical<>0 anyway ... 
	monochromator.perp_to_tau[0] = sin(angle_to_cut_horizontal)*cos(angle_to_cut_vertical);
	monochromator.perp_to_tau[1] = sin(angle_to_cut_vertical);
	monochromator.perp_to_tau[2] = -cos(angle_to_cut_horizontal)*cos(angle_to_cut_vertical);

	// Initialize lattice_spacing_gradient_field 
	curvature = 1/radius_x;
	monochromator.lattice_spacing_gradient_field[0][0] = -monochromator.poisson_ratio*cos(angle_to_cut_horizontal)*neutron.tau_size_zero*curvature;
	monochromator.lattice_spacing_gradient_field[0][1] = 0;
	monochromator.lattice_spacing_gradient_field[0][2] = sin(angle_to_cut_horizontal)*neutron.tau_size_zero*curvature;
	monochromator.lattice_spacing_gradient_field[1][0] = 0;
	monochromator.lattice_spacing_gradient_field[1][1] = 0;
	monochromator.lattice_spacing_gradient_field[1][2] = 0;
	monochromator.lattice_spacing_gradient_field[2][0] = sin(angle_to_cut_horizontal)*neutron.tau_size_zero*curvature;
	monochromator.lattice_spacing_gradient_field[2][1] = 0;
	monochromator.lattice_spacing_gradient_field[2][2] = -cos(angle_to_cut_horizontal)*neutron.tau_size_zero*curvature;

	// Initialize neutron structs values 
	neutron.beta = (double*) calloc (lamella_slabs, sizeof(double));
	neutron.eps_zero = (double*) calloc (lamella_slabs, sizeof(double));
	neutron.vert_angle = (double*) calloc (lamella_slabs, sizeof(double));
	neutron.horiz_angle = (double*) calloc (lamella_slabs, sizeof(double));
	neutron.path_length = (double*) calloc (lamella_slabs, sizeof(double));
	neutron.entry_time = (double*) calloc (lamella_slabs, sizeof(double));
	neutron.exit_time = (double*) calloc (lamella_slabs, sizeof(double));
	neutron.probabilities = (double*) calloc (lamella_slabs, sizeof(double));
	neutron.accu_probs = (double*) calloc (lamella_slabs, sizeof(double));
	neutron.TOR = (double*) calloc (lamella_slabs, sizeof(double));
	
%}

TRACE
%{
	
	// Initialize variables for use in TRACE 
	int current_lamella = 0;
	double weight_init = p;
	if (weight_init == 0.0) ABSORB;

	double init_v_size;
	double cutoff_minimum_relative_weight = 1e-10;
	int neutron_is_in_crystal = 1;
	int neutron_just_reflected = 0;
	double reflect_condition;
	int start_lamella;
	double intersected;
	double intersect_time_1 = 0; 
	double intersect_time_2 = 0;
	int direction = 1;
	double attenuation_coefficient;
	intersected = cylinder_intersect(&intersect_time_1,
									&intersect_time_2,
									x-radius_x - lamella_slabs*xthickness/2 - (lamella_slabs-1)*lamella_gap_size/2,
									y,z,
									vx,vy,vz,
									radius_x, yheight);
	// If the neutron has either intersected not at all, the top, or the bottom, ABSORB the neutron. 
	if (intersected == 0 || ((int)intersected)%8 == 2 || ((int)intersected)%8 == 4 ) {
		ABSORB;
	}
	// If the neutron is not within the angle width of the monochromator now, ABSORB it. 
	PROP_DT(intersect_time_2);
	double angle_on_inner_cylinder = PI - asin(z/monochromator.radius_horizontal);
	if (monochromator.max_angle + 0.0001 <= angle_on_inner_cylinder || angle_on_inner_cylinder <= monochromator.min_angle - 0.0001){
		ABSORB;
	} 

	neutron.path = 0;
	// This is an infinite loop that the neutrons should break out of. 
	
	while (neutron_is_in_crystal){
		start_lamella = current_lamella;
		// We set the neutron values for the neutron, to reduce overhead in the share functions 
		set_neutron_values(&neutron, x,y,z,vx,vy,vz);
		// TODO: Remove init_v_size. It was a debugging variable
		if (!neutron_just_reflected) init_v_size = neutron.v_size;
		// scan_lamellas calculates time of reflection, entry/exit times, probability of reflection,
		//* accumulating_propability of reflection, as well as the Bragg angle gradient for each lamella 
		scan_lamellas(&monochromator, &neutron, current_lamella, direction, neutron_just_reflected, _particle);
		// Choose reflection condition depending on the whether the neutron is incoming or outgoing. 
		if (direction>0){
			reflect_condition = neutron.accu_probs[monochromator.lamellas - 1]*rand01();
		} else{
			reflect_condition = 1*rand01();
		}

		// Find the lamella the neutron reflects from, or the final lamella the neutron is in. 
		while(neutron.accu_probs[current_lamella]<= reflect_condition &&
				current_lamella < monochromator.lamellas && 
				current_lamella >= 0){
			current_lamella += direction;
		}
		// If the neutron has not reflected at all, allow it to pass
		if (current_lamella == -1 || current_lamella == monochromator.lamellas){
			neutron.accu_probs[current_lamella] = 1;
			break;
		}
		// Add the neutrons path through the lamellas it has not reflected from, to the total neutron path. 
		for (int i = start_lamella + direction; direction*(current_lamella - i) > 0; i += direction){
			neutron.path += neutron.path_length[i];
		}  
		// Allow neutron to break from the component if the relative weight is too low, or
		// if the neutron did not reflect on it's way out.
		if (p*neutron.accu_probs[current_lamella]/weight_init < cutoff_minimum_relative_weight
			|| (current_lamella == -1 && direction == -1)){
			break;
		} 

		// Add the distance to the point of reflection in the lamella, to the  neutron path, propagate to that point, 
		// reflect and modify the weight of the neutron.
		neutron.path += neutron.TOR[current_lamella];

		PROP_DT(neutron.TOR[current_lamella] + neutron.entry_time[current_lamella]);
		
		SCATTER;
		if (direction >0){
			p *= neutron.accu_probs[monochromator.lamellas-1];
		} 
		
		neutron.r[0] = x;
		neutron.r[1] = y;
		neutron.r[2] = z;
		double calculated_epsilon = neutron.eps_zero[current_lamella] + 
		 	neutron.beta[current_lamella]*neutron.TOR[current_lamella]*neutron.v_size;
		neutron.horiz_angle[current_lamella] = calculated_epsilon;
		calculate_local_scattering_vector(&monochromator, &neutron, direction, &current_lamella);
		reflect_neutron(&neutron, &vx, &vy, &vz);
		direction *= -1;
		neutron_just_reflected++;
	}
	// Attenuate the neutron with the remaining length through the crystal. 
	switch (direction){
		case (1):
			neutron.path += neutron.exit_time[monochromator.lamellas - 1];
			break;
		case (-1):
			neutron.path += neutron.exit_time[0];
			break;
	}
	attenuation_coefficient = calculate_attenuation_coefficient(&monochromator, &neutron);
	p *= exp(-attenuation_coefficient*neutron.path*neutron.v_size);
	if (neutron_just_reflected==1) {
		neutron_counter++; 
	}
	if (neutron_just_reflected>1) {
		changed_neutrons_counter++;
	}
	
%}

FINALLY
%{
	if (verbose)
	printf("\n1 reflection= %g, more reflections=%g\n", neutron_counter, changed_neutrons_counter);
%}


MCDISPLAY
%{
	// Draw the monochromator as two curving lines, between 10 points. 

	double x_inner [2];
	double x_outer [2];
	double y_top;
	double y_bottom;
	double z_inner [2];
	double z_outer [2];
	double i = 0;
	double inner_radii = radius_x + xthickness*lamella_slabs/2 + (lamella_slabs -1)*lamella_gap_size/2;
	double outer_radii = radius_x - xthickness*lamella_slabs/2 - (lamella_slabs -1)*lamella_gap_size/2;
	x_inner[1] = radius_x - cos(angle_range/2)*inner_radii;
	y_top = yheight/2;
	z_inner[1] = -sin(angle_range/2)*inner_radii;
	
	x_outer[1] = radius_x - cos(angle_range/2)*outer_radii;
	y_bottom = -yheight/2;
	z_outer[1] = -sin(angle_range/2)*outer_radii;
	
		 
	for (i = 0; i < 2; i = i + 0.2) {

		x_inner[0] = x_inner[1];

		z_inner[0] = z_inner[1];
		x_inner[1] = radius_x - cos(fabs(i-1)*angle_range/2)*inner_radii;
		z_inner[1] = -sin(angle_range/2)*radius_x + sin(angle_range/2)*inner_radii*i;

		x_outer[0] = x_outer[1];
		z_outer[0] = z_outer[1];
		
		x_outer[1] = radius_x - cos(fabs(i-1)*angle_range/2)*outer_radii;
		z_outer[1] = -sin(angle_range/2)*outer_radii + sin(angle_range/2)*outer_radii*i;
		
		multiline(5, 
				  x_inner[0], y_top, z_inner[0],
				  x_outer[0], y_top, z_outer[0],
				  x_outer[0], y_bottom, z_outer[0],
				  x_inner[0], y_bottom, z_inner[0],
				  x_inner[0], y_top, z_inner[0]);
				  
				  
		line(x_inner[0], y_top, z_inner[0],
		     x_inner[1], y_top, z_inner[1]);
		line(x_outer[0], y_top, z_outer[0],
		     x_outer[1], y_top, z_outer[1]);
		line(x_inner[0], y_bottom, z_inner[0],
		     x_inner[1], y_bottom, z_inner[1]);
		line(x_outer[0], y_bottom, z_outer[0],
		     x_outer[1], y_bottom, z_outer[1]);
		
	}
	multiline(5, 
		  x_inner[1], y_top, z_inner[1],
		  x_outer[1], y_top, z_outer[1],
		  x_outer[1], y_bottom, z_outer[1],
		  x_inner[1], y_bottom, z_inner[1],
		  x_inner[1], y_top, z_inner[1]);
	line(0,0,0,
		monochromator.perp_to_tau[0], monochromator.perp_to_tau[1], monochromator.perp_to_tau[2]); 
%}

END
