program kepler
implicit none
integer,parameter :: kosuu=256
real,dimension(0:kosuu) :: t
real,dimension(2,0:kosuu) :: pos0,pos1,vel1,pos2,vel2,pos4,vel4
real,dimension(2) :: k1p,k1v,k2p,k2v,k3p,k3v,k4p,k4v
real :: h,epot,ekin,etot,l,e,a,b,tperiod,alpha
real :: r,dt,theta,theta_old
integer :: i
real :: x0,y0,vx0,vy0
real,parameter :: t_kaishi=0.0,t_shuryo=40.0
real,parameter :: pi=4.0*atan(1.0)
!
dt = (t_shuryo-t_kaishi)/real(kosuu)
do i = 0,kosuu
t(i) = t_kaishi+dt*real(i)
end do
! 初期条件 ! ここを変更すると、異なった軌道が得られる
x0 = 1.0 ! 位置 (x座標)
y0 = 0.0 ! (y座標)
vx0 = 0.2/sqrt(sqrt(x0**2+y0**2)) ! 速度 (x方向成分)
vy0 = 1.2/sqrt(sqrt(x0**2+y0**2)) ! (y方向成分)
! 全エネルギーの計算
epot = -1.0/sqrt(x0**2+y0**2) ! 重力ポテンシャル (太陽は原点にあるとしている)
ekin = 0.5*(vx0**2+vy0**2) ! 運動エネルギー
etot = epot+ekin ! 全力学的エネルギー
if (etot >= 0.0) then ! 全エネルギーが0以上だと楕円軌道にならない
write(*,*)"楕円軌道になりません!! 全エネルギーを小さくしてください!!"
stop
endif
! 与えた初期条件のもとに得られる楕円軌道を理論的に求める
h = abs(x0*vy0-y0*vx0) ! 2×面積速度 (太陽は原点にあるとしている)
l = h**2 ! 半直弦
e = sqrt(1.0+2.0*l*etot) ! 離心率
a = l/(1.0-e**2) ! 長半径
b = sqrt(a*l) ! 短半径
tperiod = 2.0*a*b*pi/h ! 公転周期
alpha = atan2(y0,x0) & ! 楕円軌道の長軸と
-acos((l/sqrt(x0**2+y0**2)-1.0)/e) ! x軸のなす角度
! 計算結果を出力するファイル名を指定する
open(10,file="kepler.txt")
write(10,'("# 離心率 = ",f10.8)')e
write(10,'("# 長半径 = ",f11.8)')a
write(10,'("# 短半径 = ",f11.8)')b
write(10,'("# 公転周期 = ",f12.8)')tperiod
write(10,'("# 長軸とx軸の角度 = ",f12.8)')alpha
! 理論的な計算
pos0(1,0) = x0
pos0(2,0) = y0
theta_old = atan2(y0,x0)
do i = 1,kosuu
theta = theoretically_updated_theta(theta_old,tperiod,dt,e,alpha)
r = l/(1.0+e*cos(theta-alpha))
pos0(1,i) = r*cos(theta)
pos0(2,i) = r*sin(theta)
theta_old = theta
end do
! オイラー法による計算
pos1(1,0) = x0
pos1(2,0) = y0
vel1(1,0) = vx0
vel1(2,0) = vy0
do i = 0,kosuu-1
vel1(:,i+1) = vel1(:,i)+dt*dvdt(pos1(1,i),pos1(2,i))
pos1(:,i+1) = pos1(:,i)+dt*dxdt(vel1(1,i),vel1(2,i))
end do
! ホイン法による計算
pos2(1,0) = x0
pos2(2,0) = y0
vel2(1,0) = vx0
vel2(2,0) = vy0
do i = 0,kosuu-1
k1v(:) = dt*dvdt(pos2(1,i),pos2(2,i))
k1p(:) = dt*dxdt(vel2(1,i),vel2(2,i))
!
k2v(:) = dt*dvdt(pos2(1,i)+k1p(1),pos2(2,i)+k1p(2))
k2p(:) = dt*dxdt(vel2(1,i)+k1v(1),vel2(2,i)+k1v(2))
!
vel2(:,i+1) = vel2(:,i)+0.5*(k1v(:)+k2v(:))
pos2(:,i+1) = pos2(:,i)+0.5*(k1p(:)+k2p(:))
end do
! (4次の) ルンゲ-クッタ法による計算
pos4(1,0) = x0
pos4(2,0) = y0
vel4(1,0) = vx0
vel4(2,0) = vy0
do i = 0,kosuu-1
k1v(:) = dt*dvdt(pos4(1,i),pos4(2,i))
k1p(:) = dt*dxdt(vel4(1,i),vel4(2,i))
!
k2v(:) = dt*dvdt(pos4(1,i)+0.5*k1p(1),pos4(2,i)+0.5*k1p(2))
k2p(:) = dt*dxdt(vel4(1,i)+0.5*k1v(1),vel4(2,i)+0.5*k1v(2))
!
k3v(:) = dt*dvdt(pos4(1,i)+0.5*k2p(1),pos4(2,i)+0.5*k2p(2))
k3p(:) = dt*dxdt(vel4(1,i)+0.5*k2v(1),vel4(2,i)+0.5*k2v(2))
!
k4v(:) = dt*dvdt(pos4(1,i)+k3p(1),pos4(2,i)+k3p(2))
k4p(:) = dt*dxdt(vel4(1,i)+k3v(1),vel4(2,i)+k3v(2))
!
vel4(:,i+1) = vel4(:,i)+(k1v(:)+2.0*k2v(:)+2.0*k3v(:)+k4v(:))/6.0
pos4(:,i+1) = pos4(:,i)+(k1p(:)+2.0*k2p(:)+2.0*k3p(:)+k4p(:))/6.0
end do
! 結果の表示
do i = 0,kosuu
write(10,'(9f16.8)')t(i),pos0(1:2,i),pos1(1:2,i),pos2(1:2,i),pos4(1:2,i)
end do
contains
function dvdt(x,y)
real,intent(in) :: x,y
real,dimension(2) :: dvdt
real :: r3i
r3i = 1.0/sqrt(x**2+y**2)**3
dvdt(1) = -x*r3i
dvdt(2) = -y*r3i
end function dvdt
function dxdt(vx,vy)
real,intent(in) :: vx,vy
real,dimension(2) :: dxdt
dxdt(1) = vx
dxdt(2) = vy
end function dxdt
function theoretically_updated_theta(theta_old,tperiod,dt,e,alpha)
real,intent(in) :: theta_old,tperiod,dt,e,alpha
real :: theoretically_updated_theta
real :: beta,tan_work
real :: dfdb,f,uhen,deltabeta
real,parameter :: very_small=1.e-6
integer :: n
integer,parameter :: nmax=999 ! ニュートン法の最大反復回数
tan_work = sqrt((1.0-e)/(1.0+e))*tan(0.5*(theta_old-alpha))
beta = atan(tan_work)
uhen = beta-0.5*e*sin(2.0*beta)+pi*dt/tperiod
! ここからニュートン法
newton: do n = 1,nmax
f = beta-0.5*e*sin(2.0*beta)-uhen
dfdb = 1.0-e*cos(2.0*beta)
deltabeta = f/dfdb
if (abs(deltabeta) <= very_small) exit newton
beta = beta-deltabeta
end do newton
tan_work = sqrt((1.0+e)/(1.0-e))*tan(beta)
theoretically_updated_theta = 2.0*atan(tan_work)+alpha
end function theoretically_updated_theta
end program kepler