Double Pendulum returning nan results. Why? - c++

When I run this code the double pendulum shows for a split second and then disappears. It seems most of the variables in the formula, num1, num2, etc., start returning nan after that split second when the pendulum disappears. Can anyone see why? I thought it had to do with converting degrees to radians at 180 degrees so I added the if statement in my convert function.
Here is the link to the Double Pendulum motion formula. I broke down the formula into pieces for simplicity's sake. https://www.myphysicslab.com/pendulum/double-pendulum-en.html
This idea came from Coding Train's Double Pendulum video: https://www.youtube.com/watch?v=uWzPe_S-RVE
#undef __STRICT_ANSI__
#include "window.h"
#include <SFML/Graphics.hpp>
#include <GL/glew.h>
#include <cmath>
#include <iostream>
namespace Window
{
sf::ContextSettings settings;
sf::RenderWindow window(sf::VideoMode(600, 600), "Window", sf::Style::Close | sf::Style::Titlebar, settings);
void init()
{
settings.depthBits = 24;
settings.majorVersion = 4;
settings.minorVersion = 6; //OpenGL 4.6
glewInit();
glViewport(0,0, 600, 600);
}
void close()
{
window.close();
}
void update()
{
window.display();
}
void checkForClose()
{
sf::Event windowEvent;
while (window.pollEvent(windowEvent))
{
if (windowEvent.type == sf::Event::Closed)
{
close();
}
}
}
bool isOpen()
{
return window.isOpen();
}
double convDeg(double deg)
{
if (deg == 180.0)
{
return 0.0;
}
else
{
return deg * M_PI / 180.0;
}
}
void runLoop()
{
double r1 = 100;
double m1 = 40;
double a1 = 90;
double a1_v = 0;
double r2 = 100;
double m2 = 40;
double a2 = 30;
double a2_v = 0;
double x1 = 0;
double x2 = 0;
double y1 = 0;
double y2 = 0;
double g = 0.005;
double num1 = 0;
double num2 = 0;
double num3 = 0;
double num4 = 0;
double den = 0;
double a1_a = 0;
double a2_a = 0;
while (window.isOpen())
{
num1 = -g * (2 * m1 + m2) * std::sin(convDeg(a1));
num2 = -m2 * g * std::sin(convDeg(a1-2*a2));
num3 = -2*std::sin(convDeg(a1-a2))*m2;
num4 = a2_v*a2_v*r2+a1_v*a1_v*r1*std::cos(convDeg(a1-a2));
den = r1 * (2*m1+m2-m2*cos(convDeg(2*a1-2*a2)));
a1_a = (num1 + num2 + num3*num4) / den;
num1 = 2 * std::sin(convDeg(a1-a2));
num2 = (a1_v*a1_v*r1*(m1+m2));
num3 = g * (m1 + m2) * std::cos(convDeg(a1));
num4 = a2_v*a2_v*r2*m2*std::cos(convDeg(a1-a2));
den = r2 * (2*m1+m2-m2*std::cos(convDeg(2*a1-2*a2)));
a2_a = (num1*(num2+num3+num4)) / den;
x1 = 300 + (r1 * std::sin((convDeg(a1))));
y1 = 300 + (r1 * std::cos((convDeg(a1))));
x2 = x1 + (r2 * std::sin((convDeg(a2))));
y2 = y1 + (r2 * std::cos((convDeg(a2))));
a1_v += a1_a;
a2_v += a2_a;
a1 += a1_v;
a2 += a2_v;
sf::Vertex pOne[] =
{
sf::Vertex(sf::Vector2f(300,300)),
sf::Vertex(sf::Vector2f(x1,y1)),
};
sf::Vertex pTwo[] =
{
sf::Vertex(sf::Vector2f(x1,y1)),
sf::Vertex(sf::Vector2f(x2,y2)),
};
window.clear();
window.draw(pOne, 2, sf::Lines);
window.draw(pTwo, 2, sf::Lines);
update();
checkForClose();
std::cout << std::sin(convDeg(a1)) << "\n";
}
}
}

List of problems:
convDeg: 180 and 0 degrees are not equivalent.
Angular variables: acceleration / velocity a1_a, a1_v etc are in radians, whereas your angle variables a1, a2 etc are in degrees. Every time the loop runs, your code wrongly converts degrees into radians. Do the conversion before the main loop.
(Speculation based on a previous answer - I could be wrong) The formulas on MyPhysicsLab seem to contradict those from other sources, e.g. here and here; a quick calculation using Lagrangian mechanics agrees with these two sources and not MPL.

Related

C++ compiles and runs fine but I can't generate any types of C++ outputs after certain point in the code

The code starts with declaring various arrays with a size that is pre-calculated, and will be used in the rest of the program. However, after a certain point in the list of declarations, C++ will fail to generate any output even after a successful compilation. After the comment in the middle of the code, no outputs can be generated. I have tried simple outputs like "cout" and writing in a file.
Edit: I have added a sample output written by one of the answers to demonstrate. The program just runs and does not generate anything. This is the terminal output:
"
PS C:\Users\umroot.COLLAR\projects\CrackHole> g++ .\Peridynamics.cpp -o peri
PS C:\Users\umroot.COLLAR\projects\CrackHole> .\peri.exe
PS C:\Users\umroot.COLLAR\projects\CrackHole>
#include <math.h>
#include <iostream>
#include <vector>
#include <string>
#include <conio.h>
// #include "Ellipse.h"
#include <fstream>
using namespace std;
int main () {
float length = 0.5;
float width = 0.5;
float radiusMajor = 0.05;
float radiusMinor = 0.05;
double ellipseCurvature = radiusMinor * radiusMinor / radiusMajor;
float radiusPath = 0.08;
int dt = 1;
const double ELASTIC_MODULUS = 200e9;
const float POISSON_RATIO = 0.3;
const int NumofDiv_x = 100;
const int NumofDiv_y = 100;
int timeInterval = 2500;
const double appliedPressure = 500e7;
int initialTotalNumMatPoint = NumofDiv_x * NumofDiv_y;
int maxFam = 200;
float dx = length / NumofDiv_x;
float delta = 3.015 * dx;
float thick = dx;
float volCorrRadius = dx / 2;
const double SHEAR_MODULUS = ELASTIC_MODULUS / (2 * (1 + POISSON_RATIO));
const double BULK_MODULUS = ELASTIC_MODULUS / (2 * (1 - POISSON_RATIO));
const double ALPHA = 0.5 * (BULK_MODULUS - 2 * SHEAR_MODULUS);
float area = dx * dx;
float volume = area * thick;
const float BCD = 2 / (M_PI * thick * pow(delta, 4));
int temp = floor(9 * M_PI * initialTotalNumMatPoint);
float nodeFam[100000][3] = {0.0};
int nnum = 0;
float coord_excess[initialTotalNumMatPoint][2] = {0.0};
int path_horizontal[NumofDiv_x] = {0};
// Ellipse centerHole(0, 0, radiusMajor, radiusMinor);
// Ellipse leftTip((-1) * radiusMajor, 0, 0.005, 0.005);
// Ellipse rightTip(radiusMajor, 0, 0.005, 0.005);
float coordx = 0.0;
float coordy = 0.0;
int counter = 0;
for (int i = 0; i < NumofDiv_x; i++) {
for (int j = 0; j < NumofDiv_y; j++) {
coordx = (length / 2) * (-1) + (dx / 2) + i * dx;
coordy = (width / 2) * (-1) + (dx/2) + j * dx;
// if (centerHole.InEllipse(coordx, coordy)){
// continue;
// }
if (abs(coordy) <= dx && coordx >= 0) {
path_horizontal[counter] = nnum;
counter++;
}
coord_excess[nnum][0] = coordx;
coord_excess[nnum][1] = coordy;
nnum++;
}
}
int totalNumMatPoint = nnum;
float coord[totalNumMatPoint][2] = {0.0};
for (int j = 0; j < 2; j++ ) {
for (int i = 0; i < totalNumMatPoint; i++) {
coord[i][j] = coord_excess[i][j];
}
}
int numFam[totalNumMatPoint] = {0};
int pointFam[totalNumMatPoint] = {0};
float PDForce[totalNumMatPoint][2] = {0.0};
float bodyForce[totalNumMatPoint][2] = {0.0};
float PDforceold[totalNumMatPoint][2] = {0.0};
float PD_SED_Distortion[totalNumMatPoint][2] = {0.0};
float surCorrFactorDilatation[totalNumMatPoint][2] = {0.0};
float surCorrFactorDistorsion[totalNumMatPoint][2] = {0.0};
float disp[totalNumMatPoint][2] = {0.0};
float totalDisp[totalNumMatPoint][2] = {0.0};
float vel[totalNumMatPoint][2] = {0.0};
// AFTER THIS POINT DOWNWARDS, NO OUTPUTS WILL BE GENERATED
float velhalfold[totalNumMatPoint][2] = {0.0};
float velhalf[totalNumMatPoint][2] = {0.0};
float massvec[totalNumMatPoint][2] = {0.0};
float PD_SED_Dilatation[totalNumMatPoint][2] = {0.0};
float PD_SED_Dilatation_Fixed[totalNumMatPoint][2] = {0.0};
int checkTime[timeInterval] = {0};
float steadyCheck_x[timeInterval] = {0.0};
float steadyCheck_y[timeInterval] = {0.0};
float relPositionVector = 0.0;
for (int j = 0; j < 2; j++ ) {
for (int i = 0; i < totalNumMatPoint; i++) {
coord[i][j] = coord_excess[i][j];
std::cout << coord[i][j] << std::endl;
}
}
Your code, as is, is not "outputting" anything. I compiled and ran your code and added std::cout statements below and above your comment "AFTER THIS POINT DOWNWARDS, NO OUTPUTS WILL BE GENERATED". This successfully writes to stdout.
If, for example, you wanted to output all the values in the coords array you could do something like this while you are building it:
for (int j = 0; j < 2; j++ ) {
for (int i = 0; i < totalNumMatPoint; i++) {
coord[i][j] = coord_excess[i][j];
std::cout << coord[i][j] << std::endl;
}
}
I used another PC with a different OS (i.e. Ubuntu) and it is running fine. Not sure what the problem was. Probably something run with my compiler and/or editor on the first computer.

C++ Function being Skipped [duplicate]

This question already has answers here:
Calling a function in main
(4 answers)
Closed 4 years ago.
So I'm trying to make a simple pool ball simulation, and when trying to check the collision between balls, my bounce function is being skipped in the loop. There should be a display on the console with the random letters in the function bounce in the PoolTable.cpp file, but its skipped and doesn't process the hits or output the text to the console. Not sure why its not running the function. No warnings. No errors. compiles fine. Im on windows machine, using code blocks, and the GLUT library/project.
Walkthrough
So I initialize and place the balls with the constructor. Then I draw the balls on the screen with the drawBalls function. After drawing the balls, i update their positions and move them with moveBalls function. After moving each ball, while still in the moveball function, I check for collisions with checkCollisions function. checkCollisions then starts two for loops, but never runs the bounce function, as the balls don't bounce off eachother, and the cout isn't printed in the terminal. for some reason it is skipped.
PoolTable.cpp
#include "PoolTable.h"
#include "poolball.h"
#include "Graphics.h"
#include <iostream>
using namespace std;
#include <cmath>
PoolTable::PoolTable( int x){
placeBalls( x );
}
void PoolTable::placeBalls( int x ){
number_of_balls = x;
for( int i = 0; i < x; i++){
balls[i].setX( balls[i].getRadius() + i * 20 );
balls[i].setY( balls[i].getRadius() + i * 30 );
}
}
double find_angle(double vx, double vy) {
// determine the angle between poolballs when they collide
double t; double PI = acos(-1.0);
if(vx < 0) // vertical collision
t = PI + atan(vy/vx);
else if(vx > 0.0 && vy >= 0.0) // 1st quardant collision
t = atan(vy/vx);
else if(vx > 0.0 && vy < 0.0) //
t = 2.0*PI + atan(vy/vx);
else if( vx == 0.0 && vy == 0.0)
t = 0.0;
else if(vx == 0 && vy >= 0.0)
t = PI/2.0;
else
t = 1.5 * PI;
return t;
}
void PoolTable::bounce(int i, int j) {
cout << "klasdjflkadsjflkasjfsadk" << endl;
double PI = acos(-1.0);
double x1 = balls[i].getX();
double y1 = balls[i].getY();
double x2 = balls[j].getX();
double y2 = balls[j].getY();
double dx = x2 - x1;
double dy = y2 - y1;
double dist = sqrt(dx*dx+dy*dy);
// did a collision occur
if(dist <= 2 * balls[i].getRadius()) {
double phi; // angle between the two ball centers
if(dx == 0.0)
phi = PI/2.0;
else
phi = atan2 (dy, dx);
// now compute the total velocities of the two balls
double vx1 = balls[i].xSpeed;
double vy1 = balls[i].getYSpeed();
double v1total = sqrt(vx1*vx1 + vy1*vy1);
double vx2 = balls[j].getXSpeed();
double vy2 = balls[j].getYSpeed();
double v2total = sqrt(vx2*vx2 + vy2*vy2);
// find the angle of each ball's velocity
double ang1 = find_angle(vx1,vy1);
double ang2 = find_angle(vx2,vy2);
// transform velocities into normal.tangential components
double v1xr = v1total * cos(ang1 - phi);
double v1yr = v1total * sin(ang1 - phi);
double v2xr = v2total * cos(ang2 - phi);
double v2yr = v2total * sin(ang2 - phi);
// now find the final velocities (assuming equal mass)
double v1fxr = v2xr;
double v2fxr = v1xr;
double v1fyr = v1yr;
double v2fyr = v2yr;
// reset the velocities
balls[i].setXSpeed(cos(phi)*v1fxr + cos(phi+PI/2)*v1fyr);
balls[i].setYSpeed(sin(phi)*v1fxr + sin(phi+PI/2)*v1fyr);
balls[j].setXSpeed(cos(phi)*v2fxr + cos(phi+PI/2)*v2fyr);
balls[j].setYSpeed(sin(phi)*v2fxr + sin(phi+PI/2)*v2fyr);
}
}
void PoolTable::checkCollisions(void){
for( int i = 0; i < number_of_balls; i++){
for( int j = i + 1; j < number_of_balls; j++){
bounce(i, j);
}
}
}
void PoolTable::moveBalls(void){
for( int i = 0; i < number_of_balls; i++){
balls[i].move();
void checkCollisions();
}
}
void PoolTable::drawBalls(void){
for( int i = 0; i < number_of_balls; i++){
balls[i].draw();
}
}
void checkCollisions(); (in moveBalls) is a function prototype, not a function call. Remove the void.

My Gravitational Pull Algorithm behaves Oddly in Certain Situations

While trying to create my own physics engine (don't try persuading me not to), I decided to create a class for each pixel, called Particle, this system has an x and a y, and a x and y velocity, as shown below. Unfortunately, the code for calculateGravitationalVelocity doesn't abide by the laws of physics in certain situations. For example, if the x of the particle and the x of the other particle is the same, the particle will fall towards the object realistically, but when the particle gets too close, it pings off towards the positive x. I am only going to include the class source code, but I can include the source code of the other file, though it is partly written in SFML
Particle.cpp:
#include <iostream>
#include <string>
#include <math.h>
class Particle
{
private:
//Coords:
double x, y;
//Velocities:
double xVelocity = 0;
double yVelocity = 0;
//Material:
std::string material = "Generic";
//Mass:
double mass = 0;
public:
//Coords:
void setCoords(double, double);
float getCoords(char);
//Velocities:
void giveVelocity(char, float);
void setVelocity(char, float);
float getVelocity(char);
//Gravitational Velocity:
void calculateGravitationalVelocity(Particle);
//Material:
void setMaterial(std::string);
std::string getMaterial();
//Mass:
void setMass(double);
double getMass();
//Update:
void update();
};
//Coords:
void Particle::setCoords(double newX, double newY)
{
x = newX;
y = newY;
}
float Particle::getCoords(char axis)
{
if (axis == 'x')
{
//return floor(x);
return x;
}
else if (axis == 'y')
{
//return floor(y);
return y;
}
}
//Velocities:
void Particle::giveVelocity(char axis, float addedVelocity)
{
if (axis == 'x') {xVelocity = xVelocity + addedVelocity;}
else if (axis == 'y') {yVelocity = yVelocity + addedVelocity;}
}
void Particle::setVelocity(char axis, float newVelocity)
{
if (axis == 'x') {xVelocity = newVelocity;}
else if (axis == 'y') {yVelocity = newVelocity;}
}
float Particle::getVelocity(char axis)
{
if (axis == 'x') {return xVelocity;}//floor(xVelocity);}
else if (axis == 'y') {return xVelocity;}//floor(yVelocity);}
}
//Gravitational Velocity (Where the problems probably are):
void Particle::calculateGravitationalVelocity(Particle distantParticle)
{
//Physics constants:
const double pi = 3.14159265359; //Pi
const double G = 0.00000000006673; //Gravitational Constant (or Big G)
//Big Triangle Trigonometry:
//Get coords of moving particle:
double x1 = x;
double y1 = y;
//Get coords of particle with gravity:
double x2 = distantParticle.getCoords('x');
double y2 = distantParticle.getCoords('y');
if (x1 != x2)
{
//Work out the angle:
double A = atan((y2 - y1) / (x2 - x1)) * 180 / pi;
//Remove the minus sign:
A = fabs(A);
//Small Triangle Trigonometry:
//Work out the hypotenuse of the big triangle:
double hyp = sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
//Work out the gravitational field (hyp of small triangle):
long double gravitationalField = G * (distantParticle.getMass() / pow(hyp, 2));
//For testing purposes:
//std::cout << "X: " << (cos(A) * gravitationalField) / 1000 << std::endl;
//std::cout << "Y: " << (sin(A) * gravitationalField) / 1000 << std::endl;
//Work out the X velocity:
xVelocity = xVelocity + (cos(A) * gravitationalField) / 1000;
//Work out the Y velocity:
yVelocity = yVelocity + (sin(A) * gravitationalField) / 1000;
}
else
{
//Work out the hypotenuse of the big triangle:
double hyp = sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
//Work out the gravitational field (hyp of small triangle):
long double gravitationalField = G * (distantParticle.getMass() / pow(hyp, 2));
yVelocity = yVelocity + gravitationalField / 1000;
}
}
//Material:
void Particle::setMaterial(std::string newMaterialType)
{
material = newMaterialType;
}
std::string Particle::getMaterial()
{
return material;
}
//Mass:
void Particle::setMass(double newMass)
{
mass = newMass;
}
double Particle::getMass()
{
return mass;
}
//Update:
void Particle::update()
{
x = x + xVelocity;
y = y + yVelocity;
}
I am sorry for the very open question, and it probably goes against the rules somewhere, but I couldn't find it. The code for working out mostly uses a two triangles to make a x and y velocity. Here is an image of what I was hoping the code would do as a triangle (sorry it doesn't look great, but I like using a whiteboard):
You don't need to perform any trigonometric calculation.
...
//Get coords of particle with gravity:
double x2 = distantParticle.getCoords('x');
double y2 = distantParticle.getCoords('y');
// Get difference vector
double rx = x1 - x2;
double ry = y1 - y2;
// square of distance
double r2 = rx * rx + ry * ry;
// distance
double r = sqrt (r2);
if (r != 0) {
// normalize difference vector
double ux = rx / r;
double uy = ry / r;
// acceleration of gravity
double a = - G * distantParticle.getMass() / r2;
xVelocity += a * ux / 1000;
yVelocity += a * uy / 1000;
}
}

N-body simulation not working

I am trying to write a code to solve the n-body problem and i run into trouble when using an array with all the bodies instead of using the different bodies seperately. I currently have no idea what's going wrong in my code but when i plot x in function of y for any body i get a straight line which obviously isn't right.
This is my current code:
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <fstream>
#define h 10000.0
#define N 3
#define G 6.67384*pow(10.0,-11)
using namespace std;
class particle{
public:
double kx1,kx2,kx3,kx4, kv1, kv2, kv3, kv4;
double ky1, ky2, ky3, ky4, kvy1, kvy2, kvy3, kvy4;
double x,y,vx,vy,m;
double dist(particle aap){
double dx = x - aap.x;
double dy = y - aap.y;
return sqrt(pow(dx,2.0)+pow(dy,2.0));
}
double g(double x1, double y1,particle aap){
return G*aap.m*(aap.x-x1)/pow(dist(aap),3.0);
}
double p(double x1, double y1, particle aap){
return G*aap.m*(aap.y-y1)/pow(dist(aap),3.0);
}
void update(){ //object advances 1 step
x = x + (1/6.0)*(kx1+2*kx2+2*kx3+kx4);
vx = vx + (1/6.0)*(kv1+2*kv2+2*kv3+kv4);
y = y + (1/6.0)*(ky1+2*ky2+2*ky3+ky4);
vy = vy + (1/6.0)*(kvy1+2*kvy2+2*kvy3+kvy4);
}
void create(double x1, double y1, double vx1, double vy1, double m1){
x = x1;
y = y1;
vx = vx1;
vy = vy1;
m =m1;
}
bool operator ==(particle &other){
if(x == other.x && y == other.y && vx == other.vx && vy == other.vy){
return true;
}
}
};
particle bodies[N];
void set(particle (&bodies)[N]){
bodies[0].create(1, 1, -2, 1, 2*pow(10.0,30));
bodies[1].create(2870671*pow(10.0,6), 0, 0, 6800, 8.6810*pow(10.0,25));
bodies[2].create(4498542*pow(10.0,6),0 ,0, 5430, 1.0243*pow(10.0,26));
}
double xforce(double x1, double y1, particle aap, particle bodies[N]){ //force in the x- direction
double fx = 0;
for (int i = 0; i <= N; i++){
if (bodies[i] == aap ){;}
else{
fx += aap.g(x1,y1,bodies[i]);
}
}
return fx;
}
double yforce(double x1, double y1, particle aap, particle bodies[N]){ //force in the y- direction
double fy = 0;
for (int i = 0; i <= N; i++){
if (bodies[i] == aap) {;}
else{
fy += aap.p(x1,y1,bodies[i]);
}
}
return fy;
}
void corr(double t, particle bodies[N]){ //runge kutta 4
for(int i =0; i <= N; i++){
bodies[i].kx1 = t*bodies[i].vx;
bodies[i].kv1 = t*xforce(bodies[i].x, bodies[i].y, bodies[i], bodies);
bodies[i].ky1 = t*bodies[i].vy;
bodies[i].kvy1 = t*yforce(bodies[i].x, bodies[i].y, bodies[i], bodies);
bodies[i].kx2 = t*(bodies[i].vx + 0.5*bodies[i].kv1);
bodies[i].kv2 = t*xforce(bodies[i].x + 0.5*bodies[i].kx1, bodies[i].y + 0.5*bodies[i].ky1, bodies[i], bodies);
bodies[i].ky2 = t*(bodies[i].vy + 0.5*bodies[i].kvy1);
bodies[i].kvy2 = t*yforce(bodies[i].x + 0.5*bodies[i].kx1, bodies[i].y + 0.5*bodies[i].ky1, bodies[i], bodies);
bodies[i].kx3 = t*(bodies[i].vx+ 0.5*bodies[i].kv2);
bodies[i].kv3 = t*xforce(bodies[i].x + 0.5*bodies[i].kx2, bodies[i].y + 0.5*bodies[i].ky2, bodies[i], bodies);
bodies[i].ky3 = t*(bodies[i].vy+ 0.5*bodies[i].kvy2);
bodies[i].kvy3 = t*yforce(bodies[i].x + 0.5*bodies[i].kx2, bodies[i].y + 0.5*bodies[i].ky2,bodies[i], bodies);
bodies[i].kx4 = t*(bodies[i].vx + bodies[i].kv3);
bodies[i].kv4 = t*xforce(bodies[i].x+ bodies[i].kx3, bodies[i].y + bodies[i].ky3, bodies[i], bodies);
bodies[i].ky4 = t*(bodies[i].vy + bodies[i].kvy3);
bodies[i].kvy4 = t*yforce(bodies[i].x + bodies[i].kx3, bodies[i].y + bodies[i].ky3, bodies[i], bodies);
}
}
void calculate(particle (&bodies)[N]){
set(bodies);
ofstream file;
file.open("tester.txt");
for(int i =0; i <=50000; i++){
corr(h, bodies);
for(int j = 0; j <= N; j++){
bodies[j].update();
}
if( i%1000 == 0){
file << i*h;
for(int j = 0; j <=N ; j++){
file <<" "<<bodies[j].x << " "<< bodies[j].y;
}
file <<" "<<"\n";
}
else{;}
}
file.close();
}
int main()
{
calculate(bodies);
system("pause");
return 0;
}
The problem probably lies outside of the class particle since the program worked before i started using the array bodies. Any suggestions for non essential improvements are ofcourse welcome. Another thing i'm trying to do is use std::vector instead of an array but i don't know how i could define a vector outside my functions like i defined the array bodies.
For a start, all of your i <= N are wrong, because your loop will execute 4 times (0, 1, 2, 3) instead of 3 for i < N.
You are likely experiencing energy drift, as RK4 is not symplectic, and the n-body problem is a Hamiltonian system. I had this same problem trying to use RK4 for a solar system n-body as well. So did this person. You should try another symplectic numerical method like Euler, Verlet, Ruth's 3rd, or Ruth's 4th order symplectic integrator.

c++ nbody simulation passing array as reference

I have a problem when trying to write a code to solve the nbody problem when using an array which contains all the bodies. My code doesn't do the right thing and i have no idea where it goes wrong though i suspect it has something to do with passing the array as a reference. To make it easier to spot my mistakes i will inculde a working version of the code which doesn't use the array containing all the bodies in the same way. The following is the code which doesn't work( when calculataing the orbit of a body you get a straight line instead of an ellipse with this code):
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <fstream>
#define h 10000.0 // size of the timestep
#define N 3 // number of bodies
#define G 6.67384*pow(10.0,-11) // gravitational constant
using namespace std;
class particle{
public:
double kx1,kx2,kx3,kx4, kv1, kv2, kv3, kv4;
double ky1, ky2, ky3, ky4, kvy1, kvy2, kvy3, kvy4;
double x,y,vx,vy,m;
double dist(particle body){
double dx = x - body.x;
double dy = y - body.y;
return sqrt(pow(dx,2.0)+pow(dy,2.0));
}
double g(double x1, double y1,particle body){
return G*body.m*(body.x-x1)/pow(dist(body),3.0);
}
double p(double x1, double y1, particle body){
return G*body.m*(body.y-y1)/pow(dist(body),3.0);
}
void update(){ //object advances 1 step
x = x + (1/6.0)*(kx1+2*kx2+2*kx3+kx4);
vx = vx + (1/6.0)*(kv1+2*kv2+2*kv3+kv4);
y = y + (1/6.0)*(ky1+2*ky2+2*ky3+ky4);
vy = vy + (1/6.0)*(kvy1+2*kvy2+2*kvy3+kvy4);
}
void create(double x1, double y1, double vx1, double vy1, double m1){ //choose the inital conditions for a new object
x = x1;
y = y1;
vx = vx1;
vy = vy1;
m =m1;
}
bool operator ==(particle &other){
if(x == other.x && y == other.y && vx == other.vx && vy == other.vy){
return true;
}
}
};
particle bodies[N];
void set(particle (&bodies)[N]){
bodies[0].create(1, 1, -2, 1, 2*pow(10.0,30));
bodies[1].create(2870671*pow(10.0,6), 0, 0, 6800, 8.6810*pow(10.0,25));
bodies[2].create(4498542*pow(10.0,6),0 ,0, 5430, 1.0243*pow(10.0,26));
}
double xforce(double x1, double y1, particle body, particle bodies[N]){ //force in the x- direction
double fx = 0;
for (int i = 0; i < N; i++){
if (bodies[i] == body ){;}
else{
fx += body.g(x1,y1,bodies[i]);
}
}
return fx;
}
double yforce(double x1, double y1, particle body, particle bodies[N]){ //force in the y- direction
double fy = 0;
for (int i = 0; i < N; i++){
if (bodies[i] == body) {;}
else{
fy += body.p(x1,y1,bodies[i]);
}
}
return fy;
}
void corr(double t, particle bodies[N]){ //runge kutta 4
for(int i =0; i <= N; i++){
bodies[i].kx1 = t*bodies[i].vx;
bodies[i].kv1 = t*xforce(bodies[i].x, bodies[i].y, bodies[i], bodies);
bodies[i].ky1 = t*bodies[i].vy;
bodies[i].kvy1 = t*yforce(bodies[i].x, bodies[i].y, bodies[i], bodies);
bodies[i].kx2 = t*(bodies[i].vx + 0.5*bodies[i].kv1);
bodies[i].kv2 = t*xforce(bodies[i].x + 0.5*bodies[i].kx1, bodies[i].y + 0.5*bodies[i].ky1, bodies[i], bodies);
bodies[i].ky2 = t*(bodies[i].vy + 0.5*bodies[i].kvy1);
bodies[i].kvy2 = t*yforce(bodies[i].x + 0.5*bodies[i].kx1, bodies[i].y + 0.5*bodies[i].ky1, bodies[i], bodies);
bodies[i].kx3 = t*(bodies[i].vx+ 0.5*bodies[i].kv2);
bodies[i].kv3 = t*xforce(bodies[i].x + 0.5*bodies[i].kx2, bodies[i].y + 0.5*bodies[i].ky2, bodies[i], bodies);
bodies[i].ky3 = t*(bodies[i].vy+ 0.5*bodies[i].kvy2);
bodies[i].kvy3 = t*yforce(bodies[i].x + 0.5*bodies[i].kx2, bodies[i].y + 0.5*bodies[i].ky2,bodies[i], bodies);
bodies[i].kx4 = t*(bodies[i].vx + bodies[i].kv3);
bodies[i].kv4 = t*xforce(bodies[i].x+ bodies[i].kx3, bodies[i].y + bodies[i].ky3, bodies[i], bodies);
bodies[i].ky4 = t*(bodies[i].vy + bodies[i].kvy3);
bodies[i].kvy4 = t*yforce(bodies[i].x + bodies[i].kx3, bodies[i].y + bodies[i].ky3, bodies[i], bodies);
}
}
void calculate(particle (&bodies)[N]){
set(bodies);
ofstream file;
file.open("tester.txt");
for(int i =0; i <=50000; i++){
corr(h, bodies);
for(int j = 0; j <= N; j++){
bodies[j].update();
}
if( i%1000 == 0){
file << i*h;
for(int j = 0; j <=N ; j++){
file <<" "<<bodies[j].x << " "<< bodies[j].y;
}
file <<" "<<"\n";
}
else{;}
}
file.close();
}
int main()
{
calculate(bodies);
system("pause");
return 0;
}
Here is the working version of the code, both are supposed to solve the same problem:
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <fstream>
#define h 10000.0
#define N 3
#define G 6.67384*pow(10.0,-11)
using namespace std;
class particle{
public:
double kx1,kx2,kx3,kx4, kv1, kv2, kv3, kv4;
double ky1, ky2, ky3, ky4, kvy1, kvy2, kvy3, kvy4;
double x,y,vx,vy,m;
double dist(particle body){
double dx = x - body.x;
double dy = y - body.y;
return sqrt(pow(dx,2.0)+pow(dy,2.0));
}
double g(double x1, double y1,particle body){
return G*body.m*(body.x-x1)/pow(dist(body),3.0);
}
double p(double x1, double y1, particle body){
return G*body.m*(body.y-y1)/pow(dist(body),3.0);
}
void update(){
x = x + (1/6.0)*(kx1+2*kx2+2*kx3+kx4);
vx = vx + (1/6.0)*(kv1+2*kv2+2*kv3+kv4);
y = y + (1/6.0)*(ky1+2*ky2+2*ky3+ky4);
vy = vy + (1/6.0)*(kvy1+2*kvy2+2*kvy3+kvy4);
}
void create(double x1, double y1, double vx1, double vy1, double m1){
x = x1;
y = y1;
vx = vx1;
vy = vy1;
m =m1;
}
bool operator ==(particle &other){
if(x == other.x && y == other.y && vx == other.vx && vy == other.vy){
return true;
}
}
bool operator !=(particle &other){
if(x != other.x || y != other.y || vx != other.vx || vy != other.vy){
return true;
}
}
};
particle zon, uranus, neptunus;
particle closest[] = {uranus, neptunus};
void set(){
zon.create(1, 1, -2, 1, 2*pow(10.0,30));
uranus.create(2870671*pow(10.0,6), 0, 0, 6800, 8.6810*pow(10.0,25));
neptunus.create(4498542*pow(10.0,6),0 ,0, 5430, 1.0243*pow(10.0,26));
}
double xforce(double x1, double y1, particle body){
particle bodies[] = {zon, uranus, neptunus};
double fx = 0;
for (int i = 0; i < 3; i++){
if (bodies[i] == body ){;}
else{
fx += body.g(x1,y1,bodies[i]);
}
}
return fx;
}
double yforce(double x1, double y1, particle body){
particle bodies[] = {zon, uranus, neptunus};
double fy = 0;
for (int i = 0; i <= 3; i++){
if (bodies[i] == body) {;}
else{
fy += body.p(x1,y1,bodies[i]);
}
}
return fy;
}
void corr(particle& body, double t){
body.kx1 = t*body.vx;
body.kv1 = t*xforce(body.x, body.y, body);
body.ky1 = t*body.vy;
body.kvy1 = t*yforce(body.x, body.y, body);
body.kx2 = t*(body.vx + 0.5*body.kv1);
body.kv2 = t*xforce(body.x + 0.5*body.kx1, body.y + 0.5*body.ky1, body);
body.ky2 = t*(body.vy + 0.5*body.kvy1);
body.kvy2 = t*yforce(body.x + 0.5*body.kx1, body.y + 0.5*body.ky1, body);
body.kx3 = t*(body.vx+ 0.5*body.kv2);
body.kv3 = t*xforce(body.x + 0.5*body.kx2, body.y + 0.5*body.ky2, body);
body.ky3 = t*(body.vy+ 0.5*body.kvy2);
body.kvy3 = t*yforce(body.x + 0.5*body.kx2, body.y + 0.5*body.ky2,body);
body.kx4 = t*(body.vx+body.kv3);
body.kv4 = t*xforce(body.x+ body.kx3, body.y + body.ky3, body);
body.ky4 = t*(body.vy + body.kvy3);
body.kvy4 = t*yforce(body.x + body.kx3, body.y + body.ky3, body);
}
void bereken(){
set();
ofstream file;
file.open("tester.txt");
for(int i =0; i <=50000; i++){
corr(zon, h);
corr(uranus, h);
corr(neptunus, h);
zon.update();
uranus.update();
neptunus.update();
if( i%1000 == 0){
file << i*h <<" "<<zon.x << " "<< zon.y <<" "<<uranus.x<<" " <<uranus.y <<" "<< neptunus.x<<" "<<neptunus.y<<" "<<"\n";
}
else{;}
}
file.close();
}
int main()
{
bereken();
system("pause");
return 0;
}
One problem is that you are overflowing your bodies[] array in 3 places:
#define N 3
particle bodies[N];
for (int i = 0; i <= N; i++) {
bodies[i].x = ... // Oops, access bodies[3] which doesn't exist
The correct loop, which you do use in two places, is:
for (int i = 0; i < N; i++) { // 0 to < N