Where is my nbody orbital code going wrong? (Something in the math?)

Viewed 80

In fortran, my nbody code worked flawlessly. When I translated it over to python I messed up somewhere. The code still successfully reads in a .txt file of bodies and still prints out a .txt file that records their position and velocity over each timestep. However, all my bodies seem to slingshot into the great unknown. I know this because when comparing the plots (using mathematica) I notice that my fortran Jupiter generated plot (First image below) is perfectly elliptical and stays within the given distances expected of Jupiter and my python generated plot (second image below) goes all the way out to a magnitude of 10^15 which is absurd. I'm assuming it's a simple fix in the math somewhere but I just can't seem to figure it out.

import numpy as np
import pandas as pd
import os
import math as mt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt

nbodies = int(input("Input the number of bodies:  "))
dt = int(input("Input the number of timesteps:  "))
ntimesteps = int(input("Max timestep:  "))
G = 6.6743E-11

def getFile():
    path = os.getcwd()
    global fileName
    fileName = input("What file?  ")
    inputFile = open(path + "\\" + str(fileName),"r")
    return(inputFile)

def hasNumbers(inputString):
    for char in inputString:
        if(char == '-'):
            return True
        try:
            float(char)
            return True
        except:
            return False
        
def readFile(inputFile):
    newdata = []
    data = []
    while True:
        line = inputFile.readline()
        #if the end of the file is reached
        if not line:
            break;
        rawdata = line.split(" ")
        for i in rawdata:
            if hasNumbers(i) == True:
                i = i.rstrip()
                newdata.append(i)
        if newdata:
            data.append(newdata)
        newdata = []
    inputFile.close()
    return(data)    

def emptyFile(fileName, datatype):
    path = os.getcwd()
    outfile = open(path + "\\" + fileName[:-4] + "_" + datatype + ".txt", "w")
    return(outfile)
           
inputFile = getFile()
Orbital = emptyFile(fileName, "Orbital")
nData = readFile(inputFile)
m = []
x = []
y = []
z = []
vx = []
vy = [] 
vz = []
for i in nData:
    if hasNumbers(i) == True:
        m.append(float(i[0]))
        x.append(float(i[1]))
        y.append(float(i[2]))
        z.append(float(i[3]))
        vx.append(float(i[4]))
        vy.append(float(i[5]))
        vz.append(float(i[6]))
mass = np.array(m)
xpos = np.array(x)
ypos = np.array(y)
zpos = np.array(z)
xvel = np.array(vx)
yvel = np.array(vy)
zvel = np.array(vz)
        

Fx = np.zeros(nbodies)
Fy = np.zeros(nbodies)
Fz = np.zeros(nbodies)


def CalcForce(axnew, aynew, aznew):
    axnew = np.zeros(nbodies)
    aynew = np.zeros(nbodies)
    aznew = np.zeros(nbodies)
    for i in range(nbodies):
        for j in range(nbodies): 
            if i!=j:
                xdiff = x[j] - x[i]
                ydiff = y[j] - y[i]
                zdiff = z[j] - z[i]
                r2 = (xdiff**2)+(ydiff**2)+(zdiff**2)
                C = (G*m[j]*m[i])/((r2)**(1.5))
                Fx[i] = Fx[i]+C*(xdiff)
                Fx[j] = Fx[j]-C*(xdiff)
                Fy[i] = Fy[i]+C*(ydiff)
                Fy[j] = Fy[j]-C*(ydiff)
                Fz[i] = Fz[i]+C*(zdiff)
                Fz[j] = Fz[j]-C*(zdiff)
                axnew[i] = np.divide(Fx[i],m[i])
                axnew[j] = np.divide(Fx[j],m[j])
                aynew[i] = np.divide(Fy[i],m[i])
                aynew[j] = np.divide(Fy[j],m[j])
                aznew[i] = np.divide(Fz[i],m[i])
                aznew[j] = np.divide(Fz[j],m[j])
            else:
                continue
    return axnew, aynew, aznew

axnew, aynew, aznew = CalcForce(axnew, aynew, aznew)

for k in range(ntimesteps):
    xpos = xpos + xvel * dt + 0.5 * axnew * dt**2
    ypos = ypos + yvel * dt + 0.5 * aynew * dt**2
    zpos = zpos + zvel * dt + 0.5 * aznew * dt**2
    axold = axnew
    ayold = aynew
    azold = aznew
    xvel = xvel + 0.5 * (axold + axnew) * dt
    yvel = yvel + 0.5 * (ayold + aynew) * dt
    zvel = zvel + 0.5 * (azold + aznew) * dt
    
    
    np.savetxt(Orbital, np.transpose([np.arange(1, nbodies+1), np.full(nbodies, k*dt), xpos, ypos, zpos]),fmt="%2i %20i %15.5E %15.5E %15.5E")

Above I have my newest version of the python code. The "CalcForce.axnew" called accelerations worked when there was no If statement for i!=j. It doesn't anymore and I'm naïve as to why. Removing it and going back to trying to return the accelerations just returns 0s when called outside the definition.

Here's useful input to copy to a .txt file. It includes 11 bodies from the Sun to a comet (including Pluto which I use for a good time comparison). It's done in all 3 cartesian coordinates based on each body's orbital inclination with total linear momentum conserved.

m         x         y       z     vx      vy      vz
1.988e30 -4.490e5  0.000  0.000  0.000 -1.600e1  -0.4247
3.285e23  5.790e10 0.000  0.000  0.000  4.701e4   5.772e3
4.867e24  1.080e11 0.000  0.000  0.000  3.496e4   2.077e3
5.972e24  1.494e11 0.000  0.000  0.000  2.980e4   0.000
6.390e23  2.280e11 0.000  0.000  0.000  2.399e4   0.775e3
1.898e27  7.783e11 0.000  0.000  0.000  1.306e4   0.299e3
5.683e26  1.434e12 0.000  0.000  0.000  9.671e3   0.421e3
8.681e25  2.871e12 0.000  0.000  0.000  6.799e3   0.091e3
1.024e26  4.495e12 0.000  0.000  0.000  5.427e3   0.168e3
1.309e22  7.376e12 0.000  0.000  0.000  4.532e3   1.399e3
2.200e14  5.300e12 0.000  0.000  0.000  0.951e3   0.309e3

Fortran Generated Plot for Jupiter

Python Generated Plot for Jupiter

My fortran code for comparison is

Module nbody
  integer, parameter:: n = 100
  integer:: nbodies, nTimesteps, i, k
  double precision:: m(n), x(n), y(n), z(n), vx(n), vy(n), vz(n)
  double precision:: axold(n), ayold(n), azold(n), axnew(n), aynew(n), aznew(n)
  double precision:: Fx(n), Fy(n), Fz(n)
  double precision:: dt, G = 6.6743D-11, r2, C, S = 1.989D30
  character:: infile*40, outfile*40
  character(len=20), parameter :: fmt0 = "(1X,I15)",fmt1 = "(1X,E30.10)",fmt2 = "(1X,E30.10,/)"
end module

Program Gravitas
  use nbody

  print*,'input the number of bodies'
  read*, nbodies
  print*, 'input filename'
  read*, infile
  print*, 'output filename'
  read*, outfile
  open(unit=42, file=infile, status='unknown')
  open(unit=43, file=outfile, status='unknown')

  
  read(42,*)
  Do i=1, nbodies
    read(42,*) m(i), x(i), y(i), z(i), vx(i), vy(i), vz(i)
  end do
  print*, 'input timestep(dt)'
  read*, dt
  print*, 'Tmax'
  read*, nTimesteps
  
  Do i=1, nbodies
    WRITE(43,fmt1,advance="no")real(k)*dt 
    WRITE(43,fmt0,advance="no")i 
    WRITE(43,fmt1,advance="no")x(i) 
    WRITE(43,fmt1,advance="no")y(i) 
    WRITE(43,fmt2,advance="no")z(i)   
  end do
  
  call calcforces
  
  Do k = 1, nTimesteps
    x = x + vx * dt + 0.5 * axnew * dt**2
    y = y + vy * dt + 0.5 * aynew * dt**2
    z = z + vz * dt + 0.5 * aznew * dt**2
    axold = axnew
    ayold = aynew
    azold = aznew
    call calcforces
    vx = vx + 0.5 * (axold + axnew) * dt
    vy = vy + 0.5 * (ayold + aynew) * dt
    vz = vz + 0.5 * (azold + aznew) * dt
    Do i = 1, nbodies
      WRITE(43,fmt1,advance="no")real(k)*dt 
      WRITE(43,fmt0,advance="no")i 
      WRITE(43,fmt1,advance="no")x(i) 
      WRITE(43,fmt1,advance="no")y(i) 
      WRITE(43,fmt2,advance="no")z(i) 
    end do

Subroutine calcforces
  use nbody
  Fx=0.0
  Fy=0.0
  Fz=0.0
  do i=1, nbodies
    do j=(i+1), (nbodies)
      xdiff = x(j)-x(i)
      ydiff = y(j)-y(i)
      zdiff = z(j)-z(i)
      r2 = (xdiff**2)+(ydiff**2)+(zdiff)**2
      C = (G*m(j)*m(i))/((r2)**1.5)
      Fx(i)=Fx(i)+C*(xdiff)
      Fx(j)=Fx(j)-C*(xdiff)
      Fy(i)=Fy(i)+C*(ydiff)
      Fy(j)=Fy(j)-C*(ydiff)
      Fz(i)=Fz(i)+C*(zdiff)
      Fz(j)=Fz(j)-C*(zdiff)
    end do
  end do

  axnew = Fx/m
  aynew = Fy/m
  aznew = Fz/m
end subroutine

0 Answers
Related