C N-body calculation using the modified Euler method. C Uses O(N^2) processors to calculate accelerations. C C Data arrays (position, velocity, acceleration, mass) C are (N, N, 3), organized: C C ________________________________ C /| z /_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/| C (c) y /_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ | C / x /_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ /| C 1 |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|//| C | |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|//| C | |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|//| C P |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|//| C a |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|//| C r |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|//| C t |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|//| C i |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|//| C c |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|//| C l |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|//| C e |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|//| C |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|//| C (i) |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|//| C | |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|/// C v |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|// C N |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|/ C C 1 --- Particle (j) --> N C C The parameter N and common block /NBODYPE/ C with arrays C C X0, X1 - Current, next positions C V0, V1 - velocities C MI,GJ - mass(i), G*mass(j) C i,j,c - array coordinates C diag - diagonal entries C act - active in acceleration C C and common block /NBODYFE/ with C C NB - integer <= N, actual no. of bodies C G - real, the gravitational constant C C are defined in the file nb2mode.h C C System wide conventions regarding data: C C Entries (i, :, :) - data for particle i C except GJ (:, j, :) = G*mass(j) C C This is intentionally redundant, and the programs C perform many redundant calculations. C C File: nb2mode.fcm Header: nb2mode.h C------- setup() --------------------------------------- C Initialize coordinates, logical flags. subroutine setup() INCLUDE 'nb2mode.h' i = SPREAD(SPREAD([1:N], 2, N), 3, 3) j = SPREAD(SPREAD([1:N], 1, N), 3, 3) c = SPREAD(SPREAD([1:3], 1, N), 2, N) diag = (i == j) actv = .FALSE. end subroutine setup C------- init() ---------------------------------------- C Load initial positions, velocities, also masses, C gravitational const. All are read from logical C unit 10. Replicates masses over x,y,z. C Data for particle i are left in (i, :, :) . subroutine init() INCLUDE 'nb2mode.h' integer k real mx X0 = 0.0 ! For unused particles V0 = 0.0 do, k = 1, NB read (10, *) X0(k, 1, 1), X0(k, 1, 2), X0(k, 1, 3) end do do, k = 1, NB read (10, *) V0(k, 1, 1), V0(k, 1, 2), V0(k, 1, 3) end do M = 0.0 ! For unused particles do, k = 1, NB read (10, *) mx MI(k, 1, :) = mx end do read (10, *) G X0 = SPREAD(X0(:, 1, :), 2, N) V0 = SPREAD(V0(:, 1, :), 2, N) MI = SPREAD(MI(:, 1, :), 2, N) end subroutine init C------- mkast(nx, ar, am) ----------------------------- C Insert nx asteroids at radius ar with total mass am. C This assumes that an orbit of radius 1 about origin C corresponds to speed 2*pi (e.g., au-earth-yr units). C Data for particle i are left in (i, :, :) . subroutine mkast(nx, ar, am) integer nx ! Number of new asteroids real ar, am ! Orbit radius, total mass INCLUDE 'nb2mode.h' real, parameter:: TWOPI = 6.2831853 real, array(1:N, 1:N, 1:3) :: sx, cx, arg real spd integer nn print 21, nx, ar, am 21 FORMAT (" Making ", I4, " asteroids, radius ", * F10.5, ", mass ", F8.5) nn = NB + nx spd = TWOPI/sqrt(ar) where ( (NB < i) .and. (i <= nn) ) arg = (TWOPI*(i - NB))/nx sx = sin(arg) cx = cos(arg) end where where ( (NB < i) .and. (i <= nn) .and. (c == 1) ) X0 = cx V0 = -sx end where where ( (NB < i) .and. (i <= nn) .and. (c == 2) ) X0 = sx V0 = cx end where where ( (NB < i) .and. (i <= nn) .and. (c == 3) ) X0 = 0.0 V0 = 0.0 end where where ( (NB < i) .and. (i <= nn) ) X0 = X0*ar V0 = V0*spd MI = am/nx end where NB = nn end subroutine mkast C------- print_state(PX) ------------------------------- C Print current state of system according to PX. C See comments for main program for PX settings. subroutine print_state(PX) integer PX ! Print control INCLUDE 'nb2mode.h' integer k, np np = NB if ( (PX >= 0) .and. (PX < NB) ) np = PX print 21 21 FORMAT (/ " x y z ", * " vx vy vz m") do, k = 1, np print 22, k, X0(k,1,1), X0(k,1,2), X0(k,1,3), * V0(k,1,1), V0(k,1,2), V0(k,1,3), MI(k,1,1) end do 22 FORMAT (1X, I3, 6F10.5, E10.2) end subroutine print_state C------- print_x(ax, nx, cx) ------------------------------- C Print ax(1:nx, 1:nx, cx), nx <= 10 (for testing) subroutine print_x(ax, nx, cx) INCLUDE 'nb2mode.h' real, array(1:N, 1:N, 1:3) :: ax CMPF ONDPU ax CMF$ LAYOUT ax(,,) integer nx, cx integer u, lm lm = min(nx, 10) do, u = 1, lm print 21, ax(u, 1:lm, cx) 21 FORMAT ( 10(1X : E9.2 :) ) end do end subroutine print_x C------- Grav(Xf) -------------------------------------- C Calculate accelerations (Newtonian gravitation) C Uses completely parallel calculation, ignoring C anti-symmetry of force. Acceleration for body i is C left in (i, :, :) of result. C Acceleration on body i due to body j is calculated C in entries (i, j, :) . function Grav(Xf) INCLUDE 'nb2mode.h' real, array(1:N, 1:N, 1:3) :: Xf, Grav CMPF ONDPU Xf CMF$ LAYOUT Xf(,,) real, array(1:N, 1:N, 1:3) :: Ac, D, R, Xj integer k, lm C Spread Xf across j from diagonal: where ( diag ) Xj = Xf elsewhere Xj = -1E30 ! Enormously negative end where Xj = SPREAD(MAXVAL(Xj, 1), 1, N) C Now Xj(i,j,:) = X0(j,j,:) Ac = 0.0 where ( actv ) D = Xj - Xf ! Displacement elsewhere D = 1.0 ! Protects from div by 0 endwhere R = sqrt(SPREAD(SUM(D*D, 3), 3, 3)) where ( actv ) Ac = GJ*D/R**3 Grav = SPREAD(SUM(Ac, 2), 2, N) end function Grav C------- modeul(h, ns) -------------------------------- C Perform ns steps of modified Euler with time-step h. C X0, V0 are taken to be current state, and are updated C with final state. X1, V1 are used in the process. C Note that X's, V's are calculated redundantly. subroutine modeul(h, ns) real h integer ns INCLUDE 'nb2mode.h' INTERFACE function Grav(Xf) INCLUDE 'nb2mode.h' real, array(1:N, 1:N, 1:3) :: Xf, Grav CMPF ONDPU Xf CMF$ LAYOUT Xf(,,) END INTERFACE real h2 integer k h2 = 0.5*h ! h/2, for convenience do k = 1, ns ! ns steps X1 = X0 + h2*V0 ! Half-way positions V1 = V0 + h2*Grav(X0) ! Half-way velocities X0 = X0 + h*V1 ! Next positions V0 = V0 + h*Grav(X1) ! Next velocities end do end subroutine modeul C------- nb2mode ------------------------------- C Main program: reads problem and control information C from data file named by user, performs and times C calculation. Print control: C 0 - just performance data C < 0 - all C > 0 - number given program nb2mode INCLUDE 'nb2mode.h' integer ns, np, nv, k, PX real dt, ar, am character*50 fnm print '(" Data file> "$)' ! Request data file read *, fnm print '(" Print ctl> "$)' ! Request print control read *, PX C Begin initialization open(10, file=fnm, status = "OLD") read (10, *) NB call setup() ! Setup geometric structure call init() ! Read data for individual bodies C Read "asteroid" descriptions from file read (10, *) ns do while ( ns > 0 ) read (10, *) ar, am call mkast(ns, ar, am) read (10, *) ns end do read (10, *) ns, np, dt ! Integration parameters close(10) print 21, NB, N 21 FORMAT (/, X, I4, " bodies, geometry allows ", I4) print 22, G, dt 22 FORMAT (" G = ", E13.6, " delta-t = ", E12.5) print *, " simulated time = 0" call print_state(PX) C Scale masses and distribute, set actv where ( diag ) GJ = G*MI elsewhere GJ = -1E30 ! Enormously negative end where GJ = SPREAD(MAXVAL(GJ, 1), 1, N) actv = (i <= NB) .and. (j <= NB) .and. .not. diag C np runs of ns steps, timestep dt do, k = 1, np call CM_timer_clear(1) call CM_timer_start(1) call modeul(dt, ns) call CM_timer_stop(1) call CM_timer_print(1) print 23, k*ns*dt 23 FORMAT (/ " simulated time = ", F9.3) call print_state(PX) end do end program nb2mode C******************************************************* C Data files: See nbmode.f C*******************************************************