Entry 5296
newtonian numerical integrator
Submitted by anonymous
on July 27, 2010 at 8:49 p.m.
Language: C++. Code size: 7.5 KB.
// Fourth-order Runge-Kutta integrator for solving N-body newtonian problem // // This is only a first approach to the problem. #include <cmath> #include <cstdio> /* Class vector3: tridimensional vectors and their operators */ struct vector3 { double x, y, z; vector3() { x = y = z = 0; } vector3(double X, double Y, double Z) { x = X; y = Y; z = Z; } vector3 operator+ (vector3 a) { vector3 sum(x + a.x, y + a.y, z + a.z); return sum; } vector3 &operator+= (vector3 a) { x += a.x; y += a.y; z += a.z; return *this; } vector3 operator- () { vector3 op(-x, -y, -z); return op; } vector3 operator- (vector3 a) { vector3 sum(x - a.x, y - a.y, z - a.z); return sum; } vector3 &operator-= (vector3 a) { x -= a.x; y -= a.y; z -= a.z; return *this; } vector3 operator* (double k) { vector3 res(x * k, y * k, z * k); return res; } vector3 &operator*= (double k) { x *= k; y *= k; z *= k; return *this; } vector3 operator/ (double k) { vector3 res(x / k, y / k, z / k); return res; } friend double abs(vector3); friend double cubeabs(vector3); friend double dot(vector3, vector3); friend vector3 cross(vector3, vector3); }; inline vector3 makevec(double a) { vector3 ret(a, a, a); return ret; } inline vector3 makevec(double a, double b, double c) { vector3 ret(a, b, c); return ret; } inline double abs(vector3 a) { return sqrt(a.x * a.x + a.y * a.y + a.z * a.z); } inline double dot(vector3 a, vector3 b) { return (a.x * b.x + a.y * b.y + a.z * b.z); } inline double cubeabs(vector3 a) { double sqabs = a.x * a.x + a.y * a.y + a.z * a.z; return (sqabs * sqrt(sqabs)); } inline vector3 cross(vector3 a, vector3 b) { vector3 ret(a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x); return ret; } /* class solvebody: numerically integrate N-body problem */ class solvebody { /* Parameters of the integrator */ int n; // number of bodies char steps; // number of calls to step mod 2 (error estimation) double time; // time double tol; // step size [TODO automatic stepsize with tolerance] /* Body data */ double *gm; // product of the mass by the gravitational constant vector3 *r0; // initial positions for the bodies 0..n-1 vector3 *v0; // initial velocities for the bodies 0..n-1 /* State information */ vector3 *k1, *k2, *k3, *k4; // Constants used in Runge-Kutta method vector3 *epsk2, *epsk3, *epsk4; // Constants used in error estimation public: solvebody() { n = steps = 0; tol = time = 0.0; gm = NULL; r0 = v0 = k1 = k2 = k3 = k4 = epsk2 = epsk3 = epsk4 = NULL; } // Constructor /* Argument list: (double)itime initial time (int)n number of bodies (double [n])gm product of the mass by the gravitational constant (vector3 [n])r0 initial positions (vector3 [n])v0 initial velocities (double)tol tolerance (see documentation) [TODO explain when done] Negative values for gm have special meanings. If gm = -0.5, the body is not affected by gravitational forces. If gm = -n, the body is fixed with relation to the body numbered (n - 1). */ solvebody(double, int, double *, vector3 *, vector3 *, double = 0.01); // Step the Runge-Kutta integrator /* Argument list: (vector3 **)r (*r)[0..n-1] is set to the radius vector list (vector3 **)v (*v)[0..n-1] is set to the velocity list (vector3 **)eps (*eps)[0..n-1] are the estimated errors in Returns: the time for which the state vector has been computed */ double step(); /* Clears the error estimate and step count */ double clearerr(); /* Destructor method */ ~solvebody() { delete [] k1; } friend int main(int, char **); }; // Constructor solvebody::solvebody(double TIME, int N, double *GM, vector3 *R0, vector3 *V0, double TOL) { time = TIME; n = N; gm = GM; r0 = R0; v0 = V0; tol = TOL; k1 = new vector3[8*N]; k2 = k1 + N; k3 = k1 + 2 * N; k4 = k1 + 3 * N; epsk2 = k1 + 4 * N; epsk3 = k1 + 5 * N; epsk4 = k1 + 6 * N; } // Step the Runge-Kutta integrator double solvebody::step( ) { double &h = tol; // Zero coefficients for(int i = 0; i != n; ++i) k1[i] = k2[i] = k3[i] = k4[i] = makevec(0); // Initial point for(int i = 0; i != n; ++i) { for(int j = i + 1; j != n; ++j) { vector3 relpos = r0[j] - r0[i]; double crelpos = cubeabs(relpos); if(crelpos != 0.0) { k1[i] += relpos * gm[j] / crelpos; k1[j] += -relpos * gm[i] / crelpos; } } k1[i] *= h; } // Double step size and calculate if(!steps) { for(int i = 0; i != n; ++i) epsk2[i] = epsk3[i] = epsk4[i] = makevec(0); } // Midpoint, first evaluation for(int i = 0; i != n; ++i) { for(int j = i + 1; j != n; ++j) { vector3 relpos = (v0[j] - v0[i]) * h / 2 + r0[j] - r0[i]; double crelpos = cubeabs(relpos); if(crelpos != 0.0) { k2[i] += relpos * gm[j] / crelpos; k2[j] += -relpos * gm[i] / crelpos; } } k2[i] *= h; } // Midpoint, second evaluation for(int i = 0; i != n; ++i) { for(int j = i + 1; j != n; ++j) { vector3 relpos = (v0[j] - v0[i] + (k1[j] - k1[i]) / 2) * h / 2 + r0[j] - r0[i]; double crelpos = cubeabs(relpos); if(crelpos != 0.0) { k3[i] += relpos * gm[j] / crelpos; k3[j] += -relpos * gm[i] / crelpos; } } k3[i] *= h; } // Final point for(int i = 0; i != n; ++i) { for(int j = i + 1; j != n; ++j) { vector3 relpos = (v0[j] - v0[i] + (k2[j] - k2[i]) / 2) * h + r0[j] - r0[i]; double crelpos = cubeabs(relpos); if(crelpos != 0.0) { k4[i] += relpos * gm[j] / crelpos; k4[j] += -relpos * gm[i] / crelpos; } } k4[i] *= h; } // Advance state vectors for(int i = 0; i != n; ++i) { r0[i] += (v0[i] + (k1[i] + k2[i] + k3[i]) / 6) * h; v0[i] += (k1[i] + k4[i]) / 6 + (k2[i] + k3[i]) / 3; } steps = (steps + 1) % 2; return (time += h); } int main(int argc, char *argv[]) { if(argc < 2) { fprintf(stderr, "usage: newton data [output]\n"); return 0; } FILE *fin = fopen(argv[1], "r"); if(fin == NULL) { fprintf(stderr, "Could not open file %s for input\n", argv[1]); return 0; } FILE *fout = stdout; if(argc > 2) { fout = fopen(argv[2], "w+"); if(fout == NULL) { fprintf(stderr, "Could not open file %s for output; using stdout instead\n", argv[2]); fout = stdout; } } double g, step, time; int n; fscanf(fin, "%lf\n%lf\n%lf\n%d\n", &g, &step, &time, &n); fprintf(stderr, "Gravitational constant = %1.4E\n", g); fprintf(stderr, "Step size = %lf\n", step); fprintf(stderr, "Initial time = %1.9E\n", time); fprintf(stderr, "Number of bodies = %d\n", n); if(n < 2 || n > 320) { fprintf(stderr, "Number of bodies outside expected range\n"); return 0; } char *dummy = new char[256]; double *gm = new double[n]; vector3 *pos = new vector3[n]; vector3 *vel = new vector3[n]; for(int i = 0; i != n; ++i) { fscanf(fin, "%s\n", dummy); fscanf(fin, "%lf\n%lf;%lf;%lf\n%lf;%lf;%lf|\n", &gm[i], &pos[i].x, &pos[i].y, &pos[i].z, &vel[i].x, &vel[i].y, &vel[i].z); gm[i] *= g; } solvebody solver(time, n, gm, pos, vel, step); for(;;) { fprintf(fout, "%1.8E\n", solver.time); for(int i = 0; i != n; ++i) { fprintf(fout, "%d 0; %+1.11E; %+1.11E; %+1.11E\n",i, solver.r0[i].x, solver.r0[i].y, solver.r0[i].z); fprintf(fout, "%d 1; %+1.11E; %+1.11E; %+1.11E\n",i, solver.v0[i].x, solver.v0[i].y, solver.v0[i].z); } solver.step(); } delete [] gm; delete [] pos; delete [] vel; delete [] dummy; fclose(fin); if(fout != stdout) fclose(fout); return 0; }
This snippet took 0.07 seconds to highlight.
Back to the Entry List or Home.