/*******************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright (C) 1997-2008, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Component: SasView_stickyhardsphere
*
* %Identification
* Written by: Jose Robledo
* Based on sasmodels from SasView
* Origin: FZJ / DTU / ESS DMSC
*
*
* SasView stickyhardsphere model component as sample description.
*
* %Description
*
* SasView_stickyhardsphere component, generated from stickyhardsphere.c in sasmodels.
*
* Example: 
*  SasView_stickyhardsphere(radius_effective, volfraction, perturb, stickiness, 
*     model_scale=1.0, model_abs=0.0, xwidth=0.01, yheight=0.01, zdepth=0.005, R=0, 
*     int target_index=1, target_x=0, target_y=0, target_z=1,
*     focus_xw=0.5, focus_yh=0.5, focus_aw=0, focus_ah=0, focus_r=0, 
*     pd_radius_effective=0.0)
*
* %Parameters
* INPUT PARAMETERS:
* radius_effective: [Ang] ([0, inf]) effective radius of hard sphere.
* volfraction: [] ([0, 0.74]) volume fraction of hard spheres.
* perturb: [] ([0.01, 0.1]) perturbation parameter, tau.
* stickiness: [] ([-inf, inf]) stickiness, epsilon.
* Optional parameters:
* model_abs: [ ] Absorption cross section density at 2200 m/s.
* model_scale: [ ] Global scale factor for scattering kernel. For systems without inter-particle interference, the form factors can be related to the scattering intensity by the particle volume fraction.
* xwidth: [m] ([-inf, inf]) Horiz. dimension of sample, as a width.
* yheight: [m] ([-inf, inf]) vert . dimension of sample, as a height for cylinder/box
* zdepth: [m] ([-inf, inf]) depth of sample
* R: [m] Outer radius of sample in (x,z) plane for cylinder/sphere.
* target_x: [m] relative focus target position.
* target_y: [m] relative focus target position.
* target_z: [m] relative focus target position.
* target_index: [ ] Relative index of component to focus at, e.g. next is +1.
* focus_xw: [m] horiz. dimension of a rectangular area.
* focus_yh: [m], vert. dimension of a rectangular area.
* focus_aw: [deg], horiz. angular dimension of a rectangular area.
* focus_ah: [deg], vert. angular dimension of a rectangular area.
* focus_r: [m] case of circular focusing, focusing radius.
* pd_radius_effective: [] (0,inf) defined as (dx/x), where x is de mean value and dx the standard devition of the variable
*
* %Link
* %End
*******************************************************************************/
DEFINE COMPONENT SasView_stickyhardsphere

SETTING PARAMETERS (
        radius_effective=50.0,
        volfraction=0.2,
        perturb=0.05,
        stickiness=0.2,
        model_scale=1.0,
        model_abs=0.0,
        xwidth=0.01,
        yheight=0.01,
        zdepth=0.005,
        R=0,
        target_x=0,
        target_y=0,
        target_z=1,
        int target_index=1,
        focus_xw=0.5,
        focus_yh=0.5,
        focus_aw=0,
        focus_ah=0,
        focus_r=0,
        pd_radius_effective=0.0)


SHARE %{
%include "sas_kernel_header.c"

/* BEGIN Required header for SASmodel stickyhardsphere */
#define HAS_Iq

#ifndef SAS_HAVE_stickyhardsphere
#define SAS_HAVE_stickyhardsphere

#line 1 "stickyhardsphere"
double Iq_stickyhardsphere(double q, double radius_effective, double volfraction, double perturb, double stickiness)
{
    double onemineps,eta;
    double sig,aa,etam1,etam1sq,qa,qb,qc,radic;
    double lam,lam2,test,mu,alpha,beta;
    double kk,k2,k3,ds,dc,aq1,aq2,aq3,aq,bq1,bq2,bq3,bq,sq;

    onemineps = 1.0-perturb;
    eta = volfraction/onemineps/onemineps/onemineps;

    sig = 2.0 * radius_effective;
    aa = sig/onemineps;
    etam1 = 1.0 - eta;
    etam1sq=etam1*etam1;
    //C
    //C  SOLVE QUADRATIC FOR LAMBDA
    //C
    qa = eta/6.0;
    qb = stickiness + eta/etam1;
    qc = (1.0 + eta/2.0)/etam1sq;
    radic = qb*qb - 2.0*qa*qc;
    if(radic<0) {
        //if(x>0.01 && x<0.015)
        //    Print "Lambda unphysical - both roots imaginary"
        //endif
        return(-1.0);
    }
    //C   KEEP THE SMALLER ROOT, THE LARGER ONE IS UNPHYSICAL
    radic = sqrt(radic);
    lam = (qb-radic)/qa;
    lam2 = (qb+radic)/qa;
    if(lam2<lam) {
        lam = lam2;
    }
    test = 1.0 + 2.0*eta;
    mu = lam*eta*etam1;
    if(mu>test) {
        //if(x>0.01 && x<0.015)
        // Print "Lambda unphysical mu>test"
        //endif
        return(-1.0);
    }
    alpha = (1.0 + 2.0*eta - mu)/etam1sq;
    beta = (mu - 3.0*eta)/(2.0*etam1sq);
    //C
    //C   CALCULATE THE STRUCTURE FACTOR
    //C
    kk = q*aa;
    k2 = kk*kk;
    k3 = kk*k2;
    SINCOS(kk,ds,dc);
    //ds = sin(kk);
    //dc = cos(kk);
    aq1 = ((ds - kk*dc)*alpha)/k3;
    aq2 = (beta*(1.0-dc))/k2;
    aq3 = (lam*ds)/(12.0*kk);
    aq = 1.0 + 12.0*eta*(aq1+aq2-aq3);
    //
    bq1 = alpha*(0.5/kk - ds/k2 + (1.0 - dc)/k3);
    bq2 = beta*(1.0/kk - ds/k2);
    bq3 = (lam/12.0)*((1.0 - dc)/kk);
    bq = 12.0*eta*(bq1+bq2-bq3);
    //
    sq = 1.0/(aq*aq +bq*bq);

    return(sq);
}

#endif // SAS_HAVE_stickyhardsphere



/* END Required header for SASmodel stickyhardsphere */
%}
    DECLARE
%{
  double shape;
  double my_a_v;
%}

INITIALIZE
%{
  shape = -1; /* -1:no shape, 0:cyl, 1:box, 2:sphere  */
  if (xwidth && yheight && zdepth)
    shape = 1;
  else if (R > 0 && yheight)
    shape = 0;
  else if (R > 0 && !yheight)
    shape = 2;
  if (shape < 0)
    exit (fprintf (stderr,
                   "SasView_model: %s: sample has invalid dimensions.\n"
                   "ERROR     Please check parameter values.\n",
                   NAME_CURRENT_COMP));

  /* now compute target coords if a component index is supplied */
  if (!target_index && !target_x && !target_y && !target_z)
    target_index = 1;
  if (target_index) {
    Coords ToTarget;
    ToTarget = coords_sub (POS_A_COMP_INDEX (INDEX_CURRENT_COMP + target_index), POS_A_CURRENT_COMP);
    ToTarget = rot_apply (ROT_A_CURRENT_COMP, ToTarget);
    coords_get (ToTarget, &target_x, &target_y, &target_z);
  }

  if (!(target_x || target_y || target_z)) {
    printf ("SasView_model: %s: The target is not defined. Using direct beam (Z-axis).\n", NAME_CURRENT_COMP);
    target_z = 1;
  }

  my_a_v = model_abs * 2200 * 100; /* Is not yet divided by v. 100: Convert barns -> fm^2 */
%}


TRACE
%{
  double t0, t1, v, l_full, l, l_1, dt, d_phi, my_s;
  double aim_x = 0, aim_y = 0, aim_z = 1, axis_x, axis_y, axis_z;
  double arg, tmp_vx, tmp_vy, tmp_vz, vout_x, vout_y, vout_z;
  double f, solid_angle, vx_i, vy_i, vz_i, q, qx, qy, qz;
  char intersect = 0;

  /* Intersection neutron trajectory / sample (sample surface) */
  if (shape == 0) {
    intersect = cylinder_intersect (&t0, &t1, x, y, z, vx, vy, vz, R, yheight);
  } else if (shape == 1) {
    intersect = box_intersect (&t0, &t1, x, y, z, vx, vy, vz, xwidth, yheight, zdepth);
  } else if (shape == 2) {
    intersect = sphere_intersect (&t0, &t1, x, y, z, vx, vy, vz, R);
  }
  if (intersect) {
    if (t0 < 0)
      ABSORB;

    /* Neutron enters at t=t0. */
    v = sqrt (vx * vx + vy * vy + vz * vz);
    l_full = v * (t1 - t0);          /* Length of full path through sample */
    dt = rand01 () * (t1 - t0) + t0; /* Time of scattering */
    PROP_DT (dt);                    /* Point of scattering */
    l = v * (dt - t0);               /* Penetration in sample */

    vx_i = vx;
    vy_i = vy;
    vz_i = vz;
    if ((target_x || target_y || target_z)) {
      aim_x = target_x - x; /* Vector pointing at target (anal./det.) */
      aim_y = target_y - y;
      aim_z = target_z - z;
    }
    if (focus_aw && focus_ah) {
      randvec_target_rect_angular (&vx, &vy, &vz, &solid_angle, aim_x, aim_y, aim_z, focus_aw, focus_ah, ROT_A_CURRENT_COMP);
    } else if (focus_xw && focus_yh) {
      randvec_target_rect (&vx, &vy, &vz, &solid_angle, aim_x, aim_y, aim_z, focus_xw, focus_yh, ROT_A_CURRENT_COMP);
    } else {
      randvec_target_circle (&vx, &vy, &vz, &solid_angle, aim_x, aim_y, aim_z, focus_r);
    }
    NORM (vx, vy, vz);
    vx *= v;
    vy *= v;
    vz *= v;
    qx = V2K * (vx_i - vx);
    qy = V2K * (vy_i - vy);
    qz = V2K * (vz_i - vz);
    q = sqrt (qx * qx + qy * qy + qz * qz);

    double trace_radius_effective = radius_effective;
    if (pd_radius_effective != 0.0) {
      trace_radius_effective = (randnorm () * pd_radius_effective + 1.0) * radius_effective;
    }

    // Sample dependent. Retrieved from SasView./////////////////////
    float Iq_out;
    Iq_out = 1;

    Iq_out = Iq_stickyhardsphere (q, trace_radius_effective, volfraction, perturb, stickiness);

    float vol;
    vol = 1;

    // Scale by 1.0E2 [SasView: 1/cm  ->   McStas: 1/m]
    Iq_out = model_scale * Iq_out / vol * 1.0E2;

    l_1 = v * t1;
    p *= l_full * solid_angle / (4 * PI) * Iq_out * exp (-my_a_v * (l + l_1) / v);
    SCATTER;
  }
%}

MCDISPLAY
%{

  if (shape == 0) { /* cylinder */
    circle ("xz", 0, yheight / 2.0, 0, R);
    circle ("xz", 0, -yheight / 2.0, 0, R);
    line (-R, -yheight / 2.0, 0, -R, +yheight / 2.0, 0);
    line (+R, -yheight / 2.0, 0, +R, +yheight / 2.0, 0);
    line (0, -yheight / 2.0, -R, 0, +yheight / 2.0, -R);
    line (0, -yheight / 2.0, +R, 0, +yheight / 2.0, +R);
  } else if (shape == 1) { /* box */
    double xmin = -0.5 * xwidth;
    double xmax = 0.5 * xwidth;
    double ymin = -0.5 * yheight;
    double ymax = 0.5 * yheight;
    double zmin = -0.5 * zdepth;
    double zmax = 0.5 * zdepth;
    multiline (5, xmin, ymin, zmin, xmax, ymin, zmin, xmax, ymax, zmin, xmin, ymax, zmin, xmin, ymin, zmin);
    multiline (5, xmin, ymin, zmax, xmax, ymin, zmax, xmax, ymax, zmax, xmin, ymax, zmax, xmin, ymin, zmax);
    line (xmin, ymin, zmin, xmin, ymin, zmax);
    line (xmax, ymin, zmin, xmax, ymin, zmax);
    line (xmin, ymax, zmin, xmin, ymax, zmax);
    line (xmax, ymax, zmin, xmax, ymax, zmax);
  } else if (shape == 2) { /* sphere */
    circle ("xy", 0, 0.0, 0, R);
    circle ("xz", 0, 0.0, 0, R);
    circle ("yz", 0, 0.0, 0, R);
  }
%}
END

