2025-06-02 08:27:45 -04:00

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;
}
}