/// <summary> /// Copy a Particle /// </summary> /// <param name="mass"></param> public Particle(Particle particle) { mass = particle.mass; position = particle.position; momentum = particle.momentum; interactions = particle.interactions; }
/// <summary> /// Allow timeInterval to pass for the Particle with given netForce applied. /// Recommend putting Particle into a PhysicalSystem and using a PhysicalSystem.Iterate() instead. /// </summary> /// <param name="timeInterval"></param> /// <param name="netForce"></param> public void Iterate(Time timeInterval, Force netForce) { Momentum changeInMomentum = timeInterval * netForce; Displacement changeInPosition = timeInterval * Velocity(momentum + changeInMomentum / 2.0); momentum += changeInMomentum; position += changeInPosition; }
/// <summary> /// Update the positions and momenta of all particles in the system using Runge-Kutta method... /// Does not seem to be working correctly. Leave private until debugged. /// </summary> /// <param name="timeInterval">The amount of time passing during this iteration</param> private void RK4Iterate(Time timeInterval) { List <Task> tasks = new List <Task>(); // store temporary particle data for Runge-Kutta method Particle[] virtualParticles = new Particle[particles.Count]; Momentum[,] momenta = new Momentum[particles.Count, 4]; Displacement[,] displacements = new Displacement[particles.Count, 4]; // initialize virtualParticles for (int particleIndex = 0; particleIndex < particles.Count; particleIndex++) { tasks.Add(InitializeParticle(particleIndex)); } Task.WaitAll(tasks.ToArray()); // first iteration for (int particleIndex = 0; particleIndex < particles.Count; particleIndex++) { tasks.Add(CalculateRungeKuttaCoefficients(particleIndex, 0)); } Task.WaitAll(tasks.ToArray()); // update before 2nd iteration for (int particleIndex = 0; particleIndex < particles.Count; particleIndex++) { tasks.Add(UpdateParticleBefore2ndOr3rdIteration(particleIndex, 0)); } Task.WaitAll(tasks.ToArray()); // 2nd iteration for (int particleIndex = 0; particleIndex < particles.Count; particleIndex++) { tasks.Add(CalculateRungeKuttaCoefficients(particleIndex, 1)); } Task.WaitAll(tasks.ToArray()); // update before 3rd iteration for (int particleIndex = 0; particleIndex < particles.Count; particleIndex++) { tasks.Add(UpdateParticleBefore2ndOr3rdIteration(particleIndex, 1)); } Task.WaitAll(tasks.ToArray()); // 3rd iteration for (int particleIndex = 0; particleIndex < particles.Count; particleIndex++) { tasks.Add(CalculateRungeKuttaCoefficients(particleIndex, 2)); } Task.WaitAll(tasks.ToArray()); // update before 4th iteration for (int particleIndex = 0; particleIndex < particles.Count; particleIndex++) { tasks.Add(UpdateParticleBefore4thIteration(particleIndex)); } Task.WaitAll(tasks.ToArray()); // 4th iteration for (int particleIndex = 0; particleIndex < particles.Count; particleIndex++) { tasks.Add(CalculateRungeKuttaCoefficients(particleIndex, 3)); } Task.WaitAll(tasks.ToArray()); // update actual particles using the Runge-Kutta coefficients for (int particleIndex = 0; particleIndex < particles.Count; particleIndex++) { tasks.Add(UpdateParticleUsingRungeKuttaCoefficients(particleIndex)); } Task.WaitAll(tasks.ToArray()); async Task InitializeParticle(int particleIndex) { virtualParticles[particleIndex] = new Particle(particles[particleIndex]); for (int iterationNumber = 0; iterationNumber < 4; iterationNumber++) { momenta[particleIndex, iterationNumber] = new Momentum(); } } async Task CalculateRungeKuttaCoefficients(int particleIndex, int iterationNumber) { displacements[particleIndex, iterationNumber] = timeInterval * particles[particleIndex].Velocity(); foreach (Interaction interaction in virtualParticles[particleIndex].interactions) { momenta[particleIndex, iterationNumber] += timeInterval * interaction.InteractionForce(virtualParticles[particleIndex], interaction.B); } } async Task UpdateParticleBefore2ndOr3rdIteration(int particleIndex, int iterationJustFinished) { virtualParticles[particleIndex] = new Particle(particles[particleIndex]); virtualParticles[particleIndex].momentum += momenta[particleIndex, iterationJustFinished]; // / 2.0; virtualParticles[particleIndex].position += displacements[particleIndex, iterationJustFinished]; // / 2.0; } async Task UpdateParticleBefore4thIteration(int particleIndex) { virtualParticles[particleIndex] = new Particle(particles[particleIndex]); virtualParticles[particleIndex].momentum += momenta[particleIndex, 2]; virtualParticles[particleIndex].position += displacements[particleIndex, 2]; } async Task UpdateParticleUsingRungeKuttaCoefficients(int particleIndex) { //particles[particleIndex].position += (displacements[particleIndex, 0] + // 2.0 * displacements[particleIndex, 1] + 2.0 * displacements[particleIndex, 2] + // displacements[particleIndex, 3]) / 6.0; //particles[particleIndex].momentum += (momenta[particleIndex, 0] + // 2.0 * momenta[particleIndex, 1] + 2.0 * momenta[particleIndex, 2] + // momenta[particleIndex, 3]) / 6.0; particles[particleIndex].position += (displacements[particleIndex, 0] + displacements[particleIndex, 1]) / 2.0; particles[particleIndex].momentum += (momenta[particleIndex, 0] + momenta[particleIndex, 1]) / 2.0; } }
/// <summary> /// Heun's method for numerical integration /// </summary> /// <param name="timeStep"></param> private void HeunIntegration(Time timeStep) { List <Task> tasks = new List <Task>(); // store temporary particle data for Runge-Kutta method Displacement[] initialPositions = new Displacement[particles.Count]; Momentum[] initialMomenta = new Momentum[particles.Count]; Momentum[,] momenta = new Momentum[particles.Count, 2]; Displacement[,] displacements = new Displacement[particles.Count, 2]; // store initial condition for (int particleIndex = 0; particleIndex < particles.Count; particleIndex++) { tasks.Add(StoreInitialConditions(particleIndex)); } Task.WaitAll(tasks.ToArray()); // first iteration for (int particleIndex = 0; particleIndex < particles.Count; particleIndex++) { tasks.Add(CalculateRungeKuttaCoefficients(particleIndex, 0)); } Task.WaitAll(tasks.ToArray()); // update particles based on linear estimate for (int particleIndex = 0; particleIndex < particles.Count; particleIndex++) { tasks.Add(LinearUpdate(particleIndex)); } Task.WaitAll(tasks.ToArray()); // 2nd iteration for (int particleIndex = 0; particleIndex < particles.Count; particleIndex++) { tasks.Add(CalculateRungeKuttaCoefficients(particleIndex, 1)); } Task.WaitAll(tasks.ToArray()); // update actual particles using the Runge-Kutta coefficients for (int particleIndex = 0; particleIndex < particles.Count; particleIndex++) { tasks.Add(UpdateParticles(particleIndex)); } Task.WaitAll(tasks.ToArray()); async Task StoreInitialConditions(int particleIndex) { initialPositions[particleIndex] = particles[particleIndex].position; initialMomenta[particleIndex] = particles[particleIndex].momentum; } async Task CalculateRungeKuttaCoefficients(int particleIndex, int iterationNumber) { displacements[particleIndex, iterationNumber] = timeStep * particles[particleIndex].Velocity(); Force netForce = new Force(); foreach (Interaction interaction in particles[particleIndex].interactions) { netForce += interaction.InteractionForce(); } momenta[particleIndex, iterationNumber] = timeStep * netForce; } async Task LinearUpdate(int particleIndex) { particles[particleIndex].position += timeStep * particles[particleIndex].Velocity( particles[particleIndex].momentum + momenta[particleIndex, 0] / 2.0); particles[particleIndex].momentum += momenta[particleIndex, 0]; } async Task UpdateParticles(int particleIndex) { particles[particleIndex].position = initialPositions[particleIndex] + (displacements[particleIndex, 0] + displacements[particleIndex, 1]) / 2.0; particles[particleIndex].momentum = initialMomenta[particleIndex] + (momenta[particleIndex, 0] + momenta[particleIndex, 1]) / 2.0; } }
/// <summary> /// Update the positions and momenta of all particles in the system /// using the 2nd-order Runge-Kutta method. /// Doesn't perform as well as NotQuiteRK2(). Am I interpretting the Runge-Kutta method incorrectly? /// </summary> /// <param name="timeInterval">The amount of time passing during this iteration</param> private void RK2Iterate(Time timeInterval) { List <Task> tasks = new List <Task>(); // store temporary particle data for Runge-Kutta method Displacement[] initialPositions = new Displacement[particles.Count]; Momentum[] initialMomenta = new Momentum[particles.Count]; Momentum[,] momenta = new Momentum[particles.Count, 2]; Displacement[,] displacements = new Displacement[particles.Count, 2]; // store initial condition for (int particleIndex = 0; particleIndex < particles.Count; particleIndex++) { tasks.Add(StoreInitialConditions(particleIndex)); } Task.WaitAll(tasks.ToArray()); // first iteration for (int particleIndex = 0; particleIndex < particles.Count; particleIndex++) { tasks.Add(FirstIteration(particleIndex)); } Task.WaitAll(tasks.ToArray()); // 2nd iteration for (int particleIndex = 0; particleIndex < particles.Count; particleIndex++) { tasks.Add(SecondIteration(particleIndex)); } Task.WaitAll(tasks.ToArray()); async Task StoreInitialConditions(int particleIndex) { initialPositions[particleIndex] = particles[particleIndex].position; initialMomenta[particleIndex] = particles[particleIndex].momentum; } async Task FirstIteration(int particleIndex) { Force netForce = new Force(); foreach (Interaction interaction in particles[particleIndex].interactions) { netForce += interaction.InteractionForce(); } // The first iteration in RK2 only moves a half step. Momentum changeInMomentum = timeInterval * netForce / 2.0; particles[particleIndex].position += timeInterval * particles[particleIndex].Velocity( particles[particleIndex].momentum + changeInMomentum / 2.0) / 2.0; particles[particleIndex].momentum += changeInMomentum; } async Task SecondIteration(int particleIndex) { Force netForce = new Force(); foreach (Interaction interaction in particles[particleIndex].interactions) { netForce += interaction.InteractionForce(); } Momentum changeInMomentum = timeInterval * netForce; particles[particleIndex].position = initialPositions[particleIndex] + timeInterval * particles[particleIndex].Velocity( particles[particleIndex].momentum + changeInMomentum / 2.0); particles[particleIndex].momentum = initialMomenta[particleIndex] + changeInMomentum; } }
/// <summary> /// The Velocity of this Particle given a momentum /// </summary> /// <returns>The Velocity of this Particle given a momentum</returns> public Velocity Velocity(Momentum p) { return(p / mass); }