166 lines
5.5 KiB
C++
166 lines
5.5 KiB
C++
#include <iostream>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <boost/program_options.hpp>
|
|
#include <nlohmann/json.hpp>
|
|
#include "simulator.hpp"
|
|
#include "body.hpp"
|
|
#include "units.hpp"
|
|
|
|
using json = nlohmann::json;
|
|
namespace po = boost::program_options;
|
|
|
|
struct SimulationConfig {
|
|
std::string config_file;
|
|
std::string output_file;
|
|
int steps;
|
|
int steps_per_save;
|
|
Decimal step_size; // Changed to Decimal for normalized time
|
|
bool overwrite_output;
|
|
double simulation_time; // in seconds (real time)
|
|
};
|
|
|
|
// Convert time string to seconds
|
|
double parse_time(const std::string& time_str) {
|
|
std::string value_str = time_str;
|
|
std::string unit;
|
|
|
|
// Extract the unit (last character)
|
|
if (!time_str.empty()) {
|
|
unit = time_str.back();
|
|
value_str = time_str.substr(0, time_str.length() - 1);
|
|
}
|
|
|
|
double value = std::stod(value_str);
|
|
|
|
// Convert to seconds based on unit
|
|
switch (unit[0]) {
|
|
case 's': return value; // seconds
|
|
case 'm': return value * 60; // minutes
|
|
case 'h': return value * 3600; // hours
|
|
case 'd': return value * 86400; // days
|
|
default: throw std::runtime_error("Invalid time unit. Use s/m/h/d for seconds/minutes/hours/days");
|
|
}
|
|
}
|
|
|
|
SimulationConfig parse_command_line(int argc, char* argv[]) {
|
|
SimulationConfig config;
|
|
double temp_step_size; // Temporary variable for parsing
|
|
|
|
po::options_description desc("Orbital Simulator Options");
|
|
desc.add_options()
|
|
("help,h", "Show help message")
|
|
("config,c", po::value<std::string>(&config.config_file)->required(),
|
|
"Path to planet configuration file (JSON)")
|
|
("output,o", po::value<std::string>(&config.output_file)->required(),
|
|
"Path to output file")
|
|
("time,t", po::value<std::string>()->required(),
|
|
"Simulation time with unit (e.g., 1h for 1 hour, 30m for 30 minutes, 2d for 2 days)")
|
|
("step-size,s", po::value<double>(&temp_step_size)->default_value(1.0),
|
|
"Simulation step size in seconds")
|
|
("steps-per-save,p", po::value<int>(&config.steps_per_save)->default_value(100),
|
|
"Number of steps between saves")
|
|
("overwrite,w", po::bool_switch(&config.overwrite_output),
|
|
"Overwrite output file if it exists");
|
|
|
|
po::variables_map vm;
|
|
try {
|
|
po::store(po::parse_command_line(argc, argv, desc), vm);
|
|
|
|
if (vm.count("help")) {
|
|
std::cout << desc << "\n";
|
|
exit(0);
|
|
}
|
|
|
|
po::notify(vm);
|
|
|
|
// Parse simulation time
|
|
config.simulation_time = parse_time(vm["time"].as<std::string>());
|
|
|
|
// Convert step size to Decimal and normalize
|
|
config.step_size = norm_time(Decimal(temp_step_size));
|
|
|
|
// Calculate number of steps based on normalized time and step size
|
|
config.steps = static_cast<int>(norm_time(config.simulation_time) / config.step_size);
|
|
|
|
} catch (const po::error& e) {
|
|
std::cerr << "Error: " << e.what() << "\n";
|
|
std::cerr << desc << "\n";
|
|
exit(1);
|
|
} catch (const std::exception& e) {
|
|
std::cerr << "Error: " << e.what() << "\n";
|
|
std::cerr << desc << "\n";
|
|
exit(1);
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
std::vector<Body> load_planets(const std::string& config_file) {
|
|
std::ifstream f(config_file);
|
|
if (!f.is_open()) {
|
|
throw std::runtime_error("Could not open config file: " + config_file);
|
|
}
|
|
|
|
json j;
|
|
f >> j;
|
|
|
|
std::vector<Body> bodies;
|
|
for (const auto& planet : j["planets"]) {
|
|
std::string name = planet["name"];
|
|
double mass = planet["mass"];
|
|
|
|
std::vector<double> pos = planet["position"];
|
|
std::vector<double> vel = planet["velocity"];
|
|
|
|
if (pos.size() != 3 || vel.size() != 3) {
|
|
throw std::runtime_error("Position and velocity must be 3D vectors");
|
|
}
|
|
|
|
// Normalize units before creating the body
|
|
Position position{norm_pos(pos[0]), norm_pos(pos[1]), norm_pos(pos[2])};
|
|
Velocity velocity{norm_vel(vel[0]), norm_vel(vel[1]), norm_vel(vel[2])};
|
|
Mass normalized_mass = norm_mass(mass);
|
|
|
|
bodies.emplace_back(position, velocity, normalized_mass, name);
|
|
std::cout << "Loaded " << name << " with mass " << mass << " kg\n";
|
|
}
|
|
|
|
return bodies;
|
|
}
|
|
|
|
int main(int argc, char* argv[]) {
|
|
try {
|
|
// Parse command line options
|
|
auto config = parse_command_line(argc, argv);
|
|
|
|
// Load planets from config file
|
|
auto bodies = load_planets(config.config_file);
|
|
|
|
// Create and run simulator
|
|
Simulator simulator(
|
|
bodies,
|
|
config.step_size,
|
|
config.steps_per_save,
|
|
config.output_file,
|
|
0, // current_step
|
|
config.overwrite_output
|
|
);
|
|
|
|
std::cout << "Starting simulation with " << bodies.size() << " bodies\n";
|
|
std::cout << "Step size: " << real_time(config.step_size) << " seconds\n";
|
|
std::cout << "Simulation time: " << config.simulation_time << " seconds\n";
|
|
std::cout << "Total steps: " << config.steps << "\n";
|
|
std::cout << "Steps per save: " << config.steps_per_save << "\n";
|
|
|
|
simulator.run(config.steps);
|
|
|
|
std::cout << "Simulation completed successfully\n";
|
|
return 0;
|
|
|
|
} catch (const std::exception& e) {
|
|
std::cerr << "Error: " << e.what() << std::endl;
|
|
return 1;
|
|
}
|
|
}
|