static inline real isotropicRandomV(real r, real scaleRad1, real scaleRad2, real Mass1, real Mass2) { real val; val = mw_sqrt(Mass1/mw_sqrt(mw_pow(r,2) + mw_pow(scaleRad1,2)) + Mass2/mw_sqrt(mw_pow(r,2) + mw_pow(scaleRad2,2))); return val; }
/* generatePlummer: generate Plummer model initial conditions for test * runs, scaled to units such that M = -4E = G = 1 (Henon, Heggie, * etc). See Aarseth, SJ, Henon, M, & Wielen, R (1974) Astr & Ap, 37, * 183. */ static int nbGenerateIsotropicCore(lua_State* luaSt, dsfmt_t* prng, unsigned int nbody, real mass1, real mass2, mwbool ignore, mwvector rShift, mwvector vShift, real radiusScale1, real radiusScale2) { unsigned int i; int table; Body b; real r, velScale; real mass = mass1 + mass2; real radiusScale = mw_sqrt(mw_pow(radiusScale1,2) + mw_pow(radiusScale2,2)); memset(&b, 0, sizeof(b)); velScale = mw_sqrt(mass / radiusScale); /* and recip. speed scale */ b.bodynode.type = BODY(ignore); /* Same for all in the model */ b.bodynode.mass = mass / nbody; /* Mass per particle */ lua_createtable(luaSt, nbody, 0); table = lua_gettop(luaSt); for (i = 0; i < nbody; ++i) { do { r = isotropicRandomR(prng, radiusScale1, radiusScale2, mass1, mass2); /* FIXME: We should avoid the divide by 0.0 by multiplying * the original random number by 0.9999.. but I'm too lazy * to change the tests. Same with other models */ } while (isinf(r)); b.bodynode.pos = isotropicBodyPosition(prng, rShift, r); b.vel = isotropicBodyVelocity(prng, r, vShift, velScale, radiusScale1, radiusScale2, mass1, mass2); assert(nbPositionValid(b.bodynode.pos)); pushBody(luaSt, &b); // printf("Body %d is pushed. \n",i); lua_rawseti(luaSt, table, i + 1); } return 1; }
/* gets negative of the acceleration vector of this disk component */ static inline mwvector miyamotoNagaiDiskAccel(const Disk* disk, mwvector pos, real r) { mwvector acc; const real a = disk->scaleLength; const real b = disk->scaleHeight; const real zp = mw_sqrt(sqr(Z(pos)) + sqr(b)); const real azp = a + zp; const real rp = sqr(X(pos)) + sqr(Y(pos)) + sqr(azp); const real rth = mw_sqrt(cube(rp)); /* rp ^ (3/2) */ X(acc) = -disk->mass * X(pos) / rth; Y(acc) = -disk->mass * Y(pos) / rth; Z(acc) = -disk->mass * Z(pos) * azp / (zp * rth); return acc; }
static real nbCalculateEps2(real nbody, real r0) { real eps = r0 / (10.0 * mw_sqrt(nbody)); real eps2 = sqr(eps); if (eps2 <= REAL_EPSILON) { eps2 = 2.0*REAL_EPSILON; } return eps2; }
static real emdDistL2(const real* x, const real* y, void* user_param) { int i; int dims = (int)(size_t)user_param; real s = 0.0; for (i = 0; i < dims; i++) { real t = x[i] - y[i]; s += t * t; } return mw_sqrt((real) s); }
/* pickshell: pick a random point on a sphere of specified radius. */ static inline mwvector pickShell(dsfmt_t* dsfmtState, real rad) { real rsq, rsc; mwvector vec; do /* pick point in NDIM-space */ { vec = mwRandomUnitPoint(dsfmtState); rsq = mw_sqrv(vec); /* compute radius squared */ } while (rsq > 1.0); /* reject if outside sphere */ rsc = rad / mw_sqrt(rsq); /* compute scaling factor */ mw_incmulvs(vec, rsc); /* rescale to radius given */ return vec; }
static mwvector nbGravity_Exact(const NBodyCtx* ctx, NBodyState* st, const Body* p) { int i; const int nbody = st->nbody; mwvector a = ZERO_VECTOR; const real eps2 = ctx->eps2; for (i = 0; i < nbody; ++i) { const Body* b = &st->bodytab[i]; mwvector dr = mw_subv(Pos(b), Pos(p)); real drSq = mw_sqrv(dr) + eps2; real drab = mw_sqrt(drSq); real phii = Mass(b) / drab; real mor3 = phii / drSq; mw_incaddv(a, mw_mulvs(dr, mor3)); } return a; }
static real nbCalculateEps2(real nbody, real r0) { real eps = r0 / (10.0 * mw_sqrt(nbody)); return sqr(eps); }
static real nbCalculateTimestep(real mass, real r0) { return sqr(1/10.0) * mw_sqrt((PI_4_3 * cube(r0)) / mass); }
/* * nbodyGravity: Walk the tree starting at the root to do force * calculations. * * * Random notes: * - Not inlined without inline from multiple calls in * mapForceBody(). Measurably better with the inline, but only * slightly. */ static inline mwvector nbGravity(const NBodyCtx* ctx, NBodyState* st, const Body* p) { mwbool skipSelf = FALSE; mwvector pos0 = Pos(p); mwvector acc0 = ZERO_VECTOR; const NBodyNode* q = (const NBodyNode*) st->tree.root; /* Start at the root */ while (q != NULL) /* while not at end of scan */ { mwvector dr = mw_subv(Pos(q), pos0); /* Then compute distance */ real drSq = mw_sqrv(dr); /* and distance squared */ if (isBody(q) || (drSq >= Rcrit2(q))) /* If is a body or far enough away to approximate */ { if (mw_likely((const Body*) q != p)) /* self-interaction? */ { real drab, phii, mor3; /* Compute gravity */ drSq += ctx->eps2; /* use standard softening */ drab = mw_sqrt(drSq); phii = Mass(q) / drab; mor3 = phii / drSq; acc0.x += mor3 * dr.x; acc0.y += mor3 * dr.y; acc0.z += mor3 * dr.z; if (ctx->useQuad && isCell(q)) /* if cell, add quad term */ { real dr5inv, drQdr, phiQ; mwvector Qdr; /* form Q * dr */ Qdr.x = Quad(q).xx * dr.x + Quad(q).xy * dr.y + Quad(q).xz * dr.z; Qdr.y = Quad(q).xy * dr.x + Quad(q).yy * dr.y + Quad(q).yz * dr.z; Qdr.z = Quad(q).xz * dr.x + Quad(q).yz * dr.y + Quad(q).zz * dr.z; /* form dr * Q * dr */ drQdr = Qdr.x * dr.x + Qdr.y * dr.y + Qdr.z * dr.z; dr5inv = 1.0 / (sqr(drSq) * drab); /* form dr^-5 */ /* get quad. part of phi */ phiQ = 2.5 * (dr5inv * drQdr) / drSq; acc0.x += phiQ * dr.x; acc0.y += phiQ * dr.y; acc0.z += phiQ * dr.z; /* acceleration */ acc0.x -= dr5inv * Qdr.x; acc0.y -= dr5inv * Qdr.y; acc0.z -= dr5inv * Qdr.z; } } else { skipSelf = TRUE; /* Encountered self */ } q = Next(q); /* Follow next link */ } else { q = More(q); /* Follow to the next level if need to go deeper */ } } if (!skipSelf) { /* If a body does not encounter itself in its traversal of the * tree, it is "tree incest" */ nbReportTreeIncest(ctx, st); } return acc0; }