Пример #1
0
        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.
            }
        }
Пример #2
0
        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));
        }
Пример #3
0
        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);
        }
Пример #4
0
        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);
        }