/*******************************************************************************
* McStas, neutron ray-tracing package
*         Copyright 1997-2003, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Component: SANSCurve
*
* %I
* Written by: Søren Kynde (kynde@nbi.dk)
* Rewritten by: Martin Cramer Pedersen (mcpe@nbi.dk)
* Date: October 17, 2012
* Origin: KU-Science
*
* A component mimicking the scattering from a given I(q)-curve by using
* linear interpolation between the given points.
*
* %D
* A box-shaped component simulating the scattering from any given I(q)-curve.
* The component uses linear interpolation to generate the points necessary to
* compute the scattering of any given neutron.
*
* %P
* DeltaRho: [cm/AA^3]            Excess scattering length density of the particles.
* Volume: [AA^3]                 Volume of the particles.
* Concentration: [mM]            Concentration of sample.
* AbsorptionCrosssection: [1/m]  Absorption cross section of the sample.
* xwidth: [m]                    Dimension of component in the x-direction.
* yheight: [m]                   Dimension of component in the y-direction.
* zdepth: [m]                    Dimension of component in the z-direction.
* SampleToDetectorDistance: [m]  Distance from sample to detector (for focusing the scattered neutrons).
* DetectorRadius: [m]            Radius of the detector (for focusing the scattered neutrons).
* FileWithCurve: []		Datafile with the given I(q).
*
* %E
*******************************************************************************/

DEFINE COMPONENT SANSCurve

DEFINITION PARAMETERS ()

SETTING PARAMETERS (DeltaRho = 1.0e-14, Volume = 1000.0,
Concentration = 0.01, AbsorptionCrosssection = 0.0,
xwidth, yheight, zdepth,
SampleToDetectorDistance, DetectorRadius,
string FileWithCurve = "Curve.dat")

OUTPUT PARAMETERS ()

SHARE
%{
// Function used to determine the number of datapoints in the input file
int CountLines(FILE* File)
	{
		// Declarations
		double Dummy1;
		double Dummy2;
        char Line[256];
        int NumberOfDatapoints = 0;

		// I/O
        while (fgets(Line, sizeof(Line), File) != NULL) {

            if (sscanf(Line, "%lf %lf", &Dummy1, &Dummy2) == 2) {
                ++NumberOfDatapoints;
			}
        }

        return NumberOfDatapoints;
    }

	// Function used to extract the scattering profile from a given curve
    int LoadCurve(char Filename[], double** Q, double** I)
	{
		// Declarations
        FILE* File;

        int i = 0;
		int NumberOfDatapoints;

        char Line[256];

		double *IntensityArray;
		double *qArray;

		// Reading file
        if ((File = fopen(Filename, "r")) == 0) {
            printf("Cannot open file: %s...\n", Filename);
            exit(0);
        }

        NumberOfDatapoints = CountLines(File);

        qArray         = (double *) calloc(NumberOfDatapoints, sizeof(double));
        IntensityArray = (double *) calloc(NumberOfDatapoints, sizeof(double));

        rewind(File);

        while (i < NumberOfDatapoints && fgets(Line, sizeof(Line), File) != NULL) {
            if (sscanf(Line, "%lf %lf", &qArray[i], &IntensityArray[i]) == 2) {
                ++i;
            }
        }

        *I = IntensityArray;
        *Q = qArray;

        return NumberOfDatapoints;
    }
%}

DECLARE
%{
double Absorption;
double q;
double* Q;
double* I;
double ForwardScattering;
double Prefactor;
int NumberOfDatapoints;
double NumberDensity;
%}

INITIALIZE
%{
	// Rescale concentration into number of aggregates per m^3 times 10^-4
	NumberDensity = Concentration * 6.02214129e19;


	// Initializing curve from file
    NumberOfDatapoints = LoadCurve(FileWithCurve, &Q, &I);
    ForwardScattering = I[0];

	if (!xwidth || !yheight || !zdepth) {
		printf("%s: Sample has no volume, check parameters!\n", NAME_CURRENT_COMP);
	}

	Absorption = AbsorptionCrosssection;
	Prefactor = NumberDensity * pow(DeltaRho * Volume, 2);
%}

TRACE
%{
	// Declarations
	double t0;
	double t1;
	double l_full;
	double l;
	double l1;
	double Intensity;
	double Weight;
	double IntensityPart;
	double SolidAngle;
	double qx;
	double qy;
	double qz;
	double v;
	double dt;
	double vx_i;
	double vy_i;
	double vz_i;
	char Intersect = 0;
	double Slope;
	double Offset;
	int i;

	// Computation
	Intersect = box_intersect(&t0, &t1, x, y, z, vx, vy, vz, xwidth, yheight, zdepth);

	if (Intersect) {

		if (t0 < 0.0) {
			fprintf(stderr, "Neutron already inside sample %s - absorbing...\n", NAME_CURRENT_COMP);
			ABSORB;
    	}

		// Compute properties of neutron
		v = sqrt(pow(vx, 2) + pow(vy, 2) + pow(vz, 2));
		l_full = v * (t1 - t0);
		dt = rand01() * (t1 - t0) + t0;
		PROP_DT(dt);
	    l = v * (dt - t0);

		// Store properties of incoming neutron
		vx_i = vx;
		vy_i = vy;
		vz_i = vz;

		// Generate new direction of neutron
		randvec_target_circle(&vx, &vy, &vz, &SolidAngle, 0, 0, SampleToDetectorDistance, DetectorRadius);

		NORM(vx, vy, vz);

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

		// Compute q
		qx = V2K * (vx_i - vx);
		qy = V2K * (vy_i - vy);
		qz = V2K * (vz_i - vz);

		q = sqrt(pow(qx, 2) + pow(qy, 2) + pow(qz, 2));

		// Discard neutron, if q is out of range
		if ((q < Q[0]) || (q > Q[NumberOfDatapoints - 1])) {
		    ABSORB;
		}

		// Find the first value of q in the curve larger than that of the neutron
		i = 1;

		while (q > Q[i]) {
			++i;
		}

		// Do a linear interpolation
		l1 = v * t1;

		Slope = (I[i] - I[i - 1]) / (Q[i] - Q[i - 1]);
		Offset = I[i] - Slope * Q[i];

		Intensity = (Slope * q + Offset) / ForwardScattering;

		p *= l_full * SolidAngle / (4.0 * PI) * Prefactor * Intensity * exp(- Absorption * (l + l1) / v);

		SCATTER;
	}
%}

MCDISPLAY
%{
	box(0, 0, 0, xwidth, yheight, zdepth);
%}

END
