/*******************************************************************************
*         McStas instrument definition URL=http://www.mcstas.org
*
* Instrument: FLEX (after full upgrade)
*
* %Identification
* Written by: M. Skoulatos and K. Habicht, port to McStas 2.0 by Mathias Kure KU
* Date: 26 October 2010
* Origin: Helmholtz-Zentrum Berlin
* %INSTRUMENT_SITE: HZB
*
* Instrument short description:
* Primary and secondary spectrometer for the FLEX upgrade
*
* %Description
*
* %Example: kI=1.55 Detector: dpsd1_I=4.3e+06
*
* %Parameters
* kI: [inv AA]          incident wavevector
* kF: [inv AA]          outgoing wavevector
* wVS: [m]              Width of virtual source slit
* tilt: [deg]           Tilt angle for velocity selector
* SA: [1]               Analyzer scattering sense
* A3: [deg]             Sample omega angle
* A4: [deg]             Sample 2-theta angle
* L3: [m]
* L4: [m]
* Mono_flatswitch: [1]  Flag for flat or curved monochromator
*
* %End
*******************************************************************************/
DEFINE INSTRUMENT HZB_FLEX(kI=1.55, kF=1.55, wVS=0.03, tilt=0, SA=-1, A3=0, A4=70, L3=1.00, L4=1.00, int Mono_flatswitch = 0)

/* The DECLARE section allows us to declare variables or small      */
/* functions in C syntax. These may be used in the whole instrument. */
DECLARE
%{

// unit conversions
double m_n=1.674928e-27;     // in kg
double hbar=1.054572e-34;    // in J sec
double meV_to_SI=1.6022e-22; // conversion
double SI_to_meV=6.2414e21;

// guide coating properties
double MGUIDE=3.2;
double W_para = 0.0025;
double R0_para = 0.99;
double Qc_para = 0.0217;
double alpha_para=3.90;

// curved guide properties
double LGUIDE_1=18.65;
double LGUIDE_2=17.8;
double beta_guide_1, s_guide_1;
double beta_guide_2, s_guide_2;

// monochromator properties
double RMH, RMV, rho_MH, rho_MV;
double lambdaI, EI;

// analyser properties
// double RAH, RAV, rho_AH, rho_AV;
// rho_AV=1.00 for kf=0.952
// rho_AV=1.23 for kf=1.55
// rho_AV=1.40 for kf=2.62
// rho_AV=1.97 for kf=3.696
double RAH, rho_AV=1.23, RAV;
double lambdaF, EF;

// angles
double A1, A2, A5, A6;

// distances
double L1=1.75;
double L2=2.2; // This is an actual variable in the real experiment
// double L3=1.10;
// double L4=1.00;


//velocity selector frequency
double SelFreq;

// monitor/source properties
double dlambdaI,dkI,dEI,l_min,l_max,e_min,e_max;

%}

/* The INITIALIZE section is executed when the simulation starts     */
/* (C code). You may use them as component parameter values.         */
INITIALIZE
%{

int    SM=-1;      // monochromator scattering sense is always negative
int    SS=+1;
// int    SA=-1;
double DM = 3.355; // PG 002 monochromator d-spacing in inv AA
double DA = 3.355; // PG 002 analyser d-spacing in inv AA

/* -------calculate spectrometer angles and monochromator/analyser curvatures-------*/
// ----- monochromator
  A1 = SM*RAD2DEG*asin(PI/(DM*kI));
  A2 = 2*A1;

//  RMH = L2/sin(A1*DEG2RAD);
  RMH = (L1+L2)/(2*sin(A1*DEG2RAD)); //additional factor done by RTP
//  rho_MH=1/RMH;
if (Mono_flatswitch == 1) RMH = 0.0;




//  RMV = 2*L2*sin(A1*DEG2RAD);
  RMV = (L1+L2)*sin(A1*DEG2RAD); //Facotr intropduced by RTP
//  rho_MV=1/RMV;

  printf("monochromator angles are:\n A1=%7.3f, A2=%7.3f [deg]\n",A1,A2);
  printf("radius of curvature RMH=%7.3f [m]\nradius of curvature RMV=%7.3f [m] \n",RMH,RMV);

//  printf("curvature rhoMH=%7.3f [m^-1]\ncurvature rhoMV=%7.3f [m] \n",rho_MH,rho_MV);

// ----- sample
//  A3 = 0;
//  A4 = 70;

printf("sample angles are:\n A3=%7.3f, A4=%7.3f [deg]\n",A3,A4);

// ----- analyser
  A5 = SA*RAD2DEG*asin(PI/(DA*kF));
  A6 = 2*A5;
  RAH = (L3+L4)/(2*sin(A5*DEG2RAD)); //Additional factor multiplied by RTP
  RAV = SA*1/rho_AV;
//  RAV should be (L3+L4)*sin(A5*DEG2RAD) but above equation gives better focusing for these specific conditions of kf=1.55 and the divergence...
  printf("analyser angles are:\n A5=%7.3f, A6=%7.3f [deg]\n",A5,A6);
  printf("radius of curvature RAH=%7.3f [m]\nradius of curvature RAV=%7.3f [m] \n",RAH,RAV);

/* ------------------------------calculate EI, lambdaI and so on ------------------*/
  lambdaI=2*PI/kI;  // in AA
  EI=SI_to_meV*hbar*hbar*kI*kI*1e20/2/m_n;

  SelFreq = 3956.06/(lambdaI*(360/(19.7+(2.1*tilt)))*0.25);
//  empirical formulas for source emmitance to save time on simulations!
//  1st attempt: linear
//  dlambdaI=0.01*(7-lambdaI)*lambdaI;
//  2nd attempt, 1/x, better one:
//  dlambdaI=((0.082/(lambdaI+0.4))-0.002)*lambdaI;
//  3rd attempt, 6th order polynomial, even better (with enough parameters we can model an elephant):
 dlambdaI = (1.75004E-05*pow(lambdaI,6) - 5.13903E-04*pow(lambdaI,5) + 6.04404E-03*pow(lambdaI,4) - 3.66989E-02*pow(lambdaI,3) + 1.22399E-01*pow(lambdaI,2) - 2.19626E-01*lambdaI + 2.0E-01)*lambdaI;

  l_min=lambdaI-dlambdaI;
  l_max=lambdaI+dlambdaI;
  e_min=81.81/l_max/l_max;
  e_max=81.81/l_min/l_min;

  printf("velocity selector frequency is %7.3f [Hz]\n",SelFreq);
  printf("lambda=%7.3f [A]\nkI=%7.3f [A^-1] \nEI=%7.3f [meV]\n",lambdaI,kI,EI);
  printf("Maxwellian source forced to emit only between l_min=%7.3f and l_max=%7.3f [A] in order to save simulation time\n",l_min,l_max);
  printf("correspondingly, the energy monitors at sample position are detecting between %7.3f and %7.3f [meV]\n",e_min,e_max);


// guide properties
  beta_guide_1=LGUIDE_1/2800*180/PI;
  beta_guide_2=LGUIDE_2/2800*180/PI;
  s_guide_1 = 2800-sqrt(2800*2800-LGUIDE_1*LGUIDE_1);
  s_guide_2 = 2800-sqrt(2800*2800-LGUIDE_2*LGUIDE_2);
%}

/* Here comes the TRACE section, where the actual      */
/* instrument is defined as a sequence of components.  */
TRACE

/* The Arm() class component defines reference points and orientations  */
/* in 3D space. Every component instance must have a unique name. Here, */
/* Origin is used. This Arm() component is set to define the origin of  */
/* our global coordinate system (AT (0,0,0) ABSOLUTE). It may be used   */
/* for further RELATIVE reference, Other useful keywords are : ROTATED  */
/* EXTEND GROUP PREVIOUS. Also think about adding a neutron source !    */
/* Progress_bar is an Arm displaying simulation progress.               */

COMPONENT Origin = Progress_bar()
  AT (0,0,0) ABSOLUTE

// note: keep source target window with fixed width 0.20 cm seperate from entrance window

COMPONENT Gen_Source = Source_gen(
    radius = 0.0775, dist = 1.53, focus_xw = 0.2, focus_yh = 0.125,
    Lmin = l_min, Lmax = l_max, I1 = 1e10,
    T1 = 45)
  AT (0, 0, 0) RELATIVE Origin
  ROTATED (0, 0, 0) RELATIVE Origin

// START complete neutron guide system in front of curved guide

// assumption: new extraction (in-pile) part is not rotated and not shifted w.r.t. NL2old axis
COMPONENT NL1A_NL1B_NL2_NL3_InPile_Entrance_Window = Arm()
  AT (0, 0, 1.530) RELATIVE Origin ROTATED (0, 0, 0) RELATIVE Origin

// this is the extraction part - with 4 mm symmetrically increased width and height
COMPONENT NL1A_NL1B_NL2_NL3_InPile = Guide(
    w1 = 0.170696, h1 = 0.129, w2 = 0.340522, h2 = 0.129, l = 1.870,
    R0=R0_para, Qc=Qc_para, alpha=alpha_para, m=MGUIDE, W=W_para)
  AT (0, 0, 0) RELATIVE NL1A_NL1B_NL2_NL3_InPile_Entrance_Window ROTATED (0, 0, 0) RELATIVE NL1A_NL1B_NL2_NL3_InPile_Entrance_Window

// the NL1B_Straight1_Entrance_Window is shifted ans rotated according to T.Krists proposal 25.08.2008 - no gaps for simplicity
COMPONENT NL1B_Straight1_Entrance_Window = Arm()
  AT (-0.1277, 0, 3.405) RELATIVE Origin ROTATED (0, -2.15, 0) RELATIVE Origin

// in the following straight guide sections the gaps are neglected and so only a single straight guide is considered!
COMPONENT NL1B_Straight1 = Guide(
    w1 = 0.06, h1 = 0.125, w2 = 0.06, h2 = 0.125, l = 1.549,
    R0=R0_para, Qc=Qc_para, alpha=alpha_para, m=MGUIDE, W=W_para)
  AT (0, 0, 0) RELATIVE NL1B_Straight1_Entrance_Window ROTATED (0, 0, 0) RELATIVE NL1B_Straight1_Entrance_Window

COMPONENT NL1B_Curved_Entrance_Window = Arm()
  AT (0, 0, 1.549) RELATIVE NL1B_Straight1_Entrance_Window ROTATED (0, 0, 0) RELATIVE NL1B_Straight1_Entrance_Window

// END complete neutron guide system in front of curved guide
// START curved guide

COMPONENT NL1B_Curved_Guide_1 = Guide_curved(
    w1 = 0.06, h1 = 0.125, l = LGUIDE_1, R0 = R0_para, Qc = Qc_para,
    alpha = alpha_para, m = MGUIDE, W = W_para, curvature = 2800)
  AT (0, 0, 0) RELATIVE NL1B_Curved_Entrance_Window ROTATED (0, 0, 180) RELATIVE NL1B_Curved_Entrance_Window

//following arm translates and rotates to allow for the 18.65m guide. It also rotates by an extra 90 deg around z, to allow for a 12 o'clock velocity selector window.

COMPONENT NL1B_Velocity_Selector_Gap_Entrance_Window = Arm()
  AT (-s_guide_1, 0, LGUIDE_1) RELATIVE NL1B_Curved_Entrance_Window ROTATED (0, -beta_guide_1, 0) RELATIVE NL1B_Curved_Entrance_Window

/************************************** 12h selector **************************************/

COMPONENT Before_selec = PSD_monitor(
    nx = 50, ny = 50, restore_neutron = 1, xwidth = 0.20,
    yheight = 0.20, filename = "PSD_monitor_before.dat")
  AT (0, 0, 0.19) RELATIVE NL1B_Velocity_Selector_Gap_Entrance_Window

COMPONENT SELEC = Selector(
xmin=-0.0625,
xmax=0.0625,
ymin=-0.03,
ymax=0.03,
length=0.25,
nslit =72,
d=0.0004,
radius=0.123,
alpha=19.7,
nu=SelFreq
)

  AT (0,0,0.2) RELATIVE NL1B_Velocity_Selector_Gap_Entrance_Window
  ROTATED (0,tilt,90)    RELATIVE NL1B_Velocity_Selector_Gap_Entrance_Window

COMPONENT After_selec = PSD_monitor(
    nx = 50, ny = 50, restore_neutron = 1, xwidth = 0.20,
    yheight = 0.20, filename = "PSD_monitor_after.dat")
  AT (0, 0, 0.46) RELATIVE NL1B_Velocity_Selector_Gap_Entrance_Window

COMPONENT NL1B_Curved_2_Entrance_Window = Arm()
  AT (0, 0, 0.47) RELATIVE NL1B_Velocity_Selector_Gap_Entrance_Window ROTATED (0, 0, 0) RELATIVE NL1B_Velocity_Selector_Gap_Entrance_Window

COMPONENT NL1B_Curved_Guide_2 = Guide_curved(
    w1 = 0.06, h1 = 0.125, l = LGUIDE_2, R0 = R0_para, Qc = Qc_para,
    alpha = alpha_para, m = MGUIDE, W = W_para, curvature = 2800)
  AT (0, 0, 0) RELATIVE NL1B_Curved_2_Entrance_Window ROTATED (0, 0, 180) RELATIVE NL1B_Curved_2_Entrance_Window

COMPONENT  NL1B_Straight2_Entrance_Window = Arm()
  AT (-s_guide_2, 0, LGUIDE_2) RELATIVE NL1B_Curved_2_Entrance_Window ROTATED (0,-beta_guide_2,0) RELATIVE NL1B_Curved_2_Entrance_Window

COMPONENT NL1B_Straight2 = Guide(
    w1 = 0.06, h1 = 0.125, w2 = 0.06, h2 = 0.125, l = 3.500,
    R0=R0_para, Qc=Qc_para, alpha=alpha_para, m=MGUIDE, W=W_para)
  AT (0, 0, 0) RELATIVE NL1B_Straight2_Entrance_Window ROTATED (0, 0, 0) RELATIVE NL1B_Straight2_Entrance_Window

// START elliptical guide section

COMPONENT NL1B_Elliptical_Entrance_Window = Arm()
//AT (0.0006, 0, 3.500) RELATIVE NL1B_Straight2_Entrance_Window ROTATED (0, 0.0114, 0.0458) RELATIVE NL1B_Straight2_Entrance_Window
  AT (0.0000, 0, 3.500) RELATIVE NL1B_Straight2_Entrance_Window ROTATED (0, 0.000,  0.00) RELATIVE NL1B_Straight2_Entrance_Window
//AT (0.0006, 0, 3.500) RELATIVE NL1B_Straight2_Entrance_Window ROTATED (0, 0.015, -0.06) RELATIVE NL1B_Straight2_Entrance_Window

COMPONENT elliptical_piece = Guide_tapering(
    option = "elliptical", segno = 800, w1 = 0.06, h1 = 0.125,
    l = 2.5, linw = 2.9, loutw = 0.4, R0 = 0.99, Qcx = 0.0217,
    Qcy = 0.0217, alphax = 4.00, alphay = alpha_para, W = 0.002,
    mx = 5.2, my = MGUIDE)
//AT (0.0006, 0, 0) RELATIVE NL1B_Elliptical_Entrance_Window ROTATED (0, 0.015, -0.06) RELATIVE NL1B_Elliptical_Entrance_Window
  AT (0, 0, 0) RELATIVE NL1B_Elliptical_Entrance_Window
    ROTATED (0, 0, 0) RELATIVE NL1B_Elliptical_Entrance_Window

// END elliptical guide section

// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
COMPONENT Virtual_source = Slit(
    xwidth = wVS, yheight = 0.2)
  AT (0, 0, 2.900) RELATIVE NL1B_Elliptical_Entrance_Window
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */

// START GUIDE before monochromator

COMPONENT NL1B_Vertical_Guide_Entrance_Window = Arm()
//AT (0.0017, 0, 2.925) RELATIVE NL1B_Elliptical_Entrance_Window ROTATED (0, 0.00388, 0.0458) RELATIVE NL1B_Elliptical_Entrance_Window
  AT (0.0000, 0, 2.925) RELATIVE NL1B_Elliptical_Entrance_Window ROTATED (0, 0.000, 0.00) RELATIVE NL1B_Elliptical_Entrance_Window
//AT (0.0017, 0, 2.925) RELATIVE NL1B_Elliptical_Entrance_Window ROTATED (0, 0.018, 0.06) RELATIVE NL1B_Elliptical_Entrance_Window

COMPONENT NL1B_Vertical_Guide = Guide_channeled(
    w1 = 0.03, h1 = 0.125, w2 = 0.15, h2 = 0.125, l = 1.475,
    R0 = R0_para, Qcx = Qc_para, Qcy = Qc_para,
    alphax = alpha_para, alphay = alpha_para, W = W_para, nslit = 1,
    mx = 0, my = MGUIDE)
//AT (0.0017, 0, 0) RELATIVE NL1B_Vertical_Guide_Entrance_Window ROTATED (0, 0.018, 0.06) RELATIVE NL1B_Vertical_Guide_Entrance_Window
  AT (0, 0, 0) RELATIVE NL1B_Vertical_Guide_Entrance_Window
    ROTATED (0, 0, 0) RELATIVE NL1B_Vertical_Guide_Entrance_Window

COMPONENT NL1B_Guide_Exit = Arm()
  AT (0, 0, 1.475) RELATIVE NL1B_Vertical_Guide_Entrance_Window ROTATED (0, 0, 0) RELATIVE NL1B_Vertical_Guide_Entrance_Window

// COMPONENT psd_endguide = PSD_monitor(
//     nx = 200, ny = 200, filename = "psd-endguide.dat",
//     restore_neutron = 1, xwidth = 0.17, yheight = 0.135)
//   AT (0, 0, 0) RELATIVE NL1B_Guide_Exit
//
COMPONENT energy_endguide = E_monitor(
    nE = 200, filename = "energy-endguide.dat", restore_neutron = 1,
    xwidth = 0.17, yheight = 0.135, Emin = e_min, Emax = e_max)
  AT (0, 0, 0) RELATIVE NL1B_Guide_Exit

// COMPONENT hdiv_endguide = Div1D_monitor(
//     ndiv = 200, filename = "hdiv-endguide.dat",
//     restore_neutron = 1, xwidth = 0.17, yheight = 0.135,
//     maxdiv = 3)
//   AT (0, 0, 0) RELATIVE NL1B_Guide_Exit
//
// COMPONENT div_endguide = Divergence_monitor(
//     nh = 200, nv = 200, filename = "div-endguide.dat",
//     restore_neutron = 1, xwidth = 0.17, yheight = 0.135,
//     h_maxdiv = 3, v_maxdiv = 3)
//   AT (0, 0, 0) RELATIVE NL1B_Guide_Exit

// START monochromator part

COMPONENT Mono_center = Arm()
  AT (0, 0, 0.25) RELATIVE NL1B_Guide_Exit ROTATED (0,A1,0) RELATIVE NL1B_Guide_Exit

// set monochromator transmission to zero
COMPONENT Monochromator = Monochromator_curved(
    zwidth = 0.02,
    yheight = 0.02,
    gap = 0.0005,
    NH = 15,
    NV = 7,
    mosaich = 40,
    mosaicv = 40,
    r0 = 1.0,
    t0 = 0.00001,
    RV = RMV,
    RH = RMH
    )
  AT (0, 0, 0) RELATIVE Mono_center ROTATED (0,0,0) RELATIVE Mono_center

COMPONENT Mono_sample_arm = Arm()
  AT (0,0,0) RELATIVE Mono_center ROTATED (0,A2,0) RELATIVE NL1B_Guide_Exit

// COMPONENT psd_mono = PSD_monitor(
//     nx = 200, ny = 200, filename = "psd-mono.dat",
//     restore_neutron = 1, xwidth = 0.2, yheight = 0.15)
//   AT (0, 0, 0.15) RELATIVE Mono_sample_arm
//
COMPONENT energy_mono = E_monitor(
    nE = 200, filename = "energy-mono.dat", restore_neutron = 1,
    xwidth = 0.2, yheight = 0.125, Emin = e_min, Emax = e_max)
  AT (0, 0, 0.15) RELATIVE Mono_sample_arm
//
// COMPONENT hdiv_mono = Div1D_monitor(
//     ndiv = 200, filename = "hdiv-mono.dat",
//     restore_neutron = 1, xwidth = 0.2, yheight = 0.125,
//     maxdiv = 4)
//   AT (0, 0, 0.15) RELATIVE Mono_sample_arm
//
// COMPONENT div_mono = Divergence_monitor(
//     nh = 200, nv = 200, filename = "div-mono.dat",
//     restore_neutron = 1, xwidth = 0.2, yheight = 0.125,
//     h_maxdiv = 4, v_maxdiv = 4)
//   AT (0, 0, 0.15) RELATIVE Mono_sample_arm
//
COMPONENT energy_pre_sample = E_monitor(
    nE = 50, filename = "energy-pre-sample.dat", restore_neutron = 1,
    xwidth = 0.08, yheight = 0.08, Emin = e_min, Emax = e_max)
  AT (0, 0, L2-0.1) RELATIVE Mono_sample_arm


// END monochromator part


// Secondary spectrometer --->
//----------------
// start sample part

COMPONENT Sample_center = Arm()
  AT (0, 0, L2) RELATIVE Mono_sample_arm ROTATED (0,A3,0) RELATIVE Mono_sample_arm



COMPONENT Sample_analyser_arm = Arm()
  AT (0,0,0) RELATIVE Sample_center ROTATED (0,A4,0) RELATIVE Mono_sample_arm

COMPONENT div_mono = Divergence_monitor(
     nh = 20, nv = 20, filename = "div-sample.dat",
     restore_neutron = 1, xwidth = 0.1, yheight = 0.1,
     maxdiv_h = 4, maxdiv_v = 4)
   AT (0, 0, L2-0.05) RELATIVE Mono_sample_arm

COMPONENT div_mono_H = Div1D_monitor(
     ndiv = 35, filename = "H-div-sample.dat",
     restore_neutron = 1, xwidth = 0.005, yheight = 0.7,
     maxdiv = 4)
   AT (0, 0, L2-0.04) RELATIVE Mono_sample_arm

COMPONENT psd_sam = PSD_monitor(
     nx = 60, ny = 60, filename = "psd-sam.dat",
     restore_neutron = 1, xwidth = 0.20, yheight = 0.20)
     AT (0, 0, 0) RELATIVE Sample_center ROTATED (0,0,0) RELATIVE Mono_sample_arm

COMPONENT dpsd1 = PSDlin_monitor(nbins=30, filename="linpsdsam.dat",
     xmin=-0.1, xmax=0.1, ymin=-0.1, ymax=0.1)
AT (0, 0, 0) RELATIVE Sample_center ROTATED (0,0,0) RELATIVE Mono_sample_arm


/* This section is executed when the simulation ends (C code). Other    */
/* optional sections are : SAVE                                         */
FINALLY
%{
%}
/* The END token marks the instrument definition end */
END
