#Simple Cyclotron model
#Author:  Manuel Jose Paez
#Instituto de Fisica
#Universidad de Antioquia
#Medellin, Colombia
#mpaez@fisica.udea.edu.co

#To run this program install Python and Vpython
#see: http://vpython.org/
#for Windows download and install Python and Vpython from: 
# http://vpython.org/contents/download_windows.html
#Copy this program and paste it in IDLE (for example), save it as
# Cyclotron.py and run it.
'''
These are the equations and the selected values for variables
mv*v/r=qvB  =>r=mv/qB, with: m=1,B=1,|q|=1 =>r=v
In gap with these units: qE=ma, => a=E
Vf*Vf=Vo*Vo+2ae, with e=20, and Vo=r, => Vf=sqrt(r*r+2*20*E), E=11
E points in opposite direction of motion in gap (to accelerate the electron)
'''
from visual.graph import * #graphics and math classes
scene=display(x=0,y=0,width=600,height=600,range=200,
              title='Cyclotron. B: cyan arrows, E: electric field white arrows',forward=(1,-1,-1))
pole1=ring(pos=(0,40,15),axis=(0,1,0),radius=140,thickness=1,shaftwidth=0.1)
pole2=ring(pos=(0,-40,15),axis=(0,1,0),radius=140,thickness=1)
d=frame()       #for yellow plate and blackbox
cylinder(frame=d,pos=(0,0,15),radius=130,axis=(0,1,0),color=color.yellow)
blackbox=box(frame=d,pos=(0,0,15),length=20,height=4,width=260,color=color.black)
ball=sphere(pos=(0,0,0),radius=5,color=color.green)  #the electron
ball.trail=curve(color=color.red,radius=1)           #trail left by electron
arrow1=arrow()                                       #arrows for electric field
arrow2=arrow()                                    #In this way, arrows will be erased
arrow3=arrow()
arrow4=arrow()
arrow5=arrow()
scene.lights=[vector(0.4,0.4,0.8),vector(-0.3,-0.5,-0.2)] #two lights
def Bfield():                        #plots magnetic field lines as blue arrows
    for z in range(-50,75,25):
        for x in range(-50,75,25):
            f=frame()                #to make cilinder with cone one unit
            cylinder(frame=f,pos=(x,0,z),radius=1,axis=(0,-80,0),color=color.cyan)
            cone(frame=f,pos=(x,-75,z),radius=3,axis=(0,-10,0),color=color.cyan)
            cone(frame=f,pos=(x,-30,z),radius=3,axis=(0,-10,0),color=color.cyan)
            f.pos=vector(x,40,z+15)
def trajectory(r,sign,offset):        #to plot semicurcular trajectories
    th=math.pi/20.0 
    for i in range(0,21):
        rate(20)
         
        if sign==-1:
            arrsize=20*math.cos(th*i)
            if arrsize>=0:
                arrow1.pos=(-10,5,-90+15)      
                arrow1.axis=(arrsize,0,0)
                arrow2.pos=(-10,5,-45+15)
                arrow2.axis=(arrsize,0,0)
                arrow3.pos=(-10,5,15)
                arrow3.axis=(arrsize,0,0)
                arrow4.pos=(-10,5,60)
                arrow4.axis=(arrsize,0,0)
                arrow5.pos=(-10,5,105)
                arrow5.axis=(arrsize,0,0)
            if arrsize<0:
                arrow1.pos=(10,5,-90+15)      
                arrow1.axis=(arrsize,0,0)
                arrow2.pos=(10,5,-45+15)
                arrow2.axis=(arrsize,0,0)
                arrow3.pos=(10,5,15)
                arrow3.axis=(arrsize,0,0)
                arrow4.pos=(10,5,60)
                arrow4.axis=(arrsize,0,0)
                arrow5.pos=(10,5,105)
                arrow5.axis=(arrsize,0,0)
            z=r*math.cos(th*i)+offset   #offset to shift z position
            x=-r*math.sin(th*i)+10*sign #10*sign makes the jump in the gap
            
        else:
            arrsize=20*math.cos(th*i)
            if arrsize>=0:
                arrow1.pos=(10,5,-90+15)      
                arrow1.axis=(-arrsize,0,0)
                arrow2.pos=(10,5,-45+15)
                arrow2.axis=(-arrsize,0,0)
                arrow3.pos=(10,5,15)
                arrow3.axis=(-arrsize,0,0)
                arrow4.pos=(10,5,60)
                arrow4.axis=(-arrsize,0,0)
                arrow5.pos=(10,5,105)
                arrow5.axis=(-arrsize,0,0)
            if arrsize<0:
                arrow1.pos=(-10,5,-90+15)      
                arrow1.axis=(-arrsize,0,0)
                arrow2.pos=(-10,5,-45+15)
                arrow2.axis=(-arrsize,0,0)
                arrow3.pos=(-10,5,15)
                arrow3.axis=(-arrsize,0,0)
                arrow4.pos=(-10,5,60)
                arrow4.axis=(-arrsize,0,0)
                arrow5.pos=(-10,5,105)
                arrow5.axis=(-arrsize,0,0)
            z=-r*math.cos(th*i)+offset
            x=r*math.sin(th*i)+10*sign
            
        ball.pos=vector(x,5,z)          #electron's position
        ball.trail.append(pos=(x,5,z))  #append the trail left by electron
def gap(sign,r):             #plots the arrows in gap and computes new radius
   
    rf=math.sqrt(440.0+r*r)     #E=11,electric field, new radius due E
    return rf   
def automatic():
   Bfield()
   r=20                         #initial trajectory radius
   sign=1.0                     #for direction of E field in gap
   offset=r                     
   trajectory(r,sign,offset)    #plot first  trajectory
   rf=gap(sign,r)               #electron finds gap
   sign=-1.0                    #change direction of E field
   offset=2*r-rf                #to match new radius and old trail
   for i in range(1,28):        #rest of the trajectories
      if sign==-1:
          trajectory(rf,sign,offset)
          offset=rf-offset
          rf=gap(sign,rf)
          offset=rf-offset
          sign =1
      else:
          trajectory(rf,sign,offset)
          offset=rf+offset
          rf=gap(sign,rf)
          offset=offset-rf
          sign =-1  
      if i==27:    #electron is expelled
          for j in range(1,8):
             rate(8)
             ball.pos=vector(30*j, 5,-rf+16)
             ball.trail.append(pos=(30*j,5,-rf+16))  
automatic()