Sindbad~EG File Manager
/* Numerical integration of a function of one variable using
Runge-Kutta solver rk4 similar to the one in Numerical Recipes */
/* M. Beeson, for MathXpert */
/* code last modified 12.20.95
1.29.98, TRIGCALC_DLL etc.
3.25.01 changed hmin in numint to be nonzero
and changed TINY to 1.0e-10 instead of 1.0e-30
5.6.13 made odeint2 static
*/
#include <string.h>
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include "ode3.h"
#include "svgGraph3.h"
#include "uthash.h"
#include "ThreeBodyDoc.h"
#include "leapfrog.h"
#define EPS 1.0e-6 // was 6
#define ODETINY 1.0e-10 // was 10
static double tvalues[MAXSTEPS];
static double xvalues[MAXBODIES][MAXSTEPS];
static double yvalues[MAXBODIES][MAXSTEPS];
static double pvalues[MAXBODIES][MAXSTEPS];
static double qvalues[MAXBODIES][MAXSTEPS];
// Reset all arrays to zero.
void reset_arrays(void) {
memset(tvalues, 0, sizeof(tvalues));
memset(xvalues, 0, sizeof(xvalues));
memset(yvalues, 0, sizeof(yvalues));
memset(pvalues, 0, sizeof(pvalues));
memset(qvalues, 0, sizeof(qvalues));
}
/* The original source of the numerical code is Numerical Recipes,
which uses 'vectors' so indices start at 1. Since that code is complicated,
and works fine, I continued to pass vectors to it, but the price of not tinkering
with the Numerical Recipes code is vigilance about vectors versus 0-based arrays.
*/
/*_______________________________________________________________________*/
/* This function solves the differential equations but does not draw the solution.
Instead, it writes the computed values into static arrays of doubles defined above.
*/
static int odeint22( double *ystart, /* ystart[1..nvar] are the initial values */
int nvar, /* number of dependent variables = 4*nbodies */
double x1, /* starting value of independent variable */
double x2, /* ending value of independent variable */
double eps, /* accuracy desired */
double h1, /* guessed first stepsize */
double hmin, /* minimum allowed stepsize (can be zero) */
int *nok, /* number of good steps taken */
int *nbad, /* number of bad, but retried and fixed, steps */
double *xptr, /* pointer to value of independent variable */
double xtol, /* don't make lines more than this long */
double ytol,
double *mass, /* array of nbodies masses */
char *integrationMethod /* rk4 or leapfrog */
)
{ int nstp,i,err;
double x,hnext,hdid,h;
int nbodies = nvar/4;
assert(nvar %4 == 0);
if(nbodies==3)
printf("masses are %lf, %lf, and %lf\n", mass[1], mass[2], mass[3]);
double *yscal,*y,*dydx;
h=(x2 > x1) ? fabs(h1) : -fabs(h1);
if(h == 0.0)
return 0; /* wrong input, nothing to draw; but not an error. */
yscal=vector(1,nvar);
y=vector(1,nvar);
dydx=vector(1,nvar);
x=x1;
int leapfrogflag = !strcmp(integrationMethod, "leapfrog");
// making the default "rk4"
*nok = (*nbad) = 0;
for (i=1;i<=nvar;i++)
y[i]=ystart[i];
for(int k = 0; k<nbodies;k++)
{ // starting position and velocity of the kth body
// presently all in the 1-based array y
// Let's put them in clearly-labeled 0-based arrays.
// xvalues etc. are 0-based arrays, y is a 1-based vector
xvalues[k][0] = y[1+2*k]; // x-coordinate of k-th body, k starting at 0
yvalues[k][0] = y[2+2*k]; // x-velocity of k-th body, k starting at 0
pvalues[k][0] = y[1+2*nbodies+2*k]; // x-velocity of k-th body, k starting at 0
qvalues[k][0] = y[2+2*nbodies+2*k]; // y-velocity of k-th body, k starting at 0
printf("body %d: %.10lf, %.10lf, %.10lf, %.10lf\n", k,xvalues[k][0], yvalues[k][0], pvalues[k][0],qvalues[k][0]);
}
for (nstp=0;nstp<MAXSTEPS;nstp++)
{ tvalues[nstp] = x;
derivs(nbodies,x,y,dydx,xptr,mass);
for (i=1;i<=nvar;i++)
yscal[i]=fabs(y[i])+fabs(dydx[i]*h)+ODETINY;
if(leapfrogflag)
h = 0.001; // added 3.28.25 for use with leapfrog integration
// not needed with rkqc because rkqc adjusts it internally
// but leapfrog_step does not, so without this, nothing
// ever increases it and we draw only a millimeter of solution.
if ((x+h-x2)*(x+h-x1) > 0.0)
h=x2-x;
// rkqc is numerically inadequate for the figure eight problem!
// So we use Leapfrog integration. See secion 16.6 of Third Edition
// of Numerical Recipes in C, or the Wikipedia article on Leapfrog Integration.
// But rkqc is more accurate in many simple examples. So we provide both.
if(leapfrogflag)
err = leapfrog_step(y,dydx,nvar,&x,h,eps,yscal,&hdid,&hnext,xptr,xtol,ytol,mass);
else
err = rkqc(y,dydx,nvar,&x,h,eps,yscal,&hdid,&hnext,xptr,xtol,ytol,mass);
if (err != 0)
printf("integration failed\n");
// printf("step %d, t = %.6f, hdid = %.10f, hnext = %.10f \n", nstp, x, hdid, hnext);
if(err)
{ free_vector(dydx,1);
free_vector(y,1);
free_vector(yscal,1);
if(err == 5)
return 0; /* failure of deval in rkqc is not an error in odeint */
return err;
}
if (hdid == h)
++(*nok);
else
++(*nbad);
for(int k = 0; k<nbodies;k++)
{ xvalues[k][nstp] = y[1+2*k];
yvalues[k][nstp] = y[2+2*k];
pvalues[k][nstp] = y[1+2*nbodies+2*k];
qvalues[k][nstp] = y[2+2*nbodies+2*k];
// starting position and velocity of the kth body
}
// #define ENERGY
#ifdef ENERGY
double E = 0.0;
// Kinetic energy
for (int ell = 0; ell < nbodies; ell++) {
E += 0.5 * mass[ell] * (pvalues[ell][nstp] * pvalues[ell][nstp] + qvalues[ell][nstp] * qvalues[ell][nstp]);
}
// Potential energy (each pair once)
for (int i = 0; i < nbodies; i++) {
for (int j = i + 1; j < nbodies; j++) {
double dx = xvalues[i][nstp] - xvalues[j][nstp];
double dy = yvalues[i][nstp] - yvalues[j][nstp];
double r = hypot(dx, dy);
E -= mass[i] * mass[j] / r;
}
}
if(nstp % 10000 == 0)
printf("E = %.10lf\n", E);
#endif
if (x >= x2) /* are we done? In this program x2 > x1 always */
{ // We're done, no need to write xvalues[k][nstp+1]
free_vector(dydx,1);
free_vector(y,1);
free_vector(yscal,1);
printf("Used %d steps\n",nstp);
return 0;
}
if (fabs(hnext) <= hmin)
{ free_vector(yscal,1);
free_vector(dydx,1);
free_vector(y,1);
return 2; /* step size too small */
/* 11.28.91, set hmin to 0.0 so we should never hit this error */
/* 9.9.93, set hmin nonzero: example, y'=1/y. The solution
is really y= \sqrt (2x-1), but the step size goes to zero as
we approach the point (0.5, 0), and the program hangs up. */
}
h=hnext;
}
free_vector(yscal,1);
free_vector(dydx,1);
free_vector(y,1);
printf("Completed all allowed %d steps\n",nstp);
return 3; /* too many steps */
}
// we need space to store the solution. There will
// 4*DIM*nbodies variables. space required will be
// 4*DIM*nbodies*MAXSTEPS. if DIM is 2 and nbodies is 12 and MAXSTEPS is 10000
// that comes to one million, which is not too much. So let's just
// allocate it statically so we can see it in the debugger.
double values[4*MAXBODIES+1][2];
double times[MAXSTEPS];
void computeSolution(PDOCDATA3 pDocData)
// pDocData must already be initialized.
// use odeint22 to compute the solution.
{
double ystart[MAXBODIES*4+1];/* the initial values*/
int i;
int nbodies = pDocData->nbodies;
int nvar = 4*nbodies;
for(i=0;i< nbodies;i++)
{ // set position variables
// there are 2*nbodies position and 2*nbodies velocity variables
// we list all the position variable first, then the velocities
// remember ystart is 1-based
ystart[2*i+1] = pDocData->bodies[i].x0;
ystart[2*i+2] = pDocData->bodies[i].y0;
// set velocity variables
ystart[2*nbodies+ 2*i+1] = pDocData->bodies[i].p0;
ystart[2*nbodies+ 2*i+2] = pDocData->bodies[i].q0;
}
double starttime = 0.0;
double endtime = pDocData->tmax; /* ending value of independent variable */
double eps = 1e-7; /* accuracy desired */
// double eps = 1.0e-10; in MathXpert; but doesn't work on comet and slingshot in ThreeBody
// 1e-8 doesn't draw the whole slingshot and 1e-9 only draws a few millimeters
double h1 = 0.0001; /* guessed first stepsize */
double hmin = 0; /* minimum allowed stepsize (can be zero) */
int nok; /* number of good steps taken */
int nbad; /* number of bad, but retried and fixed, steps */
double xtol = 0.001;
double ytol = 0.001;
double mass[MAXBODIES];
for(i=0;i<pDocData->nbodies;i++)
mass[i] = pDocData->bodies[i].mass; // 0-based indexing
//int err =
odeint22(ystart, nvar, starttime,endtime, eps, h1,hmin,
&nok, &nbad, times,
xtol, ytol, mass,
pDocData->integrationMethod);
return; // ignoring err for now
}
/*__________________________________________________________*/
#define RGB(r,g,b) ((unsigned int)(r) | ((unsigned int)(g) << 8) | ((unsigned int)(b) << 16))
void drawAxes(PDOCDATA3 pDocData)
// draw the axes on the graph
{ set_graphpencolor(RGB(128,128,128));
set_world(pDocData->xmin,pDocData->xmax, pDocData->ymin,pDocData->ymax);
begin_path();
move_to(pDocData->xmin,0);
line_to(pDocData->xmax,0); /* draw the x-axis (if visible) */
move_to(0,pDocData->ymin);
move_to(0,pDocData->ymin);
line_to(0,pDocData->ymax); /* draw the y-axis (if visible) */
end_path();
}
/*__________________________________________________________*/
int drawSolution(PDOCDATA3 pDocData)
// send the solution in SVG form to the browser
// return 0 for success, 1 for error.
// numerical error solving is considered "collision" and
// is not an error here.
// Assumes the data for the solution are in the static arrays
// defined near the top of this file. Also assumes
// theDevice has been initialized to the correct viewport
{
// set up world coordinates
int i,k;
int nbodies = pDocData->nbodies;
set_world(pDocData->xmin,pDocData->xmax, pDocData->ymin,pDocData->ymax);
for(i=0;i<nbodies;i++)
{ body theBody = pDocData->bodies[i];
int color = theBody.color;
// double mass = theBody.mass;
set_graphpencolor(color);
double tmax = pDocData->tmax;
char attr[64];
sprintf(attr,"body=\"%d\"",i+1); // so the labels with start with 1
// draw the path of the i-th body now
begin_path2(attr); // leaves it ready for 'M' command
move_to(xvalues[i][0],yvalues[i][0]);
for (k = 1; k < MAXSTEPS && tvalues[k] > 0.0 && tvalues[k] <= tmax; k++)
line_to(xvalues[i][k], yvalues[i][k]);
end_path();
}
return 0;
}
#undef MAXSTEPS
#undef ODETINY
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists