/*
* Copyright (c) 2025 Konrad Gębik
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
*/
#include
#include
#include
#include
#include
#include
#include "Simulation.hpp"
#include "Vector.hpp"
/*
This program runs a simulation of two bodies moving while interacting gravitationally.
Initial parameters and other options are to be set in a setup.ini file located in the same directory as the executable.
Three output files are created:
-the orbit file, containing raw relative position data of the bodies
-the energy file, containing relative energy differences wrt the initial energy
-the periapsis file, containing angular positions of one of the bodies when their distance reaches a local minimum
*/
//Progress bar properties
unsigned g_progBarUpdateInt = 1000000, g_progBarMaxLength = 50;
//Struct for storing program settings not used in the simulation itself.
struct Settings {
unsigned simLength; //Number of steps to be simulated.
unsigned saveInterval; //How many steps to omit when saving orbit and energy data.
unsigned orbitLimit; //How many data points to save to the orbit file.
};
//Read data from setup file, create Simulation object and save additional settings to info.
Simulation setupFromFile(std::string filename, Settings& info) {
Vector pos = {1, 0, 0}, vel = {0, 0.5, 0};
double deltaTime = 0.01;
double mass1 = 1, mass2 = 0.01, PNorder = 1;
std::string buffer;
std::ifstream inFile(filename);
if (!inFile.good()) {
std::cout << "setup.ini not found!";
throw std::ios_base::failure("setup.ini doesn't exist");
}
//Reading simulation settings. Converts from SI to natural units (c = G = 1, with time measured in SI seconds)
inFile >> buffer >> mass1 >> buffer >> mass2 >> buffer >> deltaTime >> buffer >> PNorder;
mass1 /= 1.49828e10*299792458.*299792458.*299792458.;
mass2 /= 1.49828e10*299792458.*299792458.*299792458.;
inFile >> buffer >> pos.x >> pos.y >> pos.z;
pos = pos / 299792458.;
inFile >> buffer >> vel.x >> vel.y >> vel.z;
vel = vel / 299792458.;
//Reading additional settings
inFile >> buffer >> info.simLength >> buffer >> info.saveInterval >> buffer >> info.orbitLimit;
info.orbitLimit *= info.saveInterval;
return Simulation(mass1, mass2, deltaTime, PNorder, pos, vel);
}
//Run the simulation and save the outputs as specified by info.
void twoBody(Simulation &sim, Settings info) {
Vector previous = {0, 0, 0};
bool isDecreasing = false;
double startEnergy = sim.getEnergy(), energy;
unsigned progBarLength = 0;
std::ofstream outOrbit("orbit.txt");
std::ofstream outPeriapsis("periapsis.txt");
std::ofstream outEnergy("energy.txt");
auto start = std::chrono::steady_clock::now();
for (std::size_t i = 0; i < info.simLength; i++) {
sim.simulate();
//Saving orbit and energy data
if (i % info.saveInterval == 0) {
energy = sim.getEnergy();
outEnergy << sim.getTime() << ' ' << std::abs((startEnergy - energy)/energy) << ' ' << energy << '\n';
if (i < info.orbitLimit) {
outOrbit << sim.getTime() << ' ' << sim.getPosition().x << ' ' << sim.getPosition().y << '\n';
}
}
//Finding and saving periapsis angles relative to the x axis
if (isDecreasing == true && previous.norm() < sim.getPosition().norm()) {
outPeriapsis << sim.getTime() << ' ' << std::atan2(sim.getPosition().y, sim.getPosition().x) << '\n';
isDecreasing = false;
}
else if (isDecreasing == false && previous.norm() > sim.getPosition().norm()) {
isDecreasing = true;
}
//Showing progress bar
if (i % g_progBarUpdateInt == 0) {
progBarLength = g_progBarMaxLength * i / static_cast(info.simLength);
std::cout << '[' + std::string(progBarLength, '#') + std::string(g_progBarMaxLength - progBarLength, ' ') + "] " << i / static_cast(info.simLength) * 100 << "%\r";
std::cout.flush();
}
previous = sim.getPosition();
}
std::cout << '[' + std::string(g_progBarMaxLength, '#') + "] 100%\n" ;
auto end = std::chrono::steady_clock::now();
std::cout << "Simulation took " << std::chrono::duration_cast(end - start).count() << " ms\n";
outOrbit.close();
outPeriapsis.close();
outEnergy.close();
}
int main() {
std::cout << "SIM START\n";
Settings info;
Simulation sim = setupFromFile("setup.ini", info);
twoBody(sim, info);
}