Скачать книгу

(improved Euler)

       Predictor-corrector method.

      # TestFDMSchemes.py # # A catalogue of hand-crafted numerical methods for solving ODEs # # (C) Datasim Education BV 2021 # # Testing import math import numpy as np import FDMSchemes as fdm def func(t,y): return y*(1.0 - y) # logistic ode t = 0; y = 0.5 #initial time and value T = 4.0 #end of integration interval N = 4000 #number of divisions of [0,T] TOL = 1.0e-5 #for some method, a measure of the desired accuracy # y' = f(x,y) Explicit Euler method value = fdm.Euler(func,t,y,T,N,TOL) #scalar print(value) value = fdm.PredictorCorrector(func,t,y,T,N,TOL) #scalar print(value) value = fdm.RK4(func,t,y,T,N,TOL) #scalar print(value) y = 0.5 value = fdm.Heun(func,t,y,T,N,TOL) #scalar print(value) value = fdm.Ralston2(func,t,y,T,N,TOL) #scalar print(value)

      No doubt the code can be modified to make it more “pythonic” (whatever that means) and reduce the number of lines of code (at the possible expense of readability) if that is a requirement. We have also written these algorithms in C++11 as discussed in Duffy (2018).

       using value_type = double; using FunctionType = std::function<value_type (value_type)>; using FunctionType2 = std::function<value_type (value_type x, value_type y)>; class GeneralisedRiccati { private: FunctionType p, q, r; FunctionType2 n; public: GeneralisedRiccati(const FunctionType& P, const FunctionType& Q, const FunctionType& R, const FunctionType2& N) : p(P), q(Q), r(R), n(N) { } double operator() (double x, double y) { // Function object (functor) return p(x) + q(x)*y + r(x)*y*y + n(x, y); } };

      We can instantiate this class by calling its constructor, but we prefer to use a factory method (this is a design pattern) for added flexibility. Notice that we also use C++ smart pointers in order to avoid possible memory issues):

       std::shared_ptr<GeneralisedRiccati> CreateRiccati() { // Factory method, specific case const double P = 0.0; const double Q = -10.8; const double R = 0.5 * 2.44 * 2.44; const double eta = 0.96; const double lambda = 0.13;double A = 0.42; // Generalised Riccati auto p = [=](double x) {return P;};auto q = [=](double x) {return Q;}; auto r = [=](double x) {return R;}; auto n = [=](double x, double y) { return (lambda*y) / (eta - y); }; return std::shared_ptr<GeneralisedRiccati> (new GeneralisedRiccati (p, q, r, n)); }

      3.6.1 Finite Difference Schemes

       // Global arrays to hold (t, y) data for meshes of size dt and dt/2 // t and y arrays on mesh dt std::vector<double> tValues; std::vector<double> yValues; // t and y arrays on mesh dt/2 std::vector<double> t2Values; std::vector<double> y2Values; // 2nd order extrapolated values on mesh dt std::vector<double> yRichValues;

      The code for the schemes is:

      Some test code is:

       int main() { std::cout ≪ "How many steps (need many e.g. 300000?): "; std::cin ≫ NT; CurveEuler(); CurveRichardson(); std::cout ≪ "Value at T = " ≪ U ≪ " is " ≪ std::setprecision(7) ≪ yValues[yValues.size() - 1] ≪ ", press the ANY key " ≪ std::endl; std::cout ≪ "Richardson at T = " ≪ U ≪ " is " ≪RichValues[yRichValues.size() - 1] ≪ ", press the ANY key "; // Beautiful Excel output ExcelDriver xl; xl.MakeVisible(true); xl.CreateChart(tValues, yValues, std::string("Euler for Riccati")); xl.CreateChart (tValues, yRichValues, std::string("Richardson for Riccati")); return 0; }

       Case I: , .

       Case II: , .

      In general, the library needs approximately 100 function evaluations to reach this level of accuracy. The results for explicit Euler and the extrapolated scheme for cases I and II for NT = 300, 500, 1000 and 5000 are:

       Case I: (9.2746e-6, 1.113e-5), (1.0015e-5, 1.118e-5), (1.059e-5, 1.200e-5), (1.083e-5, 1.1206e-5).

       Case

Скачать книгу