from __future__ import division import numpy as np import math import matplotlib.pyplot as plt import matplotlib.animation as animation import matplotlib.patches as patches import random theta = 0.5 AU = (149.6e6 * 1000) # 149.6 million km, in meters. G = 6.67408e-11 #m^3 kg^-1 s^-2 fig1 = plt.figure() sim = fig1.add_subplot(111, aspect='equal') fig2 = plt.figure() quadt = fig2.add_subplot(111, aspect='equal') class Node: children = None mass = None center_of_mass = None bbox = None vx = vy = None def quad_insert(root, x, y, m): if root.mass is None: #when the root is empty, add the first particle root.mass = m root.center_of_mass = [x,y] return elif root.children is None: root.children = [None,None,None,None] old_quadrant = quadrant_of_particle(root.bbox, root.center_of_mass[0], root.center_of_mass[1]) if root.children[old_quadrant] is None: root.children[old_quadrant] = Node() root.children[old_quadrant].bbox = quadrant_bbox(root.bbox,old_quadrant) quad_insert(root.children[old_quadrant], root.center_of_mass[0], root.center_of_mass[1], root.mass) new_quadrant = quadrant_of_particle(root.bbox, x, y) if root.children[new_quadrant] is None: root.children[new_quadrant] = Node() root.children[new_quadrant].bbox = quadrant_bbox(root.bbox,new_quadrant) quad_insert(root.children[new_quadrant], x, y, m) root.center_of_mass[0] = (root.center_of_mass[0]*root.mass + x*m) / (root.mass + m) root.center_of_mass[1] = (root.center_of_mass[1]*root.mass + y*m) / (root.mass + m) root.mass = root.mass + m else: new_quadrant = quadrant_of_particle(root.bbox, x, y) if root.children[new_quadrant] is None: root.children[new_quadrant] = Node() root.children[new_quadrant].bbox = quadrant_bbox(root.bbox, new_quadrant) quad_insert(root.children[new_quadrant], x, y, m) root.center_of_mass[0] = (root.center_of_mass[0]*root.mass + x*m) / (root.mass + m) root.center_of_mass[1] = (root.center_of_mass[1]*root.mass + y*m) / (root.mass + m) root.mass = root.mass + m def display(root): if root.mass is None: return if root.children is not None: x = (root.bbox[0] + root.bbox[1]) / 2 y = (root.bbox[2] + root.bbox[3]) / 2 width = x-root.bbox[0] plt_node(root.bbox[0], root.bbox[2], width) plt_node(root.bbox[0], y, width) plt_node(x, root.bbox[2], width) plt_node(x, y, width) for i in xrange(4): if root.children[i] is not None: display(root.children[i]) else: quadt.scatter(root.center_of_mass[0], root.center_of_mass[1]) def integrate(particles): bodies = particles n = len(bodies) timestep = 24*3600 #one day years = 2 * 365 #how many Earth years that simulate for day in xrange(years): particles_force = {} root = Node() root.center_of_mass = [] root.bbox = find_root_bbox(bodies) for i in xrange(n): quad_insert(root, bodies[i][3], bodies[i][4], bodies[i][2]) for i in xrange(n): total_fx, total_fy = compute_force(root,bodies[i][3],bodies[i][4],bodies[i][2]) particles_force[bodies[i][0]] = (total_fx, total_fy) for i in xrange(n): fx, fy = particles_force[bodies[i][0]] bodies[i][5] += fx / bodies[i][2] * timestep bodies[i][6] += fy / bodies[i][2] * timestep bodies[i][3] += bodies[i][5] * timestep bodies[i][4] += bodies[i][6] * timestep sim.scatter(bodies[i][3], bodies[i][4], c=bodies[i][1]) display(root) quadt.scatter(root.center_of_mass[0], root.center_of_mass[1], c='red', marker='x') def compute_force(root,x,y,m): if root.mass is None: return 0, 0 if root.center_of_mass[0] == x and root.center_of_mass[1] == y and root.mass == m: return 0, 0 d = root.bbox[1]-root.bbox[0] r = distance(x,y, root.center_of_mass[0], root.center_of_mass[1]) if d/r < theta or root.children is None: return force(m, x, y, root.mass, root.center_of_mass[0], root.center_of_mass[1]) else: fx = 0.0 fy = 0.0 for i in xrange(4): if root.children[i] is not None: fx += compute_force(root.children[i],x,y,m)[0] fy += compute_force(root.children[i],x,y,m)[1] return fx, fy ################################################# SUPPORTING FUNCTION ############################################################## def force(m, x, y, mcm, xcm, ycm): d = distance(x, y, xcm, ycm) f = G*m*mcm/(d**2) dx = xcm - x dy = ycm - y angle = math.atan2(dy, dx) fx = math.cos(angle) * f fy = math.sin(angle) * f return fx, fy def distance(x1, y1, x2, y2): return math.sqrt((x2-x1)**2+(y2-y1)**2) def plt_node(x, y, width): quadt.add_patch(patches.Rectangle((x, y), width, width, fill = False)) def find_root_bbox(array): """ Create a suitable square boundary box for the input particles """ if len(array) == 0 or len(array) == 1: return None xmin, xmax, ymin, ymax = array[0][3], array[0][3], array[0][4], array[0][4] for i in xrange(len(array)): if array[i][3] > xmax: xmax = array[i][3] if array[i][3] < xmin: xmin = array[i][3] if array[i][4] > ymax: ymax = array[i][4] if array[i][4] < ymin: ymin = array[i][4] if xmax - xmin == ymax - ymin: return xmin, xmax, ymin, ymax elif xmax - xmin > ymax - ymin: return xmin, xmax, ymin, ymax+(xmax-xmin-ymax+ymin) else: return xmin, xmax+(ymax-ymin-xmax+xmin), ymin, ymax def quadrant_of_particle(bbox, x, y): """Return position of quadrant of the particle (x,y) """ if y >= (bbox[3] + bbox[2])/2: if x <= (bbox[1] + bbox[0])/2: return 0 else: return 1 else: if x >= (bbox[1] + bbox[0])/2: return 2 else: return 3 def quadrant_bbox(bbox,quadrant): """Return the coordinate of the quadrant """ x = (bbox[0] + bbox[1]) / 2 y = (bbox[2] + bbox[3]) / 2 #Quadrant 0: (xmin, x, y, ymax) if quadrant == 0: return bbox[0], x, y, bbox[3] #Quadrant 1: (x, xmax, y, ymax) elif quadrant == 1: return x, bbox[1], y, bbox[3] #Quadrant 2: (x, xmax, ymin, y) elif quadrant == 2: return x, bbox[1], bbox[2], y #Quadrant 3: (xmin, x, ymin, y) elif quadrant == 3: return bbox[0], x, bbox[2], y def data_from_file(filename, array): with open(filename) as f: for line in f: if line[0] == '#': continue else: name,color,m,x,y,vx,vy = line.split(',') array.append([name,color,float(m),float(x)*AU,float(y)*AU,float(vx)*1000,float(vy)*1000]) if __name__ == '__main__': filename = ('solar-system.txt') particles = [] data_from_file(filename, particles) #root = Node() #root.center_of_mass = [] #root.bbox = find_root_bbox(particles) #for i in xrange(len(particles)): # quad_insert(root, particles[i][3], particles[i][4], particles[i][2]) #print 'Boundary box: ',root.bbox #print 'Total mass: ',root.mass #print 'Coordinate of center of mass: ',root.center_of_mass #plt.scatter(root.center_of_mass[0], root.center_of_mass[1], c='r', marker='x', s=50) #print 'Theta: ', theta integrate(particles) plt.show()