#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

/* ---------------------------------------------------------------------------------------------------
! The Monte-Carlo Simulation of the Stern-Gerlach Experiment (ANSI C Version)
!
! In quantum mechanics, the Stern.Gerlach experiment is a celebrated experiment (performed in 1922) 
! on the deflection of particles, often used to illustrate basic principles of quantum mechanics. 
! It can be used to demonstrate that electrons and atoms have intrinsically quantum properties, and 
! how measurement in quantum mechanics affects the system being measured.
! This is a basic simulation of an "ideal" Stern-Gerlach experiment to spatially 
! separate the electrons (spin-1/2) particles.
!
! Experimental set up
!
!             +--------------------------------------------------------------+
!             |                                                              |
!             | Ag     Slit    Magnetic field Region       Potographic Plate |
!             | Source                                                       |
!             |                                                              |
!             |        |      |        S         |         ||                |
!             |        |      |__________________|         ||                |
!             |  \|/   |                                   ||                |
!             | --O-------->                               ||                |
!             |  /|\   |       __________________          ||                |
!             |        |      |                  |         ||                |
!             |   T    |      |        N         |         ||                |
!             |               .                  .         .                 |
!             |               .<------- L ------>.<-- D -->.                 |
!             |                                                              |
!             +--------------------------------------------------------------+
!
! The program works on the command line, usage is:
!
!   spin [-f012] [-q01]  [default: -f1 and -q1]
!
!  Options:
!
!   Magnetic field gradient:
!   -f0  sets up a uniform     magnetic field along +z axis with     B > 0
!   -f1  sets up a non-uniform magnetic field along +z axis with dB/dz > 0  (default)
!   -f2  sets up a non-uniform modulated by a single Gaussian 
!        magnetic field along +z axis with dB/dz > 0
!
!   Quantum effect:
!   -q0  Excludes the quantum effect. Sz is not quantized. 
!        That is cos(theta) is randomly oriented
!   -q1  Includes the quantum effect. Sz is quantized. (default)
!        That is cos(theta) = +- 1/sqrt(3)
!
! See for details: http://www1.gantep.edu.tr/~bingul/seminar/spin/
!
! All versions   : http://www1.gantep.edu.tr/~bingul/seminar/spin/spin.f90  (Fortran 90)
!                  http://www1.gantep.edu.tr/~bingul/seminar/spin/spin.c    (ANSI C)
!                  http://www1.gantep.edu.tr/~bingul/seminar/spin/spin.C    (ROOT)
!
! Author         : Dr. Ahmet Bingul <Ahmet.Bingul (at) gantep.edu.tr>, <Ahmet.Bingul (at) cern.ch>
!
! Date           : April 2008
!
 --------------------------------------------------------------------------------------------------- */

#define  XMAX  0.05              // Slit width in x direction in m
#define  ZMAX  0.005             // Slit width in z direction in m
#define  kB    1.38E-23          // Bolzman constant in J/K
#define  PI    3.14159265359     // Number PI
#define  GS    2.0023            // Gyromagnetic ratio for the electron
#define  EoM   1.759E+11         // e/m ratio for the electron in C/kg
#define  HBAR  1.055E-34         // Plank constant in J.s
#define  L     1.000E+0          // Length   in m of the region containing magnetic filed along y axis
#define  D     0.100E+0          // Distance in m between the end of magnetic filed and Photograhic plate
#define  MAg   1.792E-25         // Mass of the silver atom kg
#define  DBZ   100.0             // Field gradient, dB/dz, in T/m


double Rnd(void);
double Maxwell_Boltzman(double, double);
double Field_Grad(int, double);
double sign(double);
void   Command_Line(int, char **, int *, int *);
void   usage(void);


int main(int argc, char **argv)
{
    const int N = 10000;             // Number of Silver, Ag(47), atoms
    double    T = 2000.0;            // Temperature in Kelvin
    double    sz, muz, theta;
    double    s,sm,x0,z,z0,v,a,dBdz;
    int       i, field, quantum;

    // Get the magnetic field type from command line
    Command_Line(argc, argv, &field, &quantum);

    if(!field && !quantum)  printf("spin: dB/dz = 0, Sz is not quantized.\n");
    if(!field &&  quantum)  printf("spin: dB/dz = 0, Sz is quantized.\n");
    if( field && !quantum)  printf("spin: dB/dz > 0, Sz is not quantized.\n");
    if( field &&  quantum)  printf("spin: dB/dz > 0, Sz is quantized.\n");

    sm    = HBAR*sqrt(3.0)/2.0;        // Magnitude of the spin vector
    srand(time(NULL));                 // initilize the random number generator

// Loop over Silver atoms
    for(i=0; i<N; i++)
    {
      // A random angle on the x-y plane in the range [0,2pi]
      //phi = 2.0*PI*Rnd();

      if(quantum){
         s = +1.0;
         if(Rnd() < 0.5) s = -1.0;
         theta = acos(1.0/sqrt(3.0)); // angle and Sz
         sz    = sm*cos(theta)*s;     // are quantized
      }
      else{
         theta = acos(2.0*Rnd()-1.0); // angle and Sz
         sz    = sm*cos(theta);       // are not quantized
      }

      // magnetic moment
      muz = -sz*GS*EoM/2.0;

      // Velocity of a Silver atom taken from a Maxwell-Boltzman distribution
      v = Maxwell_Boltzman(MAg,T);

      // Populate uniformly Silver atoms in x-z plane at y=0
      x0 = -XMAX/2.0 + Rnd()*XMAX;
      z0 = -ZMAX/2.0 + Rnd()*ZMAX;

      // Get the field gradient
      dBdz = Field_Grad(field,x0);

      // Acceceleration and final position along z axis
      a = muz*dBdz/MAg;
      z = z0 + 0.5*a*(L/v)*(L/v) + sign(a)*D*sqrt(2.0*fabs(a)*L)/v;

      // print the final results to the user screen
      printf("%12.4e  %12.4e\n",x0,z);
    }

} /* main */

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  double Maxwell_Boltzman(double m, double T){
  //--------------------------------------------------------------
  // Returns, for an atom of mass m (kg) and temperature T (K),
  // a velocity in m/s which is randomly selected from
  // a Maxwell-Boltzman Distribution function, Fmb, around the
  // most probable velocity, vp. To do this,
  // the Monte-Carlo Rejection method is used.
  //--------------------------------------------------------------
  double  r,v,vmax,vmin,vp,Ptest,fmb,fmax,kT,C;

   kT   = kB*T;
   C    = PI*sqrt(2.0) * pow(m/(PI*kT),1.5);
   vp   = sqrt(2.0*kT/m);
   vmin = vp-vp/4.0;
   vmax = vp+vp/4.0;
   fmax = C * vp*vp * exp(-1.0);

    // Rejection algorithm
    while(1)
    {
      r = Rnd();
      v = Rnd();

      Ptest = fmax*r;
      v     = vmin + (vmax-vmin)*v;
      fmb   = C * v*v * exp(-0.5*m*v*v/kT);

      if (fmb > Ptest) return v;
    }
  }

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  double Field_Grad(int op, double x){
  //------------------------------------------------------------
  // Returns the components of the magnetic field
  // gradient (in Tesla/meter) along the z direction.
  //------------------------------------------------------------
  double fg;

    if(op==0)      fg = 0.0;   // uniform magnetic field in z axis
    else if(op==1) fg = DBZ;   // non-uniform in z but uniform in x direction
    else
                   fg = DBZ*exp(-25*x*x/XMAX/XMAX);
//                 fg = DBZ*(1-4*x*x/XMAX/XMAX);

    return fg;

  }

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  double Rnd(void){
  //--------------------------------------------------------------
  // Returns an random number uniformly distributed between [0,1].
  //--------------------------------------------------------------
  double r;
   r = (double) rand()/RAND_MAX;
   return r;
  }

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  double sign(double x){
  //--------------------------------------------------------------
  // Returns sign of x (-1 or +1)
  //--------------------------------------------------------------
   if(x>0.0) return +1.0;
   else      return -1.0;
  }


//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  void Command_Line(int argc, char **argv, int *f, int *q){
  //------------------------------------------------------------
  // Gets and checks command line arguments, and returs a 
  // field (f), and quantum (q).
  //------------------------------------------------------------

    if(argc == 1){
      *f = 1;
      *q = 1;
      return;
    }

    if(argc > 3 || argc == 2) usage();

    else{
              if( strcmp(argv[1],"-f0")==0 ) *f = 0;
         else if( strcmp(argv[1],"-f1")==0 ) *f = 1;
         else if( strcmp(argv[1],"-f2")==0 ) *f = 2;
         else usage();

              if( strcmp(argv[2],"-q0")==0 ) *q = 0;
         else if( strcmp(argv[2],"-q1")==0 ) *q = 1;
         else usage();
    }
  }

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  void usage(void){
  //--------------------------------------------------------------
  // Prints the usage of the program.
  //--------------------------------------------------------------
     printf("spin: ANSI C version.\n\n");
     printf("Usage:  spin [-f012] [-q01]\n\n");
     printf("Options:\n");
     printf("Magnetic field gradient:\n");
     printf(" -f0  sets up a uniform     magnetic field along +z axis with     B > 0\n");
     printf(" -f1  sets up a non-uniform magnetic field along +z axis with dB/dz > 0  (default)\n");
     printf(" -f2  sets up a non-uniform modulated by a single Gaussian\n");
     printf("      magnetic field along +z axis with dB/dz > 0\n\n");
     printf("Quantum effect:\n");
     printf(" -q0  Excludes the quantum effect. Sz is not quantized.\n");
     printf("      That is cos(theta) is randomly oriented\n");
     printf(" -q1  Includes the quantum effect. Sz is quantized.\n");
     printf("      That is cos(theta) = +- 1/sqrt(3)\n\n");
     printf(" See for details: http://www1.gantep.edu.tr/~bingul/seminar/spin/\n\n");
     exit(1);
  }