/**************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright 1997-2006, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Component: Pol_Bfield
*
* %I
* Written by: Erik B Knudsen, Peter Christiansen and Peter Willendrup
* Date: July 2011
* Origin: RISOE
*
* Magnetic field component.
*
* %D
*
* Region with a definable magnetic field.
*
* The component is nestable if the concentric flag is set (the default). This means that it may have a
* construction like the following:
* // START MAGNETIC FIELD
* COMPONENT msf =
* Pol_Bfield(xwidth=0.08, yheight=0.08, zdepth=0.2, Bx=0, By=-0.678332e-4, Bz=0)
*      AT (0, 0, 0) RELATIVE armMSF
*
* // OTHER COMPONENTS INSIDE THE MAGNETIC FIELD CAN BE PLACED HERE
*
* // STOP MAGNETIC FIELD
* COMPONENT msf_stop = Pol_simpleBfield_stop(magnet_comp_stop=msf)
*      AT (0, 0, 0) RELATIVE armMSF
*
* Note that if you have objects within the magnetic field that extend outside it you may get
* wrong results, because propagation within the field will then possibly extend outside, e.g.
* when using a tabled field. The evaluated field will then use the nearest defined field point
* _also_ outside the defintion area. If these outside-points have a non-zero field precession will
* continue - even after the neutron has left the field region.
*
* In between the two component instances the propagation routine
* PROP_DT also handles spin propagation.
* The current algorithm used for spin propagation is:
* SimpleNumMagnetPrecession
* in pol-lib.
*
* Example: Pol_Bfield(xwidth=0.1, yheight=0.1, zdepth=0.2, Bx=0, By=1, Bz=0)
*          Pol_Bfield(xwidth=0.1, yheight=0.1, zdepth=0.2, field_type=1)
*
* Functions supplied by the system are (the number is the ID of the function to be given as the field_type parameter:
* 1. Constant magnetic field: Constant field (Bx,By,Bz) within the region
* 2. Rotating magnetic_field: Field is initially (0,By,0) but after a length of zdepth
*      has rotated to (By,0,0)
* 3. Majorana magnetic_field: Field is initially (Bx,By,0) with By<<Bx, then linearly transforms to
*      (-Bx,By,0) at z=zdepth.
* 4. MSF field:
* 5. RF field: A radio frequency field is modeled by an implcit use of a roating frame.
* 6. Gradient field: Similar to Majorana by without an x-component to the field. I.e. it (0,By,0) at z=0, and
*    becomes (0,-By,0) AT z=zdepth.
*
* If the concentric parameter is set to 1 the magnetic field is turned on by an
* instance of this component, and turned off by an instance of Pol_simpleBfield_stop.
* Anything in between is considered inside the field.
* If concentric is zero the field is considered a closed component - neutrons are propagated
* through the field region, and no other components are permitted inside the field.
*
* %P
* INPUT PARAMETERS:
*
* xwidth: [m]               Width of opening.
* yheight: [m]              Height of opening.
* zdepth: [m]               Length of field.
* radius: [m]               Radius of field if it is cylindrical or spherical.
* Bx: [T]                   Parameter used for x composant of field.
* By: [T]                   Parameter used for y composant of field.
* Bz: [T]                   Parameter used for z composant of field.
* field_type: []            ID of function describing the magnetic field. 1:constant field, 2: rotating fiel, 3: majorana type field, 4: MSF, 5: RF-field, 6: gradient field.
* concentric: [ ]           Allow components and/or other fields inside field. Requires a subsequent Pol_simpleBfield_stop component.
*
* %E
****************************************************************************/

DEFINE COMPONENT Pol_Bfield
DEFINITION PARAMETERS ()
SETTING PARAMETERS (int field_type=0, xwidth=0, yheight=0,zdepth=0, radius=0, Bx=0, By=0, Bz=0, concentric=1)
/* Neutron parameters: (x,y,z,vx,vy,vz,t,sx,sy,sz,p) */

SHARE
%{
  %include "pol-lib"

  double fmax(double, double);
  double fmin(double, double);
%}


DECLARE
%{
  /* Larmor frequency and scalar product threshold*/
  double Bprms[8];
  int shape;
%}

INITIALIZE
%{
  enum shapes {NONE=0, BOX, WINDOW, CYLINDER, SPHERE, ANY};
  enum field_functions ff=field_type;
  /* these default field functions are part of pol-lib */
  if (ff==constant){
    double *t=Bprms;
    t[0]=Bx;t[1]=By;t[2]=Bz;
  } else if (ff==rotating){
    double *t=Bprms;
    t[0]=By;t[1]=zdepth;
  } else if (ff==majorana){
    double *t=Bprms;
    t[0]=Bx; t[1]=By; t[2]=zdepth;
  } 

  if(xwidth && yheight && zdepth){
      shape=BOX;
  }else if (xwidth && yheight && !zdepth){
      shape=WINDOW;
  }else if(radius && yheight){
      shape=CYLINDER;
  }else if (radius) {
      shape=SPHERE;
  }else{
      shape=NONE;
  }

  if(shape == WINDOW && !concentric){
      printf("Warning (%s): Magnetic field has window shape and concentric==0 => Field volume == 0.\n",NAME_CURRENT_COMP);
  }
  if(shape == NONE) {
      printf("Warning (%s): Magnetic field has no geometry. Full Z=0-plane is used as boundary.\n",NAME_CURRENT_COMP);
  }

%}

TRACE
%{
    double t0,t1;
    int hit;
    enum shapes {NONE=0, BOX, WINDOW, CYLINDER, SPHERE, ANY};

    /*enter through whatever object we are*/
    switch (shape){
        case BOX:
            hit=box_intersect(&t0,&t1,x,y,z,vx,vy,vz,xwidth,yheight,zdepth);
            /*terminate neutrons which miss the component*/
            if(!hit) ABSORB;
            /*If we do hit - propagate to the start of the field unless the neutron is already there.*/
            if(t0>FLT_EPSILON) {
                PROP_DT(t0);
                t1-=t0;
            }else if (t0<-FLT_EPSILON && concentric){
                PROP_DT(t1);
            }
            break;
        case CYLINDER:
            hit=cylinder_intersect(&t0,&t1,x,y,z,vx,vy,vz,radius,yheight);
            /*terminate neutrons which miss the component*/
            if(!hit)ABSORB;
            /*If we do hit - propagate to the start of the field unless the neutron is already there.*/
            if(t0>FLT_EPSILON) {
                PROP_DT(t0);
                t1-=t0;
            }else if (t0<-FLT_EPSILON && concentric){
                PROP_DT(t1);
            }
            break;
        case WINDOW:
            PROP_Z0;
            if (2*x>xwidth || 2*x<-xwidth || 2*y>yheight || 2*y<-yheight){
                /*terminate neutrons which miss the component*/
                ABSORB;
            }
            break;
        default:
            PROP_Z0;
    }
    mcmagnet_push(_particle, field_type,&(ROT_A_CURRENT_COMP),&(POS_A_CURRENT_COMP),0,Bprms);
#ifdef MCDEBUG
    mcmagnet_print_stack();
#endif

    /*does the field "close/stop" itself*/
    if (!concentric){
        switch (shape){
            case BOX:
                PROP_DT(t1);
                break;
            case CYLINDER:
                PROP_DT(t1);
                break;
            case WINDOW:
                PROP_Z0;
                /*terminate neutrons which miss the exit window*/
                if (2*x>xwidth || 2*x<-xwidth || 2*y>yheight || 2*y<-yheight){
                    ABSORB;
                }
                break;
            default:
                PROP_Z0;
        }
        mcmagnet_pop(_particle);
    }
%}

MCDISPLAY
%{
    enum shapes {NONE=0, BOX, WINDOW, CYLINDER, SPHERE, ANY};
    switch (shape){
        case BOX:
            box(0,0,0,xwidth,yheight,zdepth);
            break;
        case CYLINDER:
            circle("xz",0, yheight/2.0,0,radius);
            circle("xz",0,-yheight/2.0,0,radius);
            line(-radius,-yheight/2.0,0,-radius,yheight/2.0,0);
            line( radius,-yheight/2.0,0, radius,yheight/2.0,0);
            line(0,-yheight/2.0,-radius,0,yheight/2.0,-radius);
            line(0,-yheight/2.0, radius,0,yheight/2.0, radius);
            break;
        case SPHERE:
            circle("xz",0,0,0,radius);
            circle("xy",0,0,0,radius);
            circle("yz",0,0,0,radius);
            break;
        case WINDOW:
            rectangle("xy",0,0,0,xwidth,yheight);
            break;
    }
%}

END
