TI-Nspire & Lua / 常微分方程式の数値解法 / 応用例 / 円制限三体問題 2 / Graphs アプリケーションで動かしてみる / Fehlberg 法

ここでも、L4 を原点に置き、太陽を廻轉中心とする廻轉座標に描く。

Fehlberg = class()
function Fehlberg:init(funcs, t0, inits, h, tol)
   self.funcs    = funcs
   self.t0       = t0
   self.inits    = inits
   self.h        = h
   self.step     = h
   self.numOfDiv = nil
   self.dim      = #funcs
   self.tol      = tol or 0.001

   self.Unpack = unpack or table.unpack
   self.Ln     = math.log
   self.Floor  = math.floor
   self.Abs    = math.abs
   self.Max    = math.max  

   self.floorB =
   function(num)
      if   (num > 0) and (num < 1) then return 2^self.Floor(self.Ln(num)/self.Ln(2))
      else                              return self.Floor(num)
      end
   end
   
   self.hNew =
   function(h, err, tol)
      if   err > tol then return 0.9 * h * (tol * h/(h * err))^(1/4)
      else                return h
      end
   end
   
   self.maxOfErr =
   function(listA, listB)
      local sute = {}
      for i = 1, #listA do
         sute[i] = self.Abs(listA[i] - listB[i])
      end
      return self.Max(self.Unpack(sute))
   end
   
   self.preCalc =
   function(numOfDiv)
      local f   = {{}, {}, {}, {}, {}, {}}
      local tmp = {{}, {}, {}, {}, {}}

      local inits4   = {}
      local preInits = {self.Unpack(self.inits)}
      local preT0    = self.t0
      local preH     = self.h/numOfDiv

      for i = 1, numOfDiv do
         for j = 1, self.dim do f[1][j] = self.funcs[j](preT0                 , self.Unpack(preInits)); tmp[1][j] = preInits[j] + preH * (1/4)    * f[1][j] end 
         for j = 1, self.dim do f[2][j] = self.funcs[j](preT0 + preH * (1/4)  , self.Unpack(tmp[1]))  ; tmp[2][j] = preInits[j] + preH * (1/32)   * (3         * f[1][j] + 9    * f[2][j]) end
         for j = 1, self.dim do f[3][j] = self.funcs[j](preT0 + preH * (3/8)  , self.Unpack(tmp[2]))  ; tmp[3][j] = preInits[j] + preH * (1/2197) * (1932      * f[1][j] - 7200 * f[2][j] + 7296        * f[3][j]) end
         for j = 1, self.dim do f[4][j] = self.funcs[j](preT0 + preH * (12/13), self.Unpack(tmp[3]))  ; tmp[4][j] = preInits[j] + preH            * ((439/216) * f[1][j] - 8    * f[2][j] + (3680/513)  * f[3][j] - (845/4104)  * f[4][j]) end
         for j = 1, self.dim do f[5][j] = self.funcs[j](preT0 + preH          , self.Unpack(tmp[4]))  ; tmp[5][j] = preInits[j] + preH            * ((-8/27)   * f[1][j] + 2    * f[2][j] - (3544/2565) * f[3][j] + (1859/4104) * f[4][j] - (11/40) * f[5][j]) end
         for j = 1, self.dim do f[6][j] = self.funcs[j](preT0 + preH * (1/2)  , self.Unpack(tmp[5])) end

         if numOfDiv == 1 then -- 内部分割数が 1 のときだけ 4 次の解を計算する。
            for j = 1, self.dim do inits4[j] = preInits[j] + preH * ((25/216) * f[1][j] + (1408/2565) * f[3][j] + (2197/4104) * f[4][j] - (1/5) * f[5][j]) end
         end
         for j = 1, self.dim do preInits[j] = preInits[j] + preH * ((16/135) * f[1][j] + (6656/12825) * f[3][j] + (28561/56430) * f[4][j] - (9/50) * f[5][j] + (2/55) * f[6][j]) end
            preT0 = preT0 + preH
         end
      
         return preT0, inits4, preInits
      end
   end
function Fehlberg:updateFE()
   self.numOfDiv   = 1
   local t, a4, a5 = self.preCalc(self.numOfDiv)                                 -- とりあえず元の刻み幅 (内部分割数 1) で 1 回だけ計算し、
   local err       = self.maxOfErr(a4, a5)  
   if err < self.tol then                                                        -- 誤差が許容範囲内であったら初期値を更新するが、
      self.t0, self.inits = t, a5
   else                                                                          -- 誤差が許容範囲外であったら、
      self.numOfDiv       = self.h/self.floorB(self.hNew(self.h, err, self.tol)) -- 新しい内部分割数を求めて、
      t, a4, a5           = self.preCalc(self.numOfDiv)                          -- その内部分割数で計算し直して初期値を更新する。
      self.t0, self.inits = t, a5 
   end
   return self
end
function Fehlberg:print()
   print(self.t0, table.concat(self.inits, "\t"), self.numOfDiv)
   return self
end


-- 確かめる。
do 

local reset =
function()
   local Sqrt   = math.sqrt
   local Pi     = math.pi
   local AU     = 149597870700                       -- 1 天文単位〔m〕
   local METER  = 1 / AU                             -- 1 メートル〔AU〕
   local YEAR   = 31557600                           -- 1 年〔秒〕 
   local SECOND = 1 / YEAR                           -- 1 秒〔年〕
   local Gc     = 6.67408e-11                        -- 万有引力定数〔m^3/(kg・s^2)〕
   local G      = Gc * (METER^3)/(SECOND^2)          -- 万有引力定数〔AU^3/(kg・year^2)〕
   local S      = 1.9884e30                          -- 太陽の質量〔kg〕
   local J      = 1.8986e27                          -- 木星の質量〔kg〕
   local R      = 5.2026                             -- 太陽と木星との距離〔AU〕
   local P      = 2 * Pi * Sqrt(R^3 / (G * (S + J))) -- 公転周期〔年〕
   local n      = 2 * Pi / P                         -- 平均運動〔1/年〕
   
   local function r  (X, Y) return Sqrt(X^2       + Y^2)   end
   local function rmj(X, Y) return Sqrt((R - X)^2 + Y^2)   end
   local function X  (xi)   return R / 2             + xi  end
   local function Y  (eta)  return Sqrt(3) * R / 2   + eta end
   
   local function xiDot    (t, xi, xiDot, eta, etaDot) return xiDot end 
   local function xiDotDot (t, xi, xiDot, eta, etaDot) return  2 * n * etaDot + n^2 * X(xi)  - G * S * X(xi)/r(X(xi), Y(eta))^3 + G * J * ((R - X(xi))/rmj(X(xi), Y(eta))^3 - 1/R^2) end
   local function etaDot   (t, xi, xiDot, eta, etaDot) return etaDot end
   local function etaDotDot(t, xi, xiDot, eta, etaDot) return -2 * n * xiDot  + n^2 * Y(eta) - G * S * Y(eta)/r(X(xi), Y(eta))^3 + G * J * (-Y(eta)/rmj(X(xi), Y(eta))^3) end
   local funcs = {xiDot, xiDotDot, etaDot, etaDotDot}
   
   local xi0     = 0.5
   local xiDot0  = 0
   local eta0    = -2
   local etaDot0 = 0
   local inits   = {xi0, xiDot0, eta0, etaDot0}
   
   local h   = 1/2^4
   local t0  = 0
   local tol = 1e-12
   
   local trojan = Fehlberg(funcs, t0, inits, h, tol)
   var.store("x", trojan.inits[1]) 
   var.store("y", trojan.inits[3])
   var.store("t", trojan.t0)
   
   return trojan
end

local trojan
function on.construction()
   trojan = reset()
   var.store  ("reset_off_on", 0)
   var.monitor("reset_off_on")
end
function on.varChange()
   local reset_off_on = var.recall("reset_off_on")
   if reset_off_on ==  1 then timer.start(0.01) end
   if reset_off_on ==  0 then timer.stop()      end
   if reset_off_on == -1 then on.construction() end
end
function on.timer()
   trojan:updateFE()
         --:print()
   var.store("x", trojan.inits[1])
   var.store("y", trojan.inits[3])
   var.store("t", trojan.t0)
end

end

 
.tns
Trojan8.tns - Google ドライブ