def setUp(self): self.field = EMField(np.array([0, 0, 0.0000001], dtype=float), np.array([0.0, 0.00001, 0.0], dtype=float)) self.proton = Proton(np.array([0, 0, 0], dtype=float), np.array([0, 1000, 0], dtype=float), np.array([0, 0, 0], dtype=float), "Proton") self.time = 5
def test_frequency(test_input, expected): """ This function tests that the (sychro)cyclotron frequency is correctly being calculated for both non-relativistic and relativistic particle bunches. """ field = EMField([-0.3, 0.1, 0.2], [6 * 10**(-5), -7 * 10**(-5), 8 * 10**(-5)]) protons = ProtonBunch(100, 1) protons.bunch = [test_input] field.setFrequency(protons) assert expected == pytest.approx(field.frequency, rel=0.001)
def test_adaptiveStep(test_input, expected): """ This will test that the step size in any integrator correctly reduces when any of the particles in the bunch are either, in the electric field, or within ±10% of the electric field's boundaries. """ deltaT = 100 protons = ProtonBunch(100, 1) protons.bunch[0].position = test_input field = EMField(ElectricFieldWidth=[-1, 1]) assert protons.adaptiveStep(deltaT, field) == expected
def test_velocityVerlet(): """ This tests that the velocity Verlet update method is correctly updating a particle's position and velocity. """ proton = ChargedParticle(Position=[0,0,0],Velocity=[3000,0,0],Acceleration=[0,-9000,0],Charge=2,Mass=1) field = EMField(MagneticField=[0,0,1.5],ElectricFieldWidth=[0,0]) proton.velocityVerlet(0.1,field,0) expected_position = [300., -45., 0.] expected_velocity = [2865., -879.75, 0.] assert np.array_equal(expected_position,proton.position) assert np.allclose(expected_velocity,proton.velocity,atol=0.01)
def test_RungeKutta4(): """ This tests that the fourth order Runge-Kutta update method is correctly updating a particle's position and velocity. """ proton = ChargedParticle(Position=[0,0,0],Velocity=[3000,0,0],Acceleration=[0,-9000,0],Charge=2,Mass=1) field = EMField(MagneticField=[0,0,1.5],ElectricFieldWidth=[0,0]) proton.RungeKutta4(0.1,field,0) expected_position = [295.5, -44.6625, 0.] expected_velocity = [2866.0125, -886.5, 0.] assert np.allclose(expected_position,proton.position,atol=0.0001) assert np.allclose(expected_velocity,proton.velocity,atol=0.01)
def __init__(self, timestep, totaltime, intmethod, ElectricF, MagneticF, BunchSize): self.timestep = timestep self.totaltime = totaltime self.intmethod = intmethod self.ElectricF = ElectricF self.MagneticF = MagneticF self.BunchSize = BunchSize Field = EMField(Magnetic=self.MagneticF, Electric=self.ElectricF) self.Field = Field ParticleBunch = Bunch(self.BunchSize) self.Bunch = ParticleBunch self.Bunch.CreateBunch()
class TestEMField(unittest.TestCase): def setUp(self): self.field = EMField(np.array([0, 0, 0.0000001], dtype=float), np.array([0.0, 0.00001, 0.0], dtype=float)) self.proton = Proton(np.array([0, 0, 0], dtype=float), np.array([0, 1000, 0], dtype=float), np.array([0, 0, 0], dtype=float), "Proton") self.time = 5 def test_lorentzaccel(self): result = self.field.lorentzaccel(self.proton, self.time) expected = np.array([9578.83322415, -667.00632589, 0.0]) assert (np.isclose(result, expected)).all()
def test_exceedingC(): """ This test will check that if the user tries to create a particle whose speed exceeds the speed of light in a vacuum, a ValueError is raised. It will then check that if a particle's speed is ever equal to or greater than the speed of the light in vacuum, the selected integrator will raise a ValueError. """ with pytest.raises(ValueError): Particle('proton', const.m_p, [0,0,0], [300000000,0,0]) proton = ChargedParticle() field = EMField() proton.velocity = np.array([3*10**(8),0,0],dtype=float) with pytest.raises(ValueError): proton.euler(0.1) with pytest.raises(ValueError): proton.eulerCromer(0.1) with pytest.raises(ValueError): proton.velocityVerlet(0.1,field,0) with pytest.raises(ValueError): proton.RungeKutta4(0.1,field,0)
def generate_file(phases): field = EMField([500 * 10**3, 0, 0], [0, 0, 2.82], [-0.029, 0.029]) protons = ProtonBunch(500 * 10**(6), 100) inital_bunch = copy.deepcopy(protons) deltaT, duration = 10**(-11), 2.78 * 10**(-8) * 10 timeSeries = [] positionSpread = [] energySpread = [] for _ in range(len(phases)): positionSpread.append([]) energySpread.append([]) timeSeries.append([]) log.logger.info('starting simulation') for (angle, loop_1, loop_2, loop_3) in zip(phases, positionSpread, energySpread, timeSeries): time = 0 field.phase = angle # apply the new phase shift to the electric field protons = copy.deepcopy(inital_bunch) # reset the proton bunch log.logger.info('phase currently being investigated: %s radians' % angle) while time <= duration: dt = protons.adaptiveStep(deltaT, field) time += dt field.setFrequency(protons) field.getAcceleration(protons.bunch, time, dt) protons.update(dt, field, time) temp_spread = copy.deepcopy(protons.positionSpread()) temp_energy = copy.deepcopy(protons.energySpread()) loop_1.append(temp_spread) loop_2.append(temp_energy) loop_3.append(time) log.logger.info('simulation finished') log.logger.info('writing to file') np.savez('phase_synchro_data', time=timeSeries, positions=positionSpread, energies=energySpread) log.logger.info('file written') return np.load('phase_synchro_data.npz', allow_pickle=True)
def generate_file(phases): field = EMField([50000,0,0], [0,0,0.07]) inital_bunch = ProtonBunch(10**(6),100,10**(-12)) field.setFrequency(inital_bunch) deltaT, duration = 10**(-9), 10**(-6)*10 timeSeries = [] positionSpread = [] energySpread = [] for _ in range(len(phases)): positionSpread.append([]) energySpread.append([]) log.logger.info('starting simulation') for (angle,loop_1,loop_2) in zip(phases,positionSpread,energySpread): time = 0 field.phase = angle # apply the new phase shift to the electric field protons = copy.deepcopy(inital_bunch) # reset the proton bunch log.logger.info('phase currently being investigated: %s radians' % angle) while time <= duration: time += deltaT if angle == phases[0]: timeSeries.append(time) field.getAcceleration(protons.bunch, time, deltaT) protons.update(deltaT,field,time,2) temp_spread = copy.deepcopy(protons.positionSpread()) temp_energy = copy.deepcopy(protons.energySpread()) loop_1.append(temp_spread) loop_2.append(temp_energy) log.logger.info('simulation finished') log.logger.info('writing to file') np.savez('phase_data', time=timeSeries, positions=positionSpread, energies=energySpread) log.logger.info('file written') return np.load('phase_data.npz',allow_pickle=True)
from ChargedParticle import ChargedParticle from ProtonBunch import ProtonBunch """ This file is an independent file that tests the simulated time period of a proton in a cyclotron against the expected time period given by: T = 2*pi*m / (qB) If you do not have the csv containing approximately 100 revolutions, the simulation will run and generate the csv file for you. It will then print the mean time period and its error (one standard deviation) and the time period predicted by the equation above. Finally it will print, the ratio of the two time periods; the simulated time period divided by the theoretical one. """ field = EMField([0.1,0,0], [0,0,1.6*10**(-5)]) proton = ChargedParticle('proton', const.m_p, const.e, [0,0,0], [3000,0,0]) protons = ProtonBunch(100,1) # create a random bunch obeject protons.bunch = [proton] # overwrite bunch atrribute to to remove the pseudorandom characteristics field.setFrequency(protons) theoretical_period = 2*const.pi*proton.mass / (proton.charge*field.magneticMag()) def generate_file(proton,field): time, deltaT, duration = 0, 10**(-5), 0.0041*100 timeSeries = [] data_csv = [] revolution = 0 data_csv.append([revolution,time,0,[0,0,0]]) # ensure data always has one element to avoid index error on line 46 log.logger.info('starting simulation') while time <= duration:
import copy from EMField import EMField from ProtonBunch import ProtonBunch """ This file generates a file called "cyclotron_data.npz" it contains a time series and four lists containing copies of a proton bunch in a cyclotron at every time value in the time series. These seperate lists were updated using different numerical methods. The numerical methods are: Euler Euler-Cromer Velocity Verlet Fourth order Runge-Kutta """ field = EMField([500000, 0, 0], [0, 0, 0.07]) protons_1 = ProtonBunch(10**(6), 3) protons_2 = copy.deepcopy(protons_1) protons_3 = copy.deepcopy(protons_1) protons_4 = copy.deepcopy(protons_1) field.setFrequency(protons_1) log.logger.info('Initial average kinetic energy: %s eV' % protons_1.KineticEnergy()) log.logger.info('Initial average momentum: %s kg m/s' % protons_1.momentum()) log.logger.info('Initial average position %s m' % protons_1.averagePosition()) log.logger.info('Initial bunch position spread: %s m' % protons_1.positionSpread()) log.logger.info('Initial bunch energy spread: %s eV' % protons_1.energySpread())
from ProtonBunch import ProtonBunch """ This file will analyse the how time step effects the accuracy of the simulation. It will record a cyclotron simulation using velocity Verlet method using three different time steps. It will then plot the fractional kinetic energy and momentum against time for each time step. If you do not have the data file: timestep_data.npz which contains the kinetic energy and momentum data then the file will be generated for you. I would recommend you reduce the size of the bunch and the simulation's duration otherwise the run time will be quite long. """ timesteps = [10**(-8), 4.5*10**(-9), 10**(-9)] step_strings = ['1e-8 s', '5e-9 s', '1e-9 s'] step_dict = {step_strings[i]:timesteps[i] for i in range(len(timesteps))} field = EMField([0,0,0], [0,0,0.07], [0,0]) protons = ProtonBunch(10**(6),1) field.setFrequency(protons) def generate_file(protons,field): inital_bunch = copy.deepcopy(protons) duration = 10**(-6)*100 timeSeries = [] kineticEnergies = [] momenta = [] timeSeries = [] for _ in range(len(timesteps)): kineticEnergies.append([]) momenta.append([]) timeSeries.append([])
""" This function tests that the (sychro)cyclotron frequency is correctly being calculated for both non-relativistic and relativistic particle bunches. """ field = EMField([-0.3, 0.1, 0.2], [6 * 10**(-5), -7 * 10**(-5), 8 * 10**(-5)]) protons = ProtonBunch(100, 1) protons.bunch = [test_input] field.setFrequency(protons) assert expected == pytest.approx(field.frequency, rel=0.001) @pytest.mark.parametrize( 'test_input,expected', [((ChargedParticle('proton-1', const.m_p, const.e, [0, 0, 0], [2000, 0, 0]), EMField([0.1, 0, 0])), [0.1, 0, 0]), ((ChargedParticle('proton-1', const.m_p, const.e, [10, 0, 0], [2000, 0, 0]), EMField([0.1, 0, 0])), [0, 0, 0]), ((ChargedParticle('proton-1', const.m_p, const.e, [10, 0, 0], [2000, -4000, 6000]), EMField([0, 0, 0], [6 * 10**(-5), -7 * 10**(-5), 8 * 10**(-5)])), [0.1, 0.2, 0.1]), ((ChargedParticle('proton-1', const.m_p, const.e, [0, 0, 0], [2000, -4000, 6000]), EMField([0, 0, 0], [6 * 10**(-5), -7 * 10**(-5), 8 * 10**(-5)])), [0, 0, 0])]) def test_getAcceleration(test_input, expected): # BUG if you run the tests, they pass as expected but if you debug the tests you get an # unbound local error in the EMField getAcceleration method but the tests still pass? """ This function will test the getAcceleration method in the EMField class, it will check that in