C N-body calculation using the modified Euler method, C which is globally 2nd-order. C Uses a ring organization to calculate accelerations. C C Data arrays (position, velocity, acceleration, mass) C are organized: C C 1 Particle number (i) --> N C +--------------------------------------------+ C | x | | C (c) y | | C v z | | C +--------------------------------------------+ C C The parameter N and common blocks /NBODYFE/, /NBODYPE/ with arrays C C X0, X1 - Current, next positions C V0, V1 - velocities C M, GM - mass, G*mass (redundant) C c, i - array coordinates C and C NB - integer <= N, actual no. of bodies C G - real, the gravitational constant C C are defined in the file nbmode.h C C File: nbmode.fcm Header: nbmode.h C------- setup() --------------------------------------- subroutine setup() INCLUDE 'nbmode.h' C Initialize coordinates c = SPREAD( [1:3], 2, N) i = SPREAD([1:N], 1, 3) 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. subroutine init() INCLUDE 'nbmode.h' integer k real mx X0 = 0.0 ! For unused particles V0 = 0.0 do, k = 1, NB read (10, *) X0(1, k), X0(2, k), X0(3, k) end do do, k = 1, NB read (10, *) V0(1, k), V0(2, k), V0(3, k) end do M = 0.0 ! For unused particles do, k = 1, NB read (10, *) mx M(:, k) = mx end do read (10, *) G 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). subroutine mkast(nx, ar, am) integer nx ! Number of new asteroids real ar, am ! Orbit radius, total mass INCLUDE 'nbmode.h' real, parameter:: TWOPI = 6.2831853 real, array(1:3, 1:N) :: 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 M = 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 'nbmode.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(1,k), X0(2,k), X0(3,k), * V0(1,k), V0(2,k), V0(3,k), M(1,k) end do 22 FORMAT (1X, I3, 6F10.5, E10.2) end subroutine print_state C------- print_x(ax, na, nf) ------------------------------- C Print ax(:, na:nf), nf-na <= 9 (for testing) subroutine print_x(ax, na, nf) INCLUDE 'nbmode.h' real, array(1:3, 1:N) :: ax CMPF ONDPU ax CMF$ LAYOUT ax(,) integer na, nf integer u, lm lm = min(nf, na+9) do, u = 1, 3 print 21, ax(u, na:lm) 21 FORMAT ( 10(1X : E9.2 :) ) end do end subroutine print_x C------- print_c(Xf, Xc, Af, Ac, na, nb) ----------------------- C Print conveyor status in positions na:nb (for testing) subroutine print_c(Xf, Xc, Af, Ac, na, nf) INCLUDE 'nbmode.h' real, array(1:3, 1:N) :: Xf, Xc, Af, Ac integer na, nf integer kk print 22, (kk, kk = na, nf) 22 FORMAT ( 10(1X : I9 :) ) print *, " - Xf ----------" call print_x(Xf, na, nf) print *, " - Xc ----------" call print_x(Xc, na, nf) print *, " - Af ----------" call print_x(Af, na, nf) print *, " - Ac ----------" call print_x(Ac, na, nf) end subroutine print_c C------- Grav(Xf) -------------------------------------- C Calculate accelerations (Newtonian gravitation) C Uses systolic ring calculation, taking advantage C of anti-symmetric force. Data being circulated C on `conveyor' are marked `c', fixed items are `f'. function Grav(Xf) INCLUDE 'nbmode.h' real, array(1:3, 1:N) :: Xf, Grav CMPF ONDPU Xf CMF$ LAYOUT Xf(,) real, array(1:3, 1:N) :: Af, Ac, D, R, Xc, Mc integer k, lm Af = 0.0 ! Fixed accelerations Ac = 0.0 ! Circulating accelerations Xc = Xf ! Circulating positions Mc = GM ! Circulating scaled masses lm = min(NB, N/2) - 1 ! For loop control do, k = 1, lm ! 2-way accel. steps Xc = CSHIFT(Xc, SHIFT=-1, DIM=2) ! Step conveyor Mc = CSHIFT(Mc, SHIFT=-1, DIM=2) if ( k > 1 ) then Ac = CSHIFT(Ac, SHIFT=-1, DIM=2) end if C Xc(:,i) = Xf(:,i-k), Similarly Mc and MG C Ac(:,i) contributes to particle i-k D = Xc - Xf R = sqrt(SPREAD(SUM(D*D, 1), 1, 3)) WHERE ( GM > 0.0 .and. Mc > 0.0 ) D = D/R**3 Af = Af + Mc*D Ac = Ac - GM*D end WHERE end do ! for 2-way accel. C Ac(i) corresponds to Af(i-lm) if ( NB > N/2 ) then ! Final 1-way accel. Xc = CSHIFT(Xc, SHIFT=-1, DIM=2) ! Step conveyor Mc = CSHIFT(Mc, SHIFT=-1, DIM=2) Ac = CSHIFT(Ac, SHIFT=-1, DIM=2) lm = N/2 ! lm gives shift of Ac D = Xc - Xf R = sqrt(SPREAD(SUM(D*D, 1), 1, 3)) WHERE ( GM > 0.0 .and. Mc > 0.0 ) D = D/R**3 Af = Af + Mc*D end WHERE end if ! for final accel. D call print_c(Xf, Xc, Af, Ac, 29, 35) Ac = CSHIFT(Ac, SHIFT=lm, DIM=2) ! Back to correct position D call print_c(Xf, Xc, Af, Ac, 1, 7) C Combine accels for final result. Grav = Af + Ac 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. subroutine modeul(h, ns) real h integer ns INCLUDE 'nbmode.h' INTERFACE function Grav(Xf) INCLUDE 'nbmode.h' real, array(1:3, 1:N) :: 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------- nbmode ------------------------------- 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 nbmode INCLUDE 'nbmode.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 GM = G*m 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 nbmode C******************************************************* C Data files: C C N No. of explicit bodies C C x y z Position of body 1 C : : C x y z Position of body N C C vx vy va Velocity of body 1 C : : C vx vy va Velocity of body N C C m Mass of body 1 C : : C m Mass of body N C C G Gravitational constant C C nx Number, C ar am radius, mass of asteroid group C : : 0 or more groups C nx Number, C ar am radius, mass of asteroid group C -1 Stopper C C ns Number of time steps per phase C np Number of phases C dt delta-t (per time step) C C Further data in the file will be ignored. C*******************************************************