public void OdeOrbitKepler() { // This is a simple Keplerian orbit. // Hull (1972) constructed initial conditions that guarantee a given orbital eccentricity // and a period of 2 \pi with unit masses and unit gravitational constant. Func <double, IList <double>, IList <double> > rhs = (double t, IList <double> r) => { double d = MoreMath.Hypot(r[0], r[1]); double d3 = MoreMath.Pow(d, 3); return(new double[] { -r[0] / d3, -r[1] / d3 }); }; foreach (double e in new double[] { 0.0, 0.1, 0.3, 0.5, 0.7, 0.9 }) { Console.WriteLine("e = {0}", e); ColumnVector r0 = new ColumnVector(1.0 - e, 0.0); ColumnVector rDot0 = new ColumnVector(0.0, Math.Sqrt((1.0 + e) / (1.0 - e))); double E = OrbitEnergy(r0, rDot0); double L = OrbitAngularMomentum(r0, rDot0); MultiOdeEvaluationSettings settings = new MultiOdeEvaluationSettings() { Listener = (MultiOdeResult b) => { Assert.IsTrue(TestUtilities.IsNearlyEqual(OrbitAngularMomentum(b.Y, b.YPrime), L, b.Settings)); EvaluationSettings relaxed = new EvaluationSettings() { RelativePrecision = 2.0 * b.Settings.RelativePrecision, AbsolutePrecision = 2.0 * b.Settings.AbsolutePrecision }; Assert.IsTrue(TestUtilities.IsNearlyEqual(OrbitEnergy(b.Y, b.YPrime), E, relaxed)); } }; MultiOdeResult result = MultiFunctionMath.IntegrateConservativeOde(rhs, 0.0, r0, rDot0, 2.0 * Math.PI, settings); ColumnVector r1 = result.Y; Console.WriteLine(result.EvaluationCount); Assert.IsTrue(TestUtilities.IsNearlyEqual(r0, r1, new EvaluationSettings() { RelativePrecision = 512 * result.Settings.RelativePrecision })); // For large eccentricities, we loose precision. This is apparantly typical behavior. // Would be nice if we could quantify expected loss, or correct to do better. } }
public void OdeOrbitFigureEight() { Func <double, IReadOnlyList <double>, IReadOnlyList <double> > rhs = (double t, IReadOnlyList <double> q) => { double x01 = q[0] - q[2]; double y01 = q[1] - q[3]; double x02 = q[0] - q[4]; double y02 = q[1] - q[5]; double x12 = q[2] - q[4]; double y12 = q[3] - q[5]; double r01 = MoreMath.Pow(MoreMath.Hypot(x01, y01), 3); double r02 = MoreMath.Pow(MoreMath.Hypot(x02, y02), 3); double r12 = MoreMath.Pow(MoreMath.Hypot(x12, y12), 3); return(new double[] { -x01 / r01 - x02 / r02, -y01 / r01 - y02 / r02, +x01 / r01 - x12 / r12, +y01 / r01 - y12 / r12, +x02 / r02 + x12 / r12, +y02 / r02 + y12 / r12 }); }; double[] q1 = new double[] { 0.97000435669734, -0.24308753153583 }; double[] p3 = new double[] { -0.93240737144104, -0.86473146092102 }; ColumnVector q0 = new ColumnVector(q1[0], q1[1], -q1[0], -q1[1], 0.0, 0.0); ColumnVector p0 = new ColumnVector(-p3[0] / 2.0, -p3[1] / 2.0, -p3[0] / 2.0, -p3[1] / 2.0, p3[0], p3[1]); double T = 6.32591398292621; MultiOdeResult result = MultiFunctionMath.IntegrateConservativeOde(rhs, 0.0, q0, p0, T); Assert.IsTrue(TestUtilities.IsNearlyEqual(q0, result.Y, 1.0E-10)); }
public void OdeSolarSystem() { Planet[] planets = new Planet[] { new Planet() { Name = "Sun", Mass = 1.3271244004193938E11 * 2.22972472E-15, Position = new ColumnVector(3.700509269632818E-03, 2.827000367199164E-03, -1.623212133858169E-04), Velocity = new ColumnVector(-1.181051079944745E-06, 7.011580463060376E-06, 1.791336618633265E-08), Year = 0.0 }, new Planet() { Name = "Earth", Mass = 398600.440 * 2.22972472E-15, Position = new ColumnVector(5.170309635282939E-01, -8.738395510520275E-01, -1.323433043109283E-04), Velocity = new ColumnVector(1.456221287512929E-02, 8.629625079574064E-03, 2.922661879104068E-07), Year = 365.25636 }, new Planet() { Name = "Jupiter", Mass = 126686511 * 2.22972472E-15, Position = new ColumnVector(-5.438893557878444E+00, 1.497713688978628E-01, 1.210124167423688E-01), Velocity = new ColumnVector(-2.955588275385831E-04, -7.186425047188191E-03, 3.648630400553893E-05), Year = 4332.59 }, new Planet() { Name = "Saturn", Mass = 37931207.8 * 2.22972472E-15, Position = new ColumnVector(-2.695377264613252E+00, -9.657410990313211E+00, 2.751886819002198E-01), Velocity = new ColumnVector(5.067197776417300E-03, -1.516587142748002E-03, -1.754902991309407E-04), Year = 10759.22 } }; Func <double, IReadOnlyList <double>, IReadOnlyList <double> > rhs = (double t, IReadOnlyList <double> p) => { ColumnVector a = new ColumnVector(3 * planets.Length); for (int i = 0; i < planets.Length; i++) { for (int j = 0; j < planets.Length; j++) { if (i == j) { continue; } double x = p[3 * i] - p[3 * j]; double y = p[3 * i + 1] - p[3 * j + 1]; double z = p[3 * i + 2] - p[3 * j + 2]; double d3 = Math.Pow(x * x + y * y + z * z, 3.0 / 2.0); a[3 * i] -= planets[j].Mass * x / d3; a[3 * i + 1] -= planets[j].Mass * y / d3; a[3 * i + 2] -= planets[j].Mass * z / d3; } } return(a); }; ColumnVector p0 = new ColumnVector(3 * planets.Length); for (int i = 0; i < planets.Length; i++) { p0[3 * i] = planets[i].Position[0]; p0[3 * i + 1] = planets[i].Position[1]; p0[3 * i + 2] = planets[i].Position[2]; } ColumnVector q0 = new ColumnVector(3 * planets.Length); for (int i = 0; i < planets.Length; i++) { q0[3 * i] = planets[i].Velocity[0]; q0[3 * i + 1] = planets[i].Velocity[1]; q0[3 * i + 2] = planets[i].Velocity[2]; } MultiOdeSettings settings = new MultiOdeSettings() { RelativePrecision = 1.0E-8, AbsolutePrecision = 1.0E-16 }; MultiOdeResult result = MultiFunctionMath.IntegrateConservativeOde(rhs, 0.0, p0, q0, 4332.59, settings); }
public void OdeOrbitTriangle() { // Lagrange described three-body systems where each body sits at the vertex // of an equilateral triangle. Each body executes an elliptical orbit // and the triangle remains equilateral, although its size can change. // The case of equal masses on a circle is easily analytically tractable. // In this case the orbits are all along the same circle and the // the triangle doesn't change size, it just rotates. Func <double, IReadOnlyList <double>, IReadOnlyList <double> > rhs = (double t, IReadOnlyList <double> r) => { double x01 = r[0] - r[1]; double x02 = r[0] - r[2]; double x12 = r[1] - r[2]; double y01 = r[3] - r[4]; double y02 = r[3] - r[5]; double y12 = r[4] - r[5]; double r01 = MoreMath.Pow(MoreMath.Hypot(x01, y01), 3); double r02 = MoreMath.Pow(MoreMath.Hypot(x02, y02), 3); double r12 = MoreMath.Pow(MoreMath.Hypot(x12, y12), 3); return(new double[] { -x01 / r01 - x02 / r02, x01 / r01 - x12 / r12, x02 / r02 + x12 / r12, -y01 / r01 - y02 / r02, y01 / r01 - y12 / r12, y02 / r02 + y12 / r12 }); }; double L = 1.0; double R = L / Math.Sqrt(3.0); double v = Math.Sqrt(1.0 / L); double T = 2.0 * Math.PI * Math.Sqrt(L * L * L / 3.0); double c = 1.0 / 2.0; double s = Math.Sqrt(3.0) / 2.0; ColumnVector r0 = new ColumnVector(R, -c * R, -c * R, 0.0, s * R, -s * R); double[] rp0 = new double[] { 0.0, -s * v, s *v, v, -c * v, -c * v }; int count = 0; MultiOdeSettings settings = new MultiOdeSettings() { RelativePrecision = 1.0E-12, AbsolutePrecision = 1.0E-12, Listener = (MultiOdeResult mer) => { count++; ColumnVector r = mer.Y; double x01 = r[0] - r[1]; double x02 = r[0] - r[2]; double x12 = r[1] - r[2]; double y01 = r[3] - r[4]; double y02 = r[3] - r[5]; double y12 = r[4] - r[5]; Assert.IsTrue(TestUtilities.IsNearlyEqual(MoreMath.Hypot(x01, y01), L, mer.Settings)); Assert.IsTrue(TestUtilities.IsNearlyEqual(MoreMath.Hypot(x02, y02), L, mer.Settings)); Assert.IsTrue(TestUtilities.IsNearlyEqual(MoreMath.Hypot(x12, y12), L, mer.Settings)); } }; MultiOdeResult result = MultiFunctionMath.IntegrateConservativeOde(rhs, 0.0, r0, rp0, T, settings); // Test that one period brought us back to the same place. Assert.IsTrue(TestUtilities.IsNearlyEqual(r0, result.Y, settings)); Assert.IsTrue(count > 0); }