示例#1
0
        public void OdeLorenz()
        {
            double sigma = 10.0;
            double beta  = 8.0 / 3.0;
            double rho   = 28.0;

            Func <double, IReadOnlyList <double>, IReadOnlyList <double> > rhs = (double t, IReadOnlyList <double> u) =>
                                                                                 new ColumnVector(
                sigma * (u[1] - u[0]),
                u[0] * (rho - u[2]) - u[1],
                u[0] * u[1] - beta * u[2]
                );

            MultiOdeSettings settings = new MultiOdeSettings()
            {
                RelativePrecision = 1.0E-8,
                EvaluationBudget  = 10000
            };

            ColumnVector u0 = new ColumnVector(1.0, 1.0, 1.0);

            MultiOdeResult result = MultiFunctionMath.IntegrateOde(rhs, 0.0, u0, 10.0, settings);

            Console.WriteLine(result.EvaluationCount);

            // Is there anything we can assert? There is no analytic solution or conserved quantity.
        }
示例#2
0
        public void OdeLotkaVolterra()
        {
            // Lotka/Volterra equations are a non-linear predator-prey model.
            //   \dot{x} = A x + B x y
            //   \dot{y} = -C y + D x y
            // See http://mathworld.wolfram.com/Lotka-VolterraEquations.html

            // It can be shown solutions are always periodic (but not sinusoidal, and
            // often with phase shift between x and y).

            // Equilibria are x, y = C/D, A /B (and 0, 0).
            // If started positive, x and y never go negative.
            // A conserved quantity is: - D x + C log x - B y + A log y
            // Period of equations linearized around equilibrium is 2 \pi / \sqrt{AC}.

            // Can also be used to model chemical reaction rates.

            double A = 1.5; // Prey growth rate
            double B = 1.0;
            double C = 3.0; // Predator death rate
            double D = 1.0;

            // Try A = 20, B = 1, C = 30, D = 1, X = 8, Y = 12, T = 1
            // Try A = 1.5, B = 1 C = 3, D = 1, X = 10, Y = 5, T = 10

            Func <double, IReadOnlyList <double>, IReadOnlyList <double> > rhs = (double t, IReadOnlyList <double> p) =>
                                                                                 new double[] {
                A *p[0] - B * p[0] * p[1],
                -C *p[1] + D * p[0] * p[1]
            };

            Func <IList <double>, double> conservedQuantity = (IList <double> p) =>
                                                              - D * p[0] + C * Math.Log(p[0]) - B * p[1] + A * Math.Log(p[1]);

            ColumnVector p0 = new ColumnVector(10.0, 5.0);

            double L0 = conservedQuantity(p0);

            // Set up a handler that verifies conservation and positivity
            MultiOdeSettings settings = new MultiOdeSettings()
            {
                Listener = (MultiOdeResult rr) => {
                    double L = conservedQuantity(rr.Y);
                    Assert.IsTrue(rr.Y[0] > 0.0);
                    Assert.IsTrue(rr.Y[1] > 0.0);
                    Assert.IsTrue(TestUtilities.IsNearlyEqual(L, L0, rr.Settings));
                }
            };

            // Estimate period
            double T = 2.0 * Math.PI / Math.Sqrt(A * C);

            // Integrate over a few estimated periods
            MultiOdeResult result = MultiFunctionMath.IntegrateOde(rhs, 0.0, p0, 3.0 * T, settings);

            Console.WriteLine(result.EvaluationCount);
        }
示例#3
0
        public static void IntegrateOde()
        {
            Func <double, double, double> rhs = (x, y) => - x * y;
            OdeResult sln = FunctionMath.IntegrateOde(rhs, 0.0, 1.0, 2.0);

            Console.WriteLine($"Numeric solution y({sln.X}) = {sln.Y}.");
            Console.WriteLine($"Required {sln.EvaluationCount} evaluations.");
            Console.WriteLine($"Analytic solution y({sln.X}) = {Math.Exp(-MoreMath.Sqr(sln.X) / 2.0)}");

            // Lotka-Volterra equations
            double A = 0.1;
            double B = 0.02;
            double C = 0.4;
            double D = 0.02;
            Func <double, IReadOnlyList <double>, IReadOnlyList <double> > lkRhs = (t, y) => {
                return(new double[] {
                    A *y[0] - B * y[0] * y[1], D *y[0] * y[1] - C * y[1]
                });
            };
            MultiOdeSettings lkSettings = new MultiOdeSettings()
            {
                Listener = r => { Console.WriteLine($"t={r.X} rabbits={r.Y[0]}, foxes={r.Y[1]}"); }
            };

            MultiFunctionMath.IntegrateOde(lkRhs, 0.0, new double[] { 20.0, 10.0 }, 50.0, lkSettings);

            Func <double, IReadOnlyList <double>, IReadOnlyList <double> > rhs1 = (x, u) => {
                return(new double[] { u[1], -u[0] });
            };
            MultiOdeSettings settings1 = new MultiOdeSettings()
            {
                EvaluationBudget = 100000
            };
            MultiOdeResult result1 = MultiFunctionMath.IntegrateOde(
                rhs1, 0.0, new double[] { 0.0, 1.0 }, 500.0, settings1
                );
            double s1 = MoreMath.Sqr(result1.Y[0]) + MoreMath.Sqr(result1.Y[1]);

            Console.WriteLine($"y({result1.X}) = {result1.Y[0]}, (y)^2 + (y')^2 = {s1}");
            Console.WriteLine($"Required {result1.EvaluationCount} evaluations.");

            Func <double, double, double> rhs2 = (x, y) => - y;
            OdeSettings settings2 = new OdeSettings()
            {
                EvaluationBudget = 100000
            };
            OdeResult result2 = FunctionMath.IntegrateConservativeOde(
                rhs2, 0.0, 0.0, 1.0, 500.0, settings2
                );
            double s2 = MoreMath.Sqr(result2.Y) + MoreMath.Sqr(result2.YPrime);

            Console.WriteLine($"y({result2.X}) = {result2.Y}, (y)^2 + (y')^2 = {s2}");
            Console.WriteLine($"Required {result2.EvaluationCount} evaluations");

            Console.WriteLine(MoreMath.Sin(500.0));
        }
示例#4
0
        public void OdeCatenary()
        {
            // The equation of a catenary is
            //  \frac{d^2 y}{dx^2} = \sqrt{1.0 + \left(\frac{dy}{dx}\right)^2}
            // with y(0) = 1 and y'(0) = 0 and solution y = \cosh(x)

            Func <double, IReadOnlyList <double>, IReadOnlyList <double> > rhs = (double t, IReadOnlyList <double> y) =>
                                                                                 new double[] { y[1], MoreMath.Hypot(1.0, y[1]) };

            MultiOdeResult result = MultiFunctionMath.IntegrateOde(rhs, 0.0, new double[] { 1.0, 0.0 }, 2.0);

            Assert.IsTrue(TestUtilities.IsNearlyEqual(result.Y[0], Math.Cosh(2.0)));
        }
示例#5
0
        public void OdeOrbit2()
        {
            Func <double, IReadOnlyList <double>, IReadOnlyList <double> > rhs = (double t, IReadOnlyList <double> r) => {
                double d  = MoreMath.Hypot(r[0], r[2]);
                double d3 = MoreMath.Pow(d, 3);
                return(new ColumnVector(r[1], -r[0] / d3, r[3], -r[2] / d3));
            };

            foreach (double e in TestUtilities.GenerateUniformRealValues(0.0, 1.0, 8))
            {
                Console.WriteLine(e);

                ColumnVector r0 = new ColumnVector(1.0 - e, 0.0, 0.0, Math.Sqrt((1.0 + e) / (1.0 - e)));

                ColumnVector r1 = MultiFunctionMath.IntegrateOde(rhs, 0.0, r0, 2.0 * Math.PI).Y;

                Assert.IsTrue(TestUtilities.IsNearlyEqual(r0, r1, new EvaluationSettings()
                {
                    RelativePrecision = 1.0E-9
                }));
            }
        }
示例#6
0
        public void OdeLaneEmden()
        {
            // The Lane-Emden equations describe a simplified model of stellar structure.
            // See http://mathworld.wolfram.com/Lane-EmdenDifferentialEquation.html

            // Analytic solutions are known for the n=0, 1, and 5 cases.

            int n = 0;

            Func <double, IReadOnlyList <double>, IReadOnlyList <double> > rhs = (double x, IReadOnlyList <double> t) =>
                                                                                 new ColumnVector(
                t[1], -MoreMath.Pow(t[0], n) - 2.0 * (x == 0.0 ? 0.0 : t[1] / x)
                );

            ColumnVector t0 = new ColumnVector(1.0, 0.0);

            double x1 = 2.0;

            n = 0;
            MultiOdeResult result = MultiFunctionMath.IntegrateOde(rhs, 0.0, t0, x1);

            Assert.IsTrue(TestUtilities.IsNearlyEqual(result.Y[0], 1.0 - MoreMath.Sqr(x1) / 6.0));
            Console.WriteLine(result.EvaluationCount);

            n      = 1;
            result = MultiFunctionMath.IntegrateOde(rhs, 0.0, t0, x1);
            Assert.IsTrue(TestUtilities.IsNearlyEqual(result.Y[0], MoreMath.Sin(x1) / x1));
            Console.WriteLine(result.EvaluationCount);

            n      = 5;
            result = MultiFunctionMath.IntegrateOde(rhs, 0.0, t0, x1);
            Assert.IsTrue(TestUtilities.IsNearlyEqual(result.Y[0], 1.0 / Math.Sqrt(1.0 + MoreMath.Sqr(x1) / 3.0)));
            Console.WriteLine(result.EvaluationCount);

            // For all of these cases, the initial step fails until it gets very small, then it increases again.
            // Look into why this is.
        }
示例#7
0
        public void OdeLRC()
        {
            // Demanding zero net voltage across L, R, and C elements in series gives
            //   Q / C + \dot{Q} R + \ddot{Q} L = 0
            // This is a second order linear ODE with constant coefficients, i.e.
            // universal damped harmonic oscillator.

            // Characteristic equation r^2 L + R r + r / C = 0 with solutions
            //   r = \frac{-R \pm \sqrt{R^2 - 4 L / C}{2L}. Define
            // t_0 = \sqrt{RC} = 1 / w_0 and t_1 = 2 L / R = 1 / w_1.

            // If t_0 < t_1 the discriminant is negative and
            //   r = - w_0 \pm i w
            // where w = \sqrt{ w_0^2 - w_1^2 } so
            //   Q = e^{-w_0 t} \left[ A cos(w t) + B sin(w t) \right]
            // We get damped oscillatory behavior.

            // If t_0 > t_1 the discriminant is positive and
            //   r = \sqrt{ w_1^2 - w_0^2 } \pm w_1
            // so
            //   Q = A e^{-w_a t} + B^{-w_b t}
            // We get purely damped behavior.

            double q0  = 1.0;
            double qp0 = 0.0;

            foreach (double L in TestUtilities.GenerateRealValues(0.1, 10.0, 4, 1))
            {
                foreach (double R in TestUtilities.GenerateRealValues(0.1, 1.0, 4, 2))
                {
                    foreach (double C in TestUtilities.GenerateRealValues(0.1, 1.0, 4, 3))
                    {
                        Func <double, IReadOnlyList <double>, IReadOnlyList <double> > rhs = (double x, IReadOnlyList <double> y) => {
                            double q = y[0]; double qp = y[1];
                            return(new double[] {
                                qp,
                                (0.0 - q / C - R * qp) / L
                            });
                        };

                        double t0 = Math.Sqrt(L * C);
                        double w0 = 1.0 / t0;

                        double t1 = 2.0 * L / R;
                        double w1 = 1.0 / t1;

                        double t = 4.0;
                        double qt;
                        if (t0 < t1)
                        {
                            double w = Math.Sqrt(w0 * w0 - w1 * w1);

                            double A = q0;
                            double B = (qp0 + w1 * q0) / w;

                            qt = (A * Math.Cos(w * t) + B * Math.Sin(w * t)) * Math.Exp(-w1 * t);
                        }
                        else
                        {
                            double w  = Math.Sqrt(w1 * w1 - w0 * w0);
                            double wa = w1 + w;
                            double wb = w0 * w0 / wa;

                            double A = (wb * q0 + qp0) / (2.0 * w);
                            double B = (wa * q0 + qp0) / (2.0 * w);

                            qt = -A *Math.Exp(-wa *t) + B * Math.Exp(-wb * t);
                        }
                        Console.WriteLine(qt);

                        MultiOdeResult result = MultiFunctionMath.IntegrateOde(rhs, 0.0, new double[] { q0, qp0 }, t);
                        Console.WriteLine(result.Y[0]);

                        Console.WriteLine(result.EvaluationCount);

                        Assert.IsTrue(TestUtilities.IsNearlyEqual(qt, result.Y[0], result.Settings));
                    }
                }
            }
        }
示例#8
0
        public void OdeEuler()
        {
            // Euler's equations for rigid body motion (without external forces) are
            //   I_1 \dot{\omega}_1  = (I_2 - I_3) \omega_2 \omega_3
            //   I_2 \dot{\omega}_2  = (I_3 - I_1) \omega_3 \omega_1
            //   I_3 \dot{\omega}_3  = (I_1 - I_2) \omega_1 \omega_2
            // where \omega's are rotations about the principal axes and I's are the moments
            // of inertia about those axes. The rotational kinetic energy
            //   I_1 \omega_1^2 + I_2 \omega_2^2 + I_3 \omega_3^2 = 2 E
            // and angular momentum
            //   I_1^2 \omega_1^2 + I_2^2 \omega_2^2 + I_3^2 \omega_3^2 = L^2
            // are conserved quantities.

            // Landau & Lifshitz, Mechanics (3rd edition) solves these equations.

            // Note
            //   (\cn u)' = - (\sn u)(\dn u)
            //   (\sn u)' = (\dn u)(\cn u)
            //   (\dn u)' = -k^2 (\cn u)(\sn u)
            // So a solution with \omega_1 ~ \cn, \omega_2 ~ \sn, \omega_3 ~ \dn
            // would seem to work, if we can re-scale variables to eliminate factors.
            // In fact, this turns out to be the case.

            // Label axis so that I_3 > I_2 > I_1. If L^2 > 2 E I_2, define
            //   v^2 = \frac{(I_3 - I_2)(L^2 - 2E I_1)}{I_1 I_2 I_3}
            //   k^2 = \frac{(I_2 - I_1)(2E I_3 - L^2)}{(I_3 - I_2)(L^2 - 2E I_1)}
            //   A_1^2 = \frac{2E I_3 - L^2}{I_1 (I_3 - I_1)}
            //   A_2^2 = \frac{2E I_3 - L^2}{I_2 (I_3 - I_2)}
            //   A_3^2 = \frac{L^2 - 2E I_1}{I_3 (I_3 - I_1)}
            // (If L^2 < 2 E I_2, just switch subscripts 1 <-> 3). Then
            //   \omega_1 = A_1 \cn (c t, k)
            //   \omega_2 = A_2 \sn (c t, k)
            //   \omega_3 = A_3 \dn (c t, k)
            // Period is complete when v T = 4 K.

            ColumnVector I = new ColumnVector(3.0, 4.0, 5.0);

            Func <double, IReadOnlyList <double>, IReadOnlyList <double> > rhs = (double t, IReadOnlyList <double> w) => {
                return(new ColumnVector((I[1] - I[2]) * w[1] * w[2] / I[0], (I[2] - I[0]) * w[2] * w[0] / I[1], (I[0] - I[1]) * w[0] * w[1] / I[2]));
            };

            ColumnVector w0 = new ColumnVector(6.0, 0.0, 1.0);

            // Determine L^2 and 2 E
            double L2 = EulerAngularMomentum(I, w0);
            double E2 = EulerKineticEnergy(I, w0);

            // As Landau points out, these inequalities should be ensured by the definitions of E and L.
            Debug.Assert(E2 * I[0] < L2);
            Debug.Assert(L2 < E2 * I[2]);

            double v, k;
            Func <double, ColumnVector> sln;

            if (L2 > E2 * I[1])
            {
                v = Math.Sqrt((I[2] - I[1]) * (L2 - E2 * I[0]) / I[0] / I[1] / I[2]);
                k = Math.Sqrt((I[1] - I[0]) * (E2 * I[2] - L2) / (I[2] - I[1]) / (L2 - E2 * I[0]));
                ColumnVector A = new ColumnVector(
                    Math.Sqrt((E2 * I[2] - L2) / I[0] / (I[2] - I[0])),
                    Math.Sqrt((E2 * I[2] - L2) / I[1] / (I[2] - I[1])),
                    Math.Sqrt((L2 - E2 * I[0]) / I[2] / (I[2] - I[0]))
                    );
                sln = t => new ColumnVector(
                    A[0] * AdvancedMath.JacobiCn(v * t, k),
                    A[1] * AdvancedMath.JacobiSn(v * t, k),
                    A[2] * AdvancedMath.JacobiDn(v * t, k)
                    );
            }
            else
            {
                v = Math.Sqrt((I[0] - I[1]) * (L2 - E2 * I[2]) / I[0] / I[1] / I[2]);
                k = Math.Sqrt((I[1] - I[2]) * (E2 * I[0] - L2) / (I[0] - I[1]) / (L2 - E2 * I[2]));
                ColumnVector A = new ColumnVector(
                    Math.Sqrt((L2 - E2 * I[2]) / I[0] / (I[0] - I[2])),
                    Math.Sqrt((E2 * I[0] - L2) / I[1] / (I[0] - I[1])),
                    Math.Sqrt((E2 * I[0] - L2) / I[2] / (I[0] - I[2]))
                    );
                sln = t => new ColumnVector(
                    A[0] * AdvancedMath.JacobiDn(v * t, k),
                    A[1] * AdvancedMath.JacobiSn(v * t, k),
                    A[2] * AdvancedMath.JacobiCn(v * t, k)
                    );
            }

            Debug.Assert(k < 1.0);

            double T = 4.0 * AdvancedMath.EllipticK(k) / v;

            int listenerCount         = 0;
            MultiOdeSettings settings = new MultiOdeSettings()
            {
                Listener = (MultiOdeResult q) => {
                    listenerCount++;

                    // Verify that energy and angular momentum conservation is respected
                    Assert.IsTrue(TestUtilities.IsNearlyEqual(EulerKineticEnergy(I, q.Y), E2, q.Settings));
                    Assert.IsTrue(TestUtilities.IsNearlyEqual(EulerAngularMomentum(I, q.Y), L2, q.Settings));

                    // Verify that the result agrees with the analytic solution
                    //ColumnVector YP = new ColumnVector(
                    //    A[0] * AdvancedMath.JacobiCn(v * q.X, k),
                    //    A[1] * AdvancedMath.JacobiSn(v * q.X, k),
                    //    A[2] * AdvancedMath.JacobiDn(v * q.X, k)
                    //);
                    Assert.IsTrue(TestUtilities.IsNearlyEqual(q.Y, sln(q.X), q.Settings));
                }
            };
            MultiOdeResult result = MultiFunctionMath.IntegrateOde(rhs, 0.0, w0, T, settings);

            Console.WriteLine(result.EvaluationCount);
            //MultiOdeResult r2 = NewMethods.IntegrateOde(rhs, 0.0, w0, T, settings);

            // Verify that one period of evolution has brought us back to our initial state
            Assert.IsTrue(TestUtilities.IsNearlyEqual(result.X, T));
            Assert.IsTrue(TestUtilities.IsNearlyEqual(result.Y, w0, settings));
            Assert.IsTrue(listenerCount > 0);
        }