Wednesday, June 18, 2014

Rotational plane star simulation

Okay I from a past class in Astronomy.  I grabbed this program and tweaked it a bit.  I managed to add initial momentum into a given system, and I were attempting to simulate something of a galactic rotational plane (without expansion).    Several variables worth noting for changing the simulator.  L changes the distance between stars in general, while expanding the field of view.  Its worth noting that you'd likely in order to see the stars better when say expanding out by or in by powers of ten correspondingly changing the
variable pertaining the visible radius set for stars.

This is found on the line
Stars = Stars+[sphere(pos=(x,y,z), radius=10*r, color=col0)]

While I've tried using something of a centripetal acceleration for tangential motion of stars related to a given center of mass, the effort is off or even in this case, setting a larger scale mass (with zero initial momentum),
relative to a given star mass, I've still encountered some issues, namely, an expansion in the overall system.  I've managed to tweak, however, initial momentum of stars in general so that rotation is about as stable as I've seen.  My system is a bit slow in processing, so I couldn't remotely get close to something like our own galaxy in the number of stars, and likely one would be dealing with much greater length scales here, relative to the given length scales presented in L.
Adjusting the parameter cmass/6e10: vmags.append([vmag(cmass/6e10,rmag1)])

from visual import *
from time import clock
from random import random,uniform
import numpy
import math

# Sets up a bunch of masses and lets them move under the influence of their
# mutual gravity.
# Masses are put in random positions within a sphere originally. An additional
# force is applied which simulates an equal density distributed over the
# rest of the universe outside this initial sphere.


def randomdirection():
      # Generates random direction on sky.
      # RA (in radians)
      # Dec (in radians)
      ra = 2.0*math.pi*random()
      dec = math.acos(2.0*random()-1.0)-0.5*math.pi
      return ra,dec

def ranvec():
      # Generates a randomly orientated unit vector.
      theta, phi = randomdirection()
      z = math.sin(phi)
      x = math.cos(phi)*math.sin(theta)
      y = math.cos(phi)*math.cos(theta)
      vec = numpy.array([x,y,z])
      return vec


# Stars interacting gravitationally
# Program uses numpy arrays for high speed computations


Nstars = 100  # change this to have more or fewer stars

G = 6.7e-11 # Universal gravitational constant

# Typical values
Msun = 2E30
Rsun = 6.96e8
Rtrail = 2e8
L = 4e11
vsun = 0.9*sqrt(G*Msun/Rsun)
h0 = 2.5e-6 # Hubble's constant - expansion rate 2.5e-6 is good.

scene = display(title="Stars", width=1300, height=740,
                range=L, forward=(-1,-1,-1))


Stars = []
poslist = []
plist = []
mlist = []
rlist = []
p0 = 0.0*Msun*100000.0

for i in range(Nstars):
    vec = L*ranvec()*(random()**0.3333)
    x = vec[0]
    y = vec[1]
    z = vec[2]/1e4
    r = Rsun
    col0 = (uniform(0.7,1.0),uniform(0.7,1.0),
                  uniform(0.7,1.0))
    Stars = Stars+[sphere(pos=(x,y,z), radius=10*r, color=col0)]
    mass = Msun
    px = p0*uniform(-1,1)
    py = p0*uniform(-1,1)
    pz = p0*uniform(-1,1)
    poslist.append((x,y,z))
    plist.append((px,py,pz))
    mlist.append(mass)
    rlist.append(r)

pos = array(poslist)
p = array(plist)
m = array(mlist)
m.shape = (Nstars,1) # Numeric Python: (1 by Nstars) vs. (Nstars by 1)
radius = array(rlist)

vcm = sum(p)/sum(m) # velocity of center of mass
def centerofmass(m, pos):
    # find center of mass
##    print(m.shape)
##    print(pos.shape)
##    print(pos*m)
##    
##    print(sum(pos*m,axis=0))
    R = sum(pos*m,axis=0)/sum(m)
    return R

def rfromRvec(pos,R):
    return pos-R

def unitnorm(r2):
    v = []
    for x in r2:
        v.append([numpy.linalg.norm(x)])
    v = array(v)
    return r2/v

def tangentfromr2(r2):
      def orthogonalangle(x,y):
            return math.pi/2 + math.atan(y/x)
      def findtancoords(rxpyp,oalpha):
            return [rxpyp*math.cos(oalpha),
                    rxpyp*math.sin(oalpha)]
      def findphi(rxy,z):
            return math.atan(z/rxy)
      def findrxy(x,y):
            return (x**2+y**2)**.5
      def findrxyz(x,y,z):
            return (x**2+y**2+z**2)**.5
      def rotationphi(phi):
            m = [(math.cos(phi),math.sin(phi)),
                 (-math.sin(phi),math.cos(phi))]
            return numpy.array(m)
      def revrotationphi(phi):
            m = [(math.cos(phi),-math.sin(phi)),
                 (math.sin(phi),math.cos(phi))]
            return numpy.array(m)      
      def rotatecoords(x,z,R):
            xzcrd = [x,z]
            XZ = numpy.array(xzcrd)
            rXZ = numpy.dot(R,XZ)
            return rXZ

      
# to find tangent one solution, involves using rotated coordinate system
# on xz plane by phi where phi is represented by the angle of r2.
#  Here we then have under rotated coordinates a tangent vector given
# by a perpendicular on the 2 dimensional plane by pi/2, then covert
# computed unit tangent vector back to our original coordinate system.
# a little easier despite the fuss of coordinate transformations since
# computing a tangent on a 2 dimensional plane is a little less cumbersome.

      #first find r for xy plane
      x=r2[0];y=r2[1];z=r2[2]
      rxy = findrxy(x,y)
      theta = findphi(x,y)
      if theta < -math.pi:
            print(theta)

      phi = findphi(rxy,z)
      if phi <0:
            print(phi)
      Rxy = rotationphi(theta) 
      Rxz = rotationphi(phi)
      revRxz = revrotationphi(phi)
      revRxy = revrotationphi(theta)
      #phi positive is 1rst or 3rd quad
      #theta positive is 1rst or 3rd quad
      XpYp = rotatecoords(x,y,Rxy)
      #z = zp
      xp = XpYp[0]; yp = XpYp[1]; zp = z;
      XppZpp = rotatecoords(xp,zp,Rxz)
      xpp = XppZpp[0]; zpp = XppZpp[1]; ypp = yp
      # under coordinate rotations ypp = yp,
      
##      orthetap = 1*math.pi/2
      #here a twice rotated coordinate system puts the zpp at zero, and likewise ypp should be zero)
      rxppypp = findrxy(xpp,ypp)
      typp, txpp = (xpp,ypp)
##      tangentcoords = findtancoords(rxppypp, orthetap)
##      txpp,typp = tangentcoords
      if typp < 0:
            print(typp)
##      typp = abs(typp); txpp = abs(txpp)
      #convert txp, zp back to original coordinates, typ = ty
      Txpzp = rotatecoords(txpp,zpp,revRxz)
      txp = Txpzp[0]; typ = typp; tzp = Txpzp[1]
      Txy = rotatecoords(txp,typ,revRxy)
      tx = Txy[0]; ty = Txy[1]; tz=tzp
      return [tx,ty,tz]

def vmag(M,rmag):
      # M is the total mass, and rmag is the distance from center of total mass M to star
      G = 6.7e-11
      return (G*M/rmag)**.5

def angles(pos):
      #find spherical coordinate angles
      c = pos[:,1]/pos[:,0]
      d = (pos[:,0]**2 + pos[:,1]**2)**.5
      e = pos[:,2]/d
      f = []; g = [];
      i = 0
      for x in c:
            f.append([math.atan(x),math.atan(e[i])])
            i += 1

      return numpy.array(f)

def finddcoords(rxy,oalpha):
      r2 = []
      for oalphai in oalpha:
            theta,phi = oalphai
            rx = rxy*math.sin(theta)*math.cos(phi)
            ry = rxy*math.sin(theta)*math.sin(phi)
            rz = rxy*math.cos(theta)
            r2.append([rx,ry,rz])
      return numpy.array(r2)

def getCMlist(r2,m):
      magnr2 = (r2[:,0]**2+r2[:,1]**2+r2[:,2]**2)**.5
      i = 0;Mlist =[];
      for m2 in magnr2:
            n=0;M=0
            for m2a in magnr2:
                  if not i == n:
                        if m2a < m2 or m2a == m2:
                              M += m[n]
                  n+=1
            Mlist.append(M)
            i+=1
      return numpy.array(Mlist)
                                    
      
m[0] = sum(m)/1
cmass = sum(m)
R = centerofmass(m, pos)
print('center of mass:')
print(R)
pos[0] = R
R = centerofmass(m, pos)
M = sum(m)
#Rsun in the direction of r
ang = angles(pos)
rSunarr = finddcoords(Rsun,ang)
r2 = rfromRvec(pos,R) + rSunarr
Mlist = getCMlist(r2,m)
print(r2)
r2n = unitnorm(r2)
print(r2n)
tpos = []
vmags = []
itr = 0
print('mlist:')
print(Mlist)
for r2i in r2n:
      txyz = tangentfromr2(r2i)
      tpos.append(txyz)
      x = r2[itr,0]; y=r2[itr,1]; z=r2[itr,2];
      rmag1 = (x**2+y**2+z**2)**.5
##      vmags.append([vmag(cmass/6e10,rmag1)])
      vmags.append([vmag(Mlist[itr],rmag1)])
      itr+=1
tposa = array(tpos)
print(tposa)
vmagsa = array(vmags)
print(vmags)
vs = tposa*vmagsa
print(vs)

p = p-m*vcm # make total initial momentum equal zero
p = vs*Msun
p[0] = [0,0,0]



print(p)
dt = 100.0
Nsteps = 0
pos = pos+(p/m)*(dt/2.) # initial half-step
time = clock()
Nhits = 0

while 1:
    rate(10000)

    L *= 1.0+h0*dt
    con = 0.9*G*Nstars*Msun/(L*L*L)# strength of force to allow for external mass
    
    # Compute all forces on all stars
    try:  # numpy
        r = pos-pos[:,newaxis] # all pairs of star-to-star vectors
        for n in range(Nstars):
            r[n,n] = 1e6  # otherwise the self-forces are infinite
        rmag = sqrt(add.reduce(r*r,-1)) # star-to-star scalar distances
        hit = less_equal(rmag,radius+radius[:,newaxis])-identity(Nstars)
        hitlist = sort(nonzero(hit.flat)[0]).tolist() # 1,2 encoded as 1*Nstars+2
        F = G*m*m[:,newaxis]*r/rmag[:,:,newaxis]**3 # all force pairs
    except: # old Numeric
        r = pos-pos[:,newaxis] # all pairs of star-to-star vectors
        for n in range(Nstars):
            r[n,n] = 1e6  # otherwise the self-forces are infinite
        rmag = sqrt(add.reduce(r*r,-1)) # star-to-star scalar distances
        hit = less_equal(rmag,radius+radius[:,newaxis])-identity(Nstars)
        hitlist = sort(nonzero(hit.flat)) # 1,2 encoded as 1*Nstars+2
        F = G*m*m[:,newaxis]*r/rmag[:,:,newaxis]**3 # all force pairs
        
    for n in range(Nstars):
        F[n,n] = 0  # no self-forces
    p = p+sum(F,1)*dt+pos*con*dt*m

    # Having updated all momenta, now update all positions         
    pos = pos+(p/m)*dt

    # Expand universe
    #pos *= 1.0+h0*dt

    # Update positions of display objects; add trail
    for i in range(Nstars):
        Stars[i].pos = pos[i]

    # If any collisions took place, merge those stars
    for ij in hitlist:
        i, j = divmod(ij,Nstars) # decode star pair
        if not Stars[i].visible: continue
        if not Stars[j].visible: continue
        # m[i] is a one-element list, e.g. [6e30]
        # m[i,0] is an ordinary number, e.g. 6e30
        newpos = (pos[i]*m[i,0]+pos[j]*m[j,0])/(m[i,0]+m[j,0])
        newmass = m[i,0]+m[j,0]
        newp = p[i]+p[j]
        newradius = Rsun*((newmass/Msun)**(1./3.))
        iset, jset = i, j
        if radius[j] > radius[i]:
            iset, jset = j, i
        Stars[iset].radius = newradius
        m[iset,0] = newmass
        pos[iset] = newpos
        p[iset] = newp
        Stars[jset].visible = 0
        p[jset] = vector(0,0,0)
        m[jset,0] = Msun*1E-30  # give it a tiny mass
        Nhits = Nhits+1
        pos[jset] = (10.*L*Nhits, 0, 0) # put it far away

    Nsteps += 1

No comments:

Post a Comment

Oblivion

 Between the fascination of an upcoming pandemic ridden college football season, Taylor Swift, and Kim Kardashian, wildfires, crazier weathe...