public void MoveWithPrecisionCheck(SimPoint accel, double deltaT) { if (!((accel.X == 0.0) && (accel.Y == 0.0))) { // Apply linear acceleration during the time interval double deltaVX = accel.X * deltaT; if (FloatingPointUtil.CheckAdditionPrecision(velocity.X, deltaVX)) { DisplayPrecisionIssue(velocity.X, deltaVX, "Adding DeltaV to VX", bodyNumber); } double newVelocityX = velocity.X + deltaVX; double deltaPX = (velocity.X + newVelocityX) / 2 * deltaT; if (FloatingPointUtil.CheckAdditionPrecision(position.X, deltaPX)) { DisplayPrecisionIssue(position.X, deltaPX, "Adding to Position.X", bodyNumber); } position.X += deltaPX; velocity.X = newVelocityX; double deltaVY = accel.Y * deltaT; if (FloatingPointUtil.CheckAdditionPrecision(velocity.Y, deltaVY)) { DisplayPrecisionIssue(velocity.Y, deltaVY, "Adding DeltaV to VY", bodyNumber); } double newVelocityY = velocity.Y + deltaVY; double deltaPY = (velocity.Y + newVelocityY) / 2 * deltaT; if (FloatingPointUtil.CheckAdditionPrecision(position.Y, deltaPY)) { DisplayPrecisionIssue(position.Y, deltaPY, "Adding to Position.Y", bodyNumber); } position.Y += deltaPY; velocity.Y = newVelocityY; } }
public static void CAP_SpecificExample() { // Run and display calculation described by this comment from CheckAdditionPrecision(): // Specific example: // This limit allows any quantity around or above 1/100 of a millimeter to be repeatedly added to 42,000 km (geosync orbit radius) // without losing too much accuracy. E.g. 1/300 of a mm added 10,000 times should about equal 1/30 of a meter double geosyncOrbitR = 42000.0; // Real example, approximate geosynchronous orbit in km double oneMillimeter = 1.0 / 1000.0 / 1000.0; // One mm in kilometers double smallDelta = oneMillimeter / 300.0; int numAdditions = 10000; Console.WriteLine("Large value = {0:N0}, small value = {1:G17}, addition iterations = {2:N0}", geosyncOrbitR, smallDelta, numAdditions); Console.WriteLine("CheckAdditionPrecision({0:N0}, {1}) = {2}", geosyncOrbitR, smallDelta, FloatingPointUtil.CheckAdditionPrecision(geosyncOrbitR, smallDelta)); for (int i = 0; i < numAdditions; i++) { geosyncOrbitR += smallDelta; } Console.WriteLine("Result = {0:G17}", geosyncOrbitR); }
// simRunning - true if sim is auto-running // false if sim is single stepping public void Step(double timeInterval, bool simRunning) { Stopwatch perfStopwatch = new Stopwatch(); long perfIntervalTicks = 0L; bool simStepping = !simRunning; double scaledTimeInterval = timeInterval * SpeedFactor; SetTimeForTrailMark(simElapsedTime); if (simStepping) { Debug.WriteLine("Elapsed times for {0} bodies:", bodies.Count()); perfStopwatch.Start(); } if (accelerations == null) { accelerations = new SimPoint[bodies.Count()]; } if (checkSim) { if (positions == null) { positions = new SimPoint[bodies.Count()]; } if (velocities == null) { velocities = new SimPoint[bodies.Count()]; } } double timeIntervalPerCycle = scaledTimeInterval / (double)simCalcSettings.CalculationCyclesPerFrame; List <SimPoint> otherPositions = new List <SimPoint>(); List <SimPoint> otherAccelerations = new List <SimPoint>(); if (checkSim) { for (int i = 0; i < bodies.Count(); i++) { positions[i] = bodies[i].Position; velocities[i] = bodies[i].Velocity; } Validate5BodyCross(positions, "Positions Before Update"); Validate5BodyCross(velocities, "Velocities Before Update"); } for (int calcCycle = 0; calcCycle < simCalcSettings.CalculationCyclesPerFrame; calcCycle++) { // Calculate NBody acceleration if (simCalcSettings.CheckAllAdditionPrecision) { for (int i = 0; i < bodies.Count(); i++) { accelerations[i].X = 0.0; accelerations[i].Y = 0.0; for (int j = 0; j < bodies.Count(); j++) { if ((i != j) && bodies[j].IsGravitySource) { SimPoint accel = bodies[i].BodyToBodyAccelerate(bodies[j]); if (simRounding > 0) { accel.X += Math.Round(accel.X, simRounding, MidpointRounding.AwayFromZero); accel.Y += Math.Round(accel.Y, simRounding, MidpointRounding.AwayFromZero); } if (FloatingPointUtil.CheckAdditionPrecision(accelerations[i].X, accel.X)) { Body.DisplayPrecisionIssue(accelerations[i].X, accel.X, "Accumulating Accel.X", i); } accelerations[i].X += accel.X; if (FloatingPointUtil.CheckAdditionPrecision(accelerations[i].Y, accel.Y)) { Body.DisplayPrecisionIssue(accelerations[i].Y, accel.Y, "Accumulating Accel.Y", i); } accelerations[i].Y += accel.Y; } } } } else if (simRounding > 0) { for (int i = 0; i < bodies.Count(); i++) { accelerations[i].X = 0.0; accelerations[i].Y = 0.0; for (int j = 0; j < bodies.Count(); j++) { if ((i != j) && bodies[j].IsGravitySource) { SimPoint accel = bodies[i].BodyToBodyAccelerate(bodies[j]); accelerations[i].X += Math.Round(accel.X, simRounding, MidpointRounding.AwayFromZero); accelerations[i].Y += Math.Round(accel.Y, simRounding, MidpointRounding.AwayFromZero); } } } } else { for (int i = 0; i < bodies.Count(); i++) { accelerations[i].X = 0.0; accelerations[i].Y = 0.0; for (int j = 0; j < bodies.Count(); j++) { if ((i != j) && bodies[j].IsGravitySource) { SimPoint accel = bodies[i].BodyToBodyAccelerate(bodies[j]); accelerations[i].X += accel.X; accelerations[i].Y += accel.Y; } } } } //if (checkSim) Validate5BodyCross(accelerations, "Accelerations Before Limit and Rounding"); if (accelerationLimitOn) { EnforceAccelerationLimit(accelerations, accelerationLimit); } if (simRounding > 0) { RoundAccelerations(accelerations, simRounding); } if (checkSim) { Validate5BodyCross(accelerations, "Accelerations After Limit and Rounding"); } // Update positons and velocities if (simCalcSettings.CheckAllAdditionPrecision) { for (int i = 0; i < bodies.Count(); i++) { bodies[i].MoveWithPrecisionCheck(accelerations[i], timeIntervalPerCycle); } } else { for (int i = 0; i < bodies.Count(); i++) { bodies[i].Move(accelerations[i], timeIntervalPerCycle); } } if (checkSim) { for (int i = 0; i < bodies.Count(); i++) { positions[i] = bodies[i].Position; velocities[i] = bodies[i].Velocity; } Validate5BodyCross(positions, "Positions After Update"); Validate5BodyCross(velocities, "Velocities After Update"); } simElapsedTime += timeIntervalPerCycle; if ((MainPage.trailsEnabled) && TimeForTrailsMark(simElapsedTime)) { DrawTrails(); } } if (simStepping) { perfIntervalTicks = DisplayPerfIntervalElapsed(perfStopwatch, perfIntervalTicks, String.Format("Compute N-body accelerations, update positions & velocities ({0} iterations)", simCalcSettings.CalculationCyclesPerFrame)); } // Update rendering renderer.BodiesMoved(bodies); simPage.UpdateMonitoredValues(bodies[monitoredBody], simElapsedTime); if (simStepping) { perfIntervalTicks = DisplayPerfIntervalElapsed(perfStopwatch, perfIntervalTicks, "Update transforms of XAML shapes & monitored values"); } if (simStepping) { Debug.WriteLine("Total elapsed time = {0:F2} ms", (double)perfIntervalTicks / (double)(Stopwatch.Frequency / 1000L)); Debug.WriteLine(""); } // stepRunning = false; }