vibe coding an orbital mechanics simulation to try out claude code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

153 lines
5.6 KiB

#include <catch2/catch_test_macros.hpp>
#include "../src/physics.h"
#include "../src/bodies.h"
#include "../src/config_loader.h"
#include "../src/test_utilities.h"
#include <cmath>
#include <vector>
#define COMET_TEST_VERBOSE_OUTPUT 0
struct ParentChange {
double time_seconds;
double time_days;
int old_parent;
int new_parent;
double distance_to_mars_au;
double distance_to_sun_au;
};
#if COMET_TEST_VERBOSE_OUTPUT
static void print_orbital_snapshots(const std::vector<OrbitalElements>& snapshots,
double expected_sma, double expected_ecc) {
printf("\n=== Comet Orbital Elements Over Time ===\n");
printf("Expected: a = %.1f AU, e = %.1f\n\n", expected_sma, expected_ecc);
for (const auto& elem : snapshots) {
printf("Day %.0f:\n", elem.time_days);
printf(" Semi-major axis: %.6f AU (expected: %.1f AU)\n", elem.semi_major_axis_au, expected_sma);
printf(" Eccentricity: %.6f (expected: %.1f)\n", elem.eccentricity, expected_ecc);
printf(" Distance to Sun: %.4f AU\n", elem.distance_to_sun_au);
printf(" Distance to Mars: %.6f AU\n", elem.distance_to_ref_body_au);
printf(" Velocity: %.2f km/s\n", elem.velocity_magnitude / 1000.0);
printf(" Specific energy: %.3e J/kg\n", elem.specific_energy);
double sma_error = fabs(elem.semi_major_axis_au - expected_sma);
double ecc_error = fabs(elem.eccentricity - expected_ecc);
if (elem.time_days >= 1530 && elem.time_days <= 1540) {
printf(" *** CLOSEST APPROACH TO MARS ***\n");
}
printf(" Error: Δa = %.6f AU, Δe = %.6f\n\n", sma_error, ecc_error);
}
}
static void print_parent_changes(const std::vector<ParentChange>& parent_changes,
int mars_index) {
printf("\n=== Parent Index Changes ===\n");
if (parent_changes.empty()) {
printf("No parent changes detected\n\n");
} else {
for (const auto& change : parent_changes) {
printf("Time: %.0f days (%.0f seconds)\n", change.time_days, change.time_seconds);
printf(" Parent: %d -> %d", change.old_parent, change.new_parent);
if (change.new_parent == mars_index) {
printf(" (Sun -> Mars)");
} else if (change.old_parent == mars_index) {
printf(" (Mars -> Sun)");
}
printf("\n");
printf(" Distance to Mars: %.6f AU\n", change.distance_to_mars_au);
printf(" Distance to Sun: %.6f AU\n\n", change.distance_to_sun_au);
}
}
}
#endif
TEST_CASE("Comet orbital elements and SOI transitions during Mars encounter", "[comet][orbital-elements][soi]") {
const double TIME_STEP = 60.0;
const double SECONDS_PER_DAY = 86400.0;
const double DAYS_TO_SIMULATE = 5000.0;
const double AU = 1.496e11;
#if COMET_TEST_VERBOSE_OUTPUT
const double EXPECTED_SMA = 2.5;
const double EXPECTED_ECC = 0.7;
#endif
SimulationState* sim = create_simulation(10, TIME_STEP);
REQUIRE(load_system_config(sim, "configs/test_simple.toml"));
const int COMET_INDEX = 3;
const int MARS_INDEX = 2;
const int SUN_INDEX = 0;
std::vector<OrbitalElements> snapshots;
std::vector<ParentChange> parent_changes;
int previous_parent = sim->bodies[COMET_INDEX].parent_index;
OrbitalElements initial = calculate_orbital_elements(
&sim->bodies[COMET_INDEX],
&sim->bodies[SUN_INDEX],
&sim->bodies[MARS_INDEX],
sim->time
);
snapshots.push_back(initial);
double checkpoint_days[] = {0, 365, 722, 1444, 1533, 1600, 1700, 2000, 5000};
size_t next_checkpoint = 1;
double max_time = DAYS_TO_SIMULATE * SECONDS_PER_DAY;
while (sim->time < max_time) {
update_simulation(sim);
int current_parent = sim->bodies[COMET_INDEX].parent_index;
if (current_parent != previous_parent) {
ParentChange change;
change.time_seconds = sim->time;
change.time_days = sim->time / SECONDS_PER_DAY;
change.old_parent = previous_parent;
change.new_parent = current_parent;
change.distance_to_sun_au = vec3_magnitude(sim->bodies[COMET_INDEX].position) / AU;
change.distance_to_mars_au = vec3_distance(sim->bodies[COMET_INDEX].position,
sim->bodies[MARS_INDEX].position) / AU;
parent_changes.push_back(change);
previous_parent = current_parent;
}
double current_days = sim->time / SECONDS_PER_DAY;
if (next_checkpoint < sizeof(checkpoint_days)/sizeof(checkpoint_days[0]) &&
current_days >= checkpoint_days[next_checkpoint]) {
OrbitalElements elem = calculate_orbital_elements(
&sim->bodies[COMET_INDEX],
&sim->bodies[SUN_INDEX],
&sim->bodies[MARS_INDEX],
sim->time
);
snapshots.push_back(elem);
next_checkpoint++;
}
}
#if COMET_TEST_VERBOSE_OUTPUT
print_orbital_snapshots(snapshots, EXPECTED_SMA, EXPECTED_ECC);
print_parent_changes(parent_changes, MARS_INDEX);
#endif
REQUIRE(parent_changes.size() > 0);
bool found_mars_transition = false;
for (const auto& change : parent_changes) {
if (change.new_parent == MARS_INDEX || change.old_parent == MARS_INDEX) {
found_mars_transition = true;
REQUIRE(fabs(change.time_days - 1550.0) < 50.0);
INFO("Mars encounter at day " << change.time_days << ", distance " << change.distance_to_mars_au << " AU");
}
}
REQUIRE(found_mars_transition);
destroy_simulation(sim);
}