/*******************************************************************************
* McXtrace, x-ray tracing package
*         Copyright, All rights reserved
*         DTU Physics, Kgs. Lyngby, Denmark
*         Synchrotron SOLEIL, Saint-Aubin, France
*
* Component: SAXSNanodiscsWithTags
*
* %Identification
* Written by: Martin Cramer Pedersen (mcpe@nbi.dk)
* Date: May 2, 2012
* Origin: KU-Science
*
* A sample of monodisperse phospholipid bilayer nanodiscs in solution (water) - with
* histidine tag still on the belt proteins.
*
* %Description
* A component simulating the scattering from a box-shaped, thin solution (water)
* of monodisperse phospholipid bilayer nanodiscs - with histidine tags still
* on the belt proteins.
*
* Example: SAXSNanodiscsWithTags( 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.
* RadiusOfGyrationForHisTag: [AA] Radius of gyration for the his-tag.
* 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.
* VolumeOfOneHisTag: [AA^3] Volume of one his-tag.
* 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).
*
* %End
*******************************************************************************/

DEFINE COMPONENT SAXSNanodiscsWithTags



SETTING PARAMETERS (AxisRatio = 1.4, NumberOfLipids = 130.0, AreaPerLipidHeadgroup = 65.0, HeightOfMSP = 24.0, RadiusOfGyrationForHisTag = 10.0,
    VolumeOfOneMSP = 26296.5, VolumeOfHeadgroup = 319.0, VolumeOfCH2Tail = 818.8, VolumeOfCH3Tail = 108.6, VolumeOfOneHisTag = 2987.3,
    ScatteringLengthOfOneMSP = 3.34E-9, ScatteringLengthOfHeadgroup = 4.62E-11, ScatteringLengthOfCH2Tail = 6.71E-11, ScatteringLengthOfCH3Tail = 5.08E-12, ScatteringLengthOfOneHisTag = 3.89E-10,
    Roughness = 3.5, Concentration = 0.01, AbsorptionCrosssection = 0.0,
    xwidth, yheight, zdepth, SampleToDetectorDistance, DetectorRadius)


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 for compution the intensity from a given liposome */
  double NDTFormfactorCylinder(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;
  }

  // Functions used to compute the characteristics of the histidine tags
  double NDTDebye(double q, double r)
  {
    // Variables needed in functions
    double ReturnValue;

    // Constants needed in function
    double Dummy1;
    double Dummy2;

    // Computation
    Dummy1 = (q * q) * (r * r);
    Dummy2 = 2 * ((exp(-Dummy1) + Dummy1 - 1) / (Dummy1 * Dummy1));

    ReturnValue = Dummy2;

    return ReturnValue;
  }


  double NDTXiRodWithoutEndcaps(double q, double r, double l, double Alpha)
  {
    // Variables needed in function
    double ReturnValue;

    // Computation
    ReturnValue = gsl_sf_bessel_J0(q * r * sin(Alpha)) * sin(q * l * cos(Alpha) / 2.f) / (q * l * cos(Alpha) / 2.f);

    return ReturnValue;
  }

  double NDTPsiHammouda(double x)
  {
    // Variables used in function
    double ReturnValue;

    // Computation
    ReturnValue = (1.0 - exp(-pow(x, 2))) / pow(x, 2);

    return ReturnValue;
  }

  /* Computation of the intensity from a given nanodisc */
  double NDT_IntensityOfEmptyNanodiscsWithTags(double q, double MajorSemiAxis, double MinorSemiAxis, double ThicknessOfBelt,
					       double HeightOfBelt, double HeightOfLipids, double HeightOfTails, double HeightOfCH3, double RadiusOfGyrationForHisTag, double VolumeOfOneHisTag,
					       double DeltaRhoBelt, double DeltaRhoHead, double DeltaRhoCH2Tail, double DeltaRhoCH3Tail, double DeltaRhoTag)
  {
    double Intensity;
    double IntensityPart;
    double IntensityDisc;

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

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

    double OuterMajorSemiAxis = MajorSemiAxis + ThicknessOfBelt;
    double OuterMinorSemiAxis = MinorSemiAxis + ThicknessOfBelt;
    double AverageRadiusOfBelt = sqrt(pow(OuterMajorSemiAxis, 2) + pow(OuterMinorSemiAxis, 2));

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

    double Autocorrelation;
    double Crosscorrelation;
    double Disccorrelation;
    double FormfactorOfTags;

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

    // Variables needed in integration over beta
    int j;
    int NumberOfStepsInBeta = 50;
    double Beta;
    double BetaMin = 0.0;
    double BetaMax = PI / 2.0;
    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 * NDTFormfactorCylinder(q, OuterMajorSemiAxis, OuterMinorSemiAxis, HeightOfBelt, Alpha, Beta) -
			    PI * HeightOfBelt * MajorSemiAxis * MinorSemiAxis * NDTFormfactorCylinder(q, MajorSemiAxis, MinorSemiAxis, HeightOfBelt, Alpha, Beta)) /
	  (PI * HeightOfBelt * OuterMajorSemiAxis * OuterMinorSemiAxis - PI * HeightOfBelt * MajorSemiAxis * MinorSemiAxis);

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

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

	FormfactorOfCH3Tail = NDTFormfactorCylinder(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
	IntensityDisc = pow(AmplitudeOfBelt + AmplitudeOfHeads + AmplitudeOfCH2Tail + AmplitudeOfCH3Tail, 2);

	// Add the histidine tags
	FormfactorOfTags = NDTXiRodWithoutEndcaps(q, AverageRadiusOfBelt + RadiusOfGyrationForHisTag, HeightOfBelt, Alpha);

	Crosscorrelation = 2.0 * pow(DeltaRhoTag * VolumeOfOneHisTag, 2) * pow(FormfactorOfTags, 2) * pow(NDTPsiHammouda(q * RadiusOfGyrationForHisTag), 2);

	Autocorrelation  = 2.0 * pow(DeltaRhoTag * VolumeOfOneHisTag, 2) * NDTDebye(q, RadiusOfGyrationForHisTag);

	Disccorrelation  = 4.0 * (AmplitudeOfBelt + AmplitudeOfHeads + AmplitudeOfCH2Tail + AmplitudeOfCH3Tail) *
	  DeltaRhoTag * VolumeOfOneHisTag * FormfactorOfTags * NDTPsiHammouda(q * RadiusOfGyrationForHisTag);

	IntensityPart = IntensityDisc + Crosscorrelation + Autocorrelation + Disccorrelation;

	// Return the final intensity
	Intensity += 2 / PI * sin(Alpha) * IntensityPart * AlphaStep * BetaStep;
      }
    }

    return Intensity;
  }
  %}

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

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

	double MajorSemiAxis;
	double MinorSemiAxis;
	double ThicknessOfBelt;

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

	double NumberDensity;
	double Absorption;
%}

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

	// 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;
	RhoTag     = ScatteringLengthOfOneHisTag / VolumeOfOneHisTag;

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

	// Geometric properties of different components
	const double 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;
%}

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;

	// 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));

		/* Compute scattering */
		Intensity = exp(- pow(q * Roughness, 2)) * NDT_IntensityOfEmptyNanodiscsWithTags(q, MajorSemiAxis, MinorSemiAxis, ThicknessOfBelt, HeightOfMSP, HeightOfLipids, HeightOfTails, HeightOfCH3,
			RadiusOfGyrationForHisTag, VolumeOfOneHisTag, DeltaRhoBelt, DeltaRhoHead, DeltaRhoCH2Tail, DeltaRhoCH3Tail, DeltaRhoTag);

		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
