[170] | 1 | /****************************************************************************/ |
---|
| 2 | /* TREEGRAV.C: routines to compute gravity. Public routines: gravcalc(). */ |
---|
| 3 | /* Copyright (c) 2001 by Joshua E. Barnes, Honolulu, Hawai`i. */ |
---|
| 4 | /****************************************************************************/ |
---|
| 5 | |
---|
| 6 | #include "stdinc.h" |
---|
| 7 | #include "mathfns.h" |
---|
| 8 | #include "vectmath.h" |
---|
| 9 | #include "treedefs.h" |
---|
| 10 | |
---|
| 11 | /* Local routines to perform force calculations. */ |
---|
| 12 | |
---|
| 13 | local void walktree(nodeptr *, nodeptr *, cellptr, cellptr, |
---|
| 14 | nodeptr, real, vector); |
---|
| 15 | local bool accept(nodeptr, real, vector); |
---|
| 16 | local void walksub(nodeptr *, nodeptr *, cellptr, cellptr, |
---|
| 17 | nodeptr, real, vector); |
---|
| 18 | local void gravsum(bodyptr, cellptr, cellptr); |
---|
| 19 | local void sumnode(cellptr, cellptr, vector, real *, vector); |
---|
| 20 | local void sumcell(cellptr, cellptr, vector, real *, vector); |
---|
| 21 | |
---|
| 22 | /* Lists of active nodes and interactions. */ |
---|
| 23 | |
---|
| 24 | #if !defined(FACTIVE) |
---|
| 25 | # define FACTIVE 0.75 /* active list fudge factor */ |
---|
| 26 | #endif |
---|
| 27 | |
---|
| 28 | local int actlen; /* length as allocated */ |
---|
| 29 | |
---|
| 30 | local nodeptr *active; /* list of nodes tested */ |
---|
| 31 | |
---|
| 32 | local cellptr interact; /* list of interactions */ |
---|
| 33 | |
---|
| 34 | /* |
---|
| 35 | * GRAVCALC: perform force calculation on all particles. |
---|
| 36 | */ |
---|
| 37 | |
---|
| 38 | void gravcalc(void) |
---|
| 39 | { |
---|
| 40 | double cpustart; |
---|
| 41 | vector rmid; |
---|
| 42 | |
---|
| 43 | actlen = FACTIVE * 216 * tdepth; /* estimate list length */ |
---|
| 44 | #if !defined(QUICKSCAN) |
---|
| 45 | actlen = actlen * rpow(theta, -2.5); /* allow for opening angle */ |
---|
| 46 | #endif |
---|
| 47 | active = (nodeptr *) allocate(actlen * sizeof(nodeptr)); |
---|
| 48 | interact = (cellptr) allocate(actlen * sizeof(cell)); |
---|
| 49 | cpustart = cputime(); /* record time, less alloc */ |
---|
| 50 | actmax = nbbcalc = nbccalc = 0; /* zero cumulative counters */ |
---|
| 51 | active[0] = (nodeptr) root; /* initialize active list */ |
---|
| 52 | CLRV(rmid); /* set center of root cell */ |
---|
| 53 | walktree(active, active + 1, interact, interact + actlen, |
---|
| 54 | (nodeptr) root, rsize, rmid); /* scan tree, update forces */ |
---|
| 55 | cpuforce = cputime() - cpustart; /* store CPU time w/o alloc */ |
---|
| 56 | free(active); |
---|
| 57 | free(interact); |
---|
| 58 | } |
---|
| 59 | |
---|
| 60 | /* |
---|
| 61 | * WALKTREE: do a complete walk of the tree, building the interaction |
---|
| 62 | * list level-by-level and computing the resulting force on each body. |
---|
| 63 | */ |
---|
| 64 | |
---|
| 65 | local void walktree(nodeptr *aptr, nodeptr *nptr, cellptr cptr, cellptr bptr, |
---|
| 66 | nodeptr p, real psize, vector pmid) |
---|
| 67 | { |
---|
| 68 | nodeptr *np, *ap, q; |
---|
| 69 | int actsafe; |
---|
| 70 | |
---|
| 71 | if (Update(p)) { /* are new forces needed? */ |
---|
| 72 | np = nptr; /* start new active list */ |
---|
| 73 | actsafe = actlen - NSUB; /* leave room for NSUB more */ |
---|
| 74 | for (ap = aptr; ap < nptr; ap++) /* loop over active nodes */ |
---|
| 75 | if (Type(*ap) == CELL) { /* is this node a cell? */ |
---|
| 76 | if (accept(*ap, psize, pmid)) { /* does it pass the test? */ |
---|
| 77 | Mass(cptr) = Mass(*ap); /* copy to interaction list */ |
---|
| 78 | SETV(Pos(cptr), Pos(*ap)); |
---|
| 79 | SETM(Quad(cptr), Quad(*ap)); |
---|
| 80 | cptr++; /* and bump cell array ptr */ |
---|
| 81 | } else { /* else it fails the test */ |
---|
| 82 | if (np - active >= actsafe) /* check list has room */ |
---|
| 83 | error("walktree: active list overflow\n"); |
---|
| 84 | for (q = More(*ap); q != Next(*ap); q = Next(q)) |
---|
| 85 | /* loop over all subcells */ |
---|
| 86 | *np++= q; /* put on new active list */ |
---|
| 87 | } |
---|
| 88 | } else /* else this node is a body */ |
---|
| 89 | if (*ap != p) { /* if not self-interaction */ |
---|
| 90 | --bptr; /* bump body array ptr */ |
---|
| 91 | Mass(bptr) = Mass(*ap); /* and copy data to array */ |
---|
| 92 | SETV(Pos(bptr), Pos(*ap)); |
---|
| 93 | } |
---|
| 94 | actmax = MAX(actmax, np - active); /* keep track of max active */ |
---|
| 95 | if (np != nptr) /* if new actives listed */ |
---|
| 96 | walksub(nptr, np, cptr, bptr, p, psize, pmid); |
---|
| 97 | /* then visit next level */ |
---|
| 98 | else { /* else no actives left, so */ |
---|
| 99 | if (Type(p) != BODY) /* must have found a body */ |
---|
| 100 | error("walktree: recursion terminated with cell\n"); |
---|
| 101 | gravsum((bodyptr) p, cptr, bptr); /* sum force on the body */ |
---|
| 102 | } |
---|
| 103 | } |
---|
| 104 | } |
---|
| 105 | |
---|
| 106 | #if defined(QUICKSCAN) |
---|
| 107 | |
---|
| 108 | /* |
---|
| 109 | * ACCEPT: quick criterion accepts any cell not touching cell p. |
---|
| 110 | */ |
---|
| 111 | |
---|
| 112 | local bool accept(nodeptr c, real psize, vector pmid) |
---|
| 113 | { |
---|
| 114 | real p15, dk; |
---|
| 115 | |
---|
| 116 | p15 = ((real) 1.5) * psize; /* premultiply cell size */ |
---|
| 117 | dk = Pos(c)[0] - pmid[0]; /* find distance to midpnt */ |
---|
| 118 | if (ABS(dk) > p15) /* if c does not touch p */ |
---|
| 119 | return (TRUE); /* then accept interaction */ |
---|
| 120 | dk = Pos(c)[1] - pmid[1]; /* find distance to midpnt */ |
---|
| 121 | if (ABS(dk) > p15) /* if c does not touch p */ |
---|
| 122 | return (TRUE); /* then accept interaction */ |
---|
| 123 | dk = Pos(c)[2] - pmid[2]; /* find distance to midpnt */ |
---|
| 124 | if (ABS(dk) > p15) /* if c does not touch p */ |
---|
| 125 | return (TRUE); /* then accept interaction */ |
---|
| 126 | return (FALSE); /* else do not accept it */ |
---|
| 127 | } |
---|
| 128 | |
---|
| 129 | #else |
---|
| 130 | |
---|
| 131 | /* |
---|
| 132 | * ACCEPT: standard criterion accepts cell if its critical radius |
---|
| 133 | * does not intersect cell p, and also imposes above condition. |
---|
| 134 | */ |
---|
| 135 | |
---|
| 136 | local bool accept(nodeptr c, real psize, vector pmid) |
---|
| 137 | { |
---|
| 138 | real dmax, dsq, dk; |
---|
| 139 | int k; |
---|
| 140 | |
---|
| 141 | dmax = psize; /* init maximum distance */ |
---|
| 142 | dsq = 0.0; /* and squared min distance */ |
---|
| 143 | for (k = 0; k < NDIM; k++) { /* loop over space dims */ |
---|
| 144 | dk = Pos(c)[k] - pmid[k]; /* form distance to midpnt */ |
---|
| 145 | if (dk < 0) /* and get absolute value */ |
---|
| 146 | dk = - dk; |
---|
| 147 | if (dk > dmax) /* keep track of max value */ |
---|
| 148 | dmax = dk; |
---|
| 149 | dk -= ((real) 0.5) * psize; /* allow for size of cell */ |
---|
| 150 | if (dk > 0) |
---|
| 151 | dsq += dk * dk; /* sum min dist to cell ^2 */ |
---|
| 152 | } |
---|
| 153 | return (dsq > Rcrit2(c) && /* test angular criterion */ |
---|
| 154 | dmax > ((real) 1.5) * psize); /* and adjacency criterion */ |
---|
| 155 | } |
---|
| 156 | |
---|
| 157 | #endif |
---|
| 158 | |
---|
| 159 | /* |
---|
| 160 | * WALKSUB: test next level's active list against subnodes of p. |
---|
| 161 | */ |
---|
| 162 | |
---|
| 163 | local void walksub(nodeptr *nptr, nodeptr *np, cellptr cptr, cellptr bptr, |
---|
| 164 | nodeptr p, real psize, vector pmid) |
---|
| 165 | { |
---|
| 166 | real poff; |
---|
| 167 | nodeptr q; |
---|
| 168 | int k; |
---|
| 169 | vector nmid; |
---|
| 170 | |
---|
| 171 | poff = psize / 4; /* precompute mid. offset */ |
---|
| 172 | if (Type(p) == CELL) { /* fanout over descendents */ |
---|
| 173 | for (q = More(p); q != Next(p); q = Next(q)) { |
---|
| 174 | /* loop over all subcells */ |
---|
| 175 | for (k = 0; k < NDIM; k++) /* locate each's midpoint */ |
---|
| 176 | nmid[k] = pmid[k] + (Pos(q)[k] < pmid[k] ? - poff : poff); |
---|
| 177 | walktree(nptr, np, cptr, bptr, q, psize / 2, nmid); |
---|
| 178 | /* recurse on subcell */ |
---|
| 179 | } |
---|
| 180 | } else { /* extend virtual tree */ |
---|
| 181 | for (k = 0; k < NDIM; k++) /* locate next midpoint */ |
---|
| 182 | nmid[k] = pmid[k] + (Pos(p)[k] < pmid[k] ? - poff : poff); |
---|
| 183 | walktree(nptr, np, cptr, bptr, p, psize / 2, nmid); |
---|
| 184 | /* and search next level */ |
---|
| 185 | } |
---|
| 186 | } |
---|
| 187 | |
---|
| 188 | /* |
---|
| 189 | * GRAVSUM: compute gravitational field at body p0. |
---|
| 190 | */ |
---|
| 191 | |
---|
| 192 | local void gravsum(bodyptr p0, cellptr cptr, cellptr bptr) |
---|
| 193 | { |
---|
| 194 | vector pos0, acc0; |
---|
| 195 | real phi0; |
---|
| 196 | |
---|
| 197 | SETV(pos0, Pos(p0)); /* copy position of body */ |
---|
| 198 | phi0 = 0.0; /* init total potential */ |
---|
| 199 | CLRV(acc0); /* and total acceleration */ |
---|
| 200 | if (usequad) /* if using quad moments */ |
---|
| 201 | sumcell(interact, cptr, pos0, &phi0, acc0); |
---|
| 202 | /* sum cell forces w quads */ |
---|
| 203 | else /* not using quad moments */ |
---|
| 204 | sumnode(interact, cptr, pos0, &phi0, acc0); |
---|
| 205 | /* sum cell forces wo quads */ |
---|
| 206 | sumnode(bptr, interact + actlen, pos0, &phi0, acc0); |
---|
| 207 | /* sum forces from bodies */ |
---|
| 208 | Phi(p0) = phi0; /* store total potential */ |
---|
| 209 | SETV(Acc(p0), acc0); /* and total acceleration */ |
---|
| 210 | nbbcalc += interact + actlen - bptr; /* count body-body forces */ |
---|
| 211 | nbccalc += cptr - interact; /* count body-cell forces */ |
---|
| 212 | } |
---|
| 213 | |
---|
| 214 | /* |
---|
| 215 | * SUMNODE: add up body-node interactions. |
---|
| 216 | */ |
---|
| 217 | |
---|
| 218 | local void sumnode(cellptr start, cellptr finish, |
---|
| 219 | vector pos0, real *phi0, vector acc0) |
---|
| 220 | { |
---|
| 221 | cellptr p; |
---|
| 222 | real eps2, dr2, drab, phi_p, mr3i; |
---|
| 223 | vector dr; |
---|
| 224 | |
---|
| 225 | eps2 = eps * eps; /* avoid extra multiplys */ |
---|
| 226 | for (p = start; p < finish; p++) { /* loop over node list */ |
---|
| 227 | DOTPSUBV(dr2, dr, Pos(p), pos0); /* compute separation */ |
---|
| 228 | /* and distance squared */ |
---|
| 229 | dr2 += eps2; /* add standard softening */ |
---|
| 230 | drab = rsqrt(dr2); /* form scalar "distance" */ |
---|
| 231 | phi_p = Mass(p) / drab; /* get partial potential */ |
---|
| 232 | *phi0 -= phi_p; /* decrement tot potential */ |
---|
| 233 | mr3i = phi_p / dr2; /* form scale factor for dr */ |
---|
| 234 | ADDMULVS(acc0, dr, mr3i); /* sum partial acceleration */ |
---|
| 235 | } |
---|
| 236 | } |
---|
| 237 | |
---|
| 238 | /* |
---|
| 239 | * SUMCELL: add up body-cell interactions. |
---|
| 240 | */ |
---|
| 241 | |
---|
| 242 | local void sumcell(cellptr start, cellptr finish, |
---|
| 243 | vector pos0, real *phi0, vector acc0) |
---|
| 244 | { |
---|
| 245 | cellptr p; |
---|
| 246 | real eps2, dr2, drab, phi_p, mr3i, drqdr, dr5i, phi_q; |
---|
| 247 | vector dr, qdr; |
---|
| 248 | |
---|
| 249 | eps2 = eps * eps; |
---|
| 250 | for (p = start; p < finish; p++) { /* loop over node list */ |
---|
| 251 | DOTPSUBV(dr2, dr, Pos(p), pos0); /* do mono part of force */ |
---|
| 252 | dr2 += eps2; |
---|
| 253 | drab = rsqrt(dr2); |
---|
| 254 | phi_p = Mass(p) / drab; |
---|
| 255 | mr3i = phi_p / dr2; |
---|
| 256 | DOTPMULMV(drqdr, qdr, Quad(p), dr); /* do quad part of force */ |
---|
| 257 | dr5i = ((real) 1.0) / (dr2 * dr2 * drab); |
---|
| 258 | phi_q = ((real) 0.5) * dr5i * drqdr; |
---|
| 259 | *phi0 -= phi_p + phi_q; /* add mono and quad pot */ |
---|
| 260 | mr3i += ((real) 5.0) * phi_q / dr2; |
---|
| 261 | ADDMULVS2(acc0, dr, mr3i, qdr, -dr5i); /* add mono and quad acc */ |
---|
| 262 | } |
---|
| 263 | } |
---|