/*******************************************************************************
* McXtrace, x-ray tracing package
*         Copyright, All rights reserved
*         DTU Physics, Kgs. Lyngby, Denmark
*         Synchrotron SOLEIL, Saint-Aubin, France
*
* Component: SAXSNanodiscsFast
*
* %Identification
* Written by: Martin Cramer Pedersen (mcpe@nbi.dk)
* Date: May 2, 2012
* Origin: KU-Science
* Release: McXtrace 1.0
*
* A sample of monodisperse phospholipid bilayer nanodiscs in solution (water).
*
* %Description
* A component very similar to SAXSNanodiscs.comp - however, the scattering
* profile is only computed once, and linear interpolation is then used to
* simulate the instrument.
*
* Example: SAXSNanodiscsFast( xwidth = 0.01, yheight = 0.01, zdepth = 0.01, SampleToDetectorDistance = 0.48, DetectorRadius = 0.1 )
*
* %Parameters
* AxisRatio: []         Axis ratio of the bilayer patch.
* NumberOfLipids: []    Number of lipids per nanodisc.
* AreaPerLipidHeadgroup: [AA^2] Area per lipid headgroup - default is POPC.
* HeightOfMSP: [AA]     Height of the belt protein - default is MSP1D1.
* VolumeOfOneMSP: [AA^3]    Volume of one belt protein - default is MSP1D1.
* VolumeOfHeadgroup: [AA^3] Volume of one lipid headgroup - default is POPC.
* VolumeOfCH2Tail: [AA^3]   Volume of the CH2-chains of one lipid - default is POPC.
* VolumeOfCH3Tail: [AA^3]   Volume of the CH3-tails of one lipid - default is POPC.
* ScatteringLengthOfOneMSP: [cm]    Scattering length of one belt protein - default is MSP1D1.
* ScatteringLengthOfHeadgroup: [cm] Scattering length of one lipid headgroup - default is POPC.
* ScatteringLengthOfCH2Tail: [cm]   Scattering length of the CH2-chains of one lipid - default is POPC.
* ScatteringLengthOfCH3Tail: [cm]   Scattering length of the CH3-tails of one lipid - default is POPC.
* Roughness: []         Factor used to smear the interfaces of the nanodisc.
* 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 x-rays).
* DetectorRadius: [m]   Radius of the detector (for focusing the scattered x-rays).
* qMin: [AA^-1]         Lowest q-value, for which a point is generated in the scattering profile
* qMax: [AA^-1]         Highest q-value, for which a point is generated in the scattering profile
* NumberOfQBins: []     Number of points generated in inital scattering profile.
*
* %End
*******************************************************************************/

DEFINE COMPONENT SAXSNanodiscsFast



SETTING PARAMETERS (AxisRatio = 1.4, NumberOfLipids = 130.0, AreaPerLipidHeadgroup = 65.0, HeightOfMSP = 24.0, 
    VolumeOfOneMSP = 26296.5, VolumeOfHeadgroup = 319.0, VolumeOfCH2Tail = 818.8, VolumeOfCH3Tail = 108.6,
    ScatteringLengthOfOneMSP = 3.34E-9, ScatteringLengthOfHeadgroup = 4.62E-11, ScatteringLengthOfCH2Tail = 6.71E-11, ScatteringLengthOfCH3Tail = 5.08E-12,
    Roughness = 3.5, Concentration = 0.01, AbsorptionCrosssection = 0.0,
    xwidth, yheight, zdepth, SampleToDetectorDistance, DetectorRadius, qMin = 0.001, qMax = 1.0, int NumberOfQBins=200)

DEPENDENCY " @GSLFLAGS@ "
NOACC

/*X-ray Parameters (x, y, z, kx, ky, kz, phi, t, Ex, Ey, Ez, p)*/
SHARE
  %{
#include <gsl/gsl_sf_bessel.h>

  /* Functions used to compute the formfactor of a nanodisc */
  double NDF_FormfactorCylinder(double q, double MajorSemiAxis, double MinorSemiAxis, double Height, double Alpha, double Beta)
  {
    double ProjectedRadius = sqrt(pow(MajorSemiAxis * sin(Beta), 2) + pow(MinorSemiAxis * cos(Beta), 2));

    double Formfactor1 = gsl_sf_bessel_J1(q * ProjectedRadius * sin(Alpha)) / (q * ProjectedRadius * sin(Alpha));
    double Formfactor2 = sin(q * Height * cos(Alpha) / 2.0) / (q * Height * cos(Alpha) / 2.0);

    return 2 * Formfactor1 * Formfactor2;
  }

  double NDF_IntensityOfEmptyNanodiscs(double q, double MajorSemiAxis, double MinorSemiAxis, double ThicknessOfBelt,
				       double HeightOfBelt, double HeightOfLipids, double HeightOfTails, double HeightOfCH3,
				       double DeltaRhoBelt, double DeltaRhoHead, double DeltaRhoCH2Tail, double DeltaRhoCH3Tail)
  {
    // Declaration
    double Intensity;
    double IntensityPart;

    double AmplitudeOfBelt;
    double AmplitudeOfHeads;
    double AmplitudeOfCH2Tail;
    double AmplitudeOfCH3Tail;

    double FormfactorOfBelt;
    double FormfactorOfHeads;
    double FormfactorOfCH2Tail;
    double FormfactorOfCH3Tail;

    const double OuterMajorSemiAxis = MajorSemiAxis + ThicknessOfBelt;
    const double OuterMinorSemiAxis = MinorSemiAxis + ThicknessOfBelt;

    const double VolumeOfBelt    = PI * HeightOfBelt * OuterMajorSemiAxis * OuterMinorSemiAxis - PI * HeightOfBelt * MajorSemiAxis * MinorSemiAxis;
    const double VolumeOfHeads   = PI * HeightOfLipids * MajorSemiAxis * MinorSemiAxis         - PI * HeightOfTails * MajorSemiAxis * MinorSemiAxis;
    const double VolumeOfCH2Tail = PI * HeightOfTails * MajorSemiAxis * MinorSemiAxis          - PI * HeightOfCH3 * MajorSemiAxis * MinorSemiAxis;
    const double VolumeOfCH3Tail = PI * HeightOfCH3 * MajorSemiAxis * MinorSemiAxis;

    // Variables needed for integration over alpha
    int i;
    const int NumberOfStepsInAlpha = 50;
    double Alpha;
    const double AlphaMin = 0.0;
    const double AlphaMax = PI / 2.0;
    const double AlphaStep = (AlphaMax - AlphaMin) / (1.0 * NumberOfStepsInAlpha);

    // Variables needed in integration over beta
    int j;
    const int NumberOfStepsInBeta = 50;
    double Beta;
    const double BetaMin = 0.0;
    const double BetaMax = PI / 2.0;
    const double BetaStep = (BetaMax - BetaMin) / (1.0 * NumberOfStepsInBeta);

    // Computation
    Intensity = 0.0;

    for (i = 0; i < NumberOfStepsInAlpha; ++i) {
      Alpha = (i + 0.5) * AlphaStep;

      for (j = 0; j < NumberOfStepsInBeta; ++j) {
	Beta = (j + 0.5) * BetaStep;

	// Compute formfactors
	FormfactorOfBelt = (PI * HeightOfBelt * OuterMajorSemiAxis * OuterMinorSemiAxis * NDF_FormfactorCylinder(q, OuterMajorSemiAxis, OuterMinorSemiAxis, HeightOfBelt, Alpha, Beta) -
			    PI * HeightOfBelt * MajorSemiAxis * MinorSemiAxis * NDF_FormfactorCylinder(q, MajorSemiAxis, MinorSemiAxis, HeightOfBelt, Alpha, Beta)) /
	  (PI * HeightOfBelt * OuterMajorSemiAxis * OuterMinorSemiAxis - PI * HeightOfBelt * MajorSemiAxis * MinorSemiAxis);

	FormfactorOfHeads = (PI * HeightOfLipids * MajorSemiAxis * MinorSemiAxis * NDF_FormfactorCylinder(q, MajorSemiAxis, MinorSemiAxis, HeightOfLipids, Alpha, Beta) -
			     PI * HeightOfTails  * MajorSemiAxis * MinorSemiAxis * NDF_FormfactorCylinder(q, MajorSemiAxis, MinorSemiAxis, HeightOfTails , Alpha, Beta)) /
	  (PI * HeightOfLipids * MajorSemiAxis * MinorSemiAxis - PI * HeightOfTails * MajorSemiAxis * MinorSemiAxis);

	FormfactorOfCH2Tail = (PI * HeightOfTails * MajorSemiAxis * MinorSemiAxis * NDF_FormfactorCylinder(q, MajorSemiAxis, MinorSemiAxis, HeightOfTails, Alpha, Beta) -
			       PI * HeightOfCH3   * MajorSemiAxis * MinorSemiAxis * NDF_FormfactorCylinder(q, MajorSemiAxis, MinorSemiAxis, HeightOfCH3  , Alpha, Beta)) /
	  (PI * HeightOfTails * MajorSemiAxis * MinorSemiAxis - PI * HeightOfCH3 * MajorSemiAxis * MinorSemiAxis);

	FormfactorOfCH3Tail = NDF_FormfactorCylinder(q, MajorSemiAxis, MinorSemiAxis, HeightOfCH3, Alpha, Beta);
    
	// Compute amplitudes
	AmplitudeOfBelt    = DeltaRhoBelt * VolumeOfBelt * FormfactorOfBelt;
	AmplitudeOfHeads   = DeltaRhoHead * VolumeOfHeads * FormfactorOfHeads;
	AmplitudeOfCH2Tail = DeltaRhoCH2Tail * VolumeOfCH2Tail * FormfactorOfCH2Tail;
	AmplitudeOfCH3Tail = DeltaRhoCH3Tail * VolumeOfCH3Tail * FormfactorOfCH3Tail;

	// Perform integration
	IntensityPart = pow(AmplitudeOfBelt + AmplitudeOfHeads + AmplitudeOfCH2Tail + AmplitudeOfCH3Tail, 2);

	Intensity += 2.0 / PI * sin(Alpha) * IntensityPart * AlphaStep * BetaStep;
      }
    }

    return Intensity;
  }

  %}

DECLARE
%{
	// Declarations
	double RhoWater;
	double RhoBelt;
	double RhoHead;
	double RhoCH2Tail;
	double RhoCH3Tail;

	double DeltaRhoHead;
	double DeltaRhoBelt;
	double DeltaRhoCH2Tail;
	double DeltaRhoCH3Tail;

	double MajorSemiAxis; 
	double MinorSemiAxis;
	double ThicknessOfBelt;

	double HeightOfBelt;
	double HeightOfLipids; 
	double HeightOfTails; 
	double HeightOfCH3;

	double AreaOfLipids;
	double *qArray;
	double *IArray;

	double NumberDensity;
	double Absorption;
%}

INITIALIZE
%{
	const double ScatteringLengthOfWater = 2.82E-12;
	const double VolumeOfWater = 30.0;

	const double qStep = (qMax - qMin) / (1.0 * NumberOfQBins);
	int i;
	double qDummy;

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

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

	Absorption = AbsorptionCrosssection;

	// Scattering properties of different components
	RhoWater   = ScatteringLengthOfWater     / VolumeOfWater;
	RhoBelt    = ScatteringLengthOfOneMSP    / VolumeOfOneMSP;
	RhoHead    = ScatteringLengthOfHeadgroup / VolumeOfHeadgroup;
	RhoCH2Tail = ScatteringLengthOfCH2Tail   / VolumeOfCH2Tail;
	RhoCH3Tail = ScatteringLengthOfCH3Tail   / VolumeOfCH3Tail;

	DeltaRhoBelt	= RhoBelt    - RhoWater;
	DeltaRhoHead    = RhoHead    - RhoWater;
	DeltaRhoCH2Tail = RhoCH2Tail - RhoWater;
	DeltaRhoCH3Tail = RhoCH3Tail - RhoWater;

	// Geometric properties of different components
	AreaOfLipids = NumberOfLipids * AreaPerLipidHeadgroup / 2.0;

	MinorSemiAxis = sqrt(AreaOfLipids / (PI * AxisRatio));
	MajorSemiAxis = MinorSemiAxis * AxisRatio;

	HeightOfLipids = 2.0 * (VolumeOfHeadgroup + VolumeOfCH2Tail + VolumeOfCH3Tail) / AreaPerLipidHeadgroup;
	HeightOfTails  = 2.0 * (VolumeOfCH2Tail + VolumeOfCH3Tail) / AreaPerLipidHeadgroup;
	HeightOfCH3    = 2.0 * VolumeOfCH3Tail / AreaPerLipidHeadgroup;

	ThicknessOfBelt = sqrt(pow(MinorSemiAxis + MajorSemiAxis, 2) / 4.0 + 2.0 * VolumeOfOneMSP / (PI * HeightOfMSP)) - (MajorSemiAxis + MinorSemiAxis) / 2.0;

	// Compute scattering from nanodiscs in predecided points
	qArray = (double *) calloc(NumberOfQBins, sizeof(double));
	IArray = (double *) calloc(NumberOfQBins, sizeof(double));

	for (i = 0; i < NumberOfQBins; ++i) {
		qDummy = qMin + (i + 0.5) * qStep;

		qArray[i] = qDummy;
	 	IArray[i] = NDF_IntensityOfEmptyNanodiscs(qDummy, MajorSemiAxis, MinorSemiAxis, ThicknessOfBelt, HeightOfMSP, HeightOfLipids, 
						      HeightOfTails, HeightOfCH3, DeltaRhoBelt, DeltaRhoHead, DeltaRhoCH2Tail, DeltaRhoCH3Tail);
	}
%}

TRACE
%{
	// Declarations	
	double l0; 
	double l1; 
	double l_full;
	double l;
	double l_1;
	double Intensity;
	double Weight;
	double IntensityPart;
	double SolidAngle;
	double q;
        double qx;
	double qy;
	double qz;
	double k;
	double dl;
	double kx_i;
	double ky_i;
	double kz_i;
	char Intersect = 0;
	int i;
	double Slope;
	double Offset;

	// Computation
	Intersect = box_intersect(&l0, &l1, x, y, z, kx, ky, kz, xwidth, yheight, zdepth);

	if (Intersect) {

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

                // Compute properties of photon
		k = sqrt(pow(kx, 2) + pow(ky, 2) + pow(kz, 2));
		l_full = l1 - l0;
		dl = rand01() * (l1 - l0) + l0; 
		PROP_DL(dl);                  
		l = dl - l0;

		// Store properties of incoming photon
		kx_i = kx;
		ky_i = ky;
		kz_i = kz;

		// Generate new direction of photon
		randvec_target_circle(&kx, &ky, &kz, &SolidAngle, 0, 0, SampleToDetectorDistance, DetectorRadius);

		NORM(kx, ky, kz);

		kx *= k;
		ky *= k;
		kz *= k;

		// Compute q
		qx = kx_i - kx;
		qy = ky_i - ky;
		qz = kz_i - kz;

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

		// Discard photon, if q is out of range
		if ((q < qArray[0]) || (q > qArray[NumberOfQBins - 1])) {
		    ABSORB;
		}
   
		// Find the first value of q in the curve larger than that of the photon
		i = 1;

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

		// Do a linear interpolation
		Slope = (IArray[i] - IArray[i - 1]) / (qArray[i] - qArray[i - 1]);
		Offset = IArray[i] - Slope * qArray[i];

		Intensity = (Slope * q + Offset) * exp(- pow(q * Roughness, 2));

		p *= l_full * SolidAngle / (4.0 * PI) * NumberDensity * Intensity * exp(- Absorption * (l + l1));
		
		SCATTER;
	}
%}

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

END
