Browse Source

Add trailing whitespace enforcement with pre-commit hook and document style rule

main
cinnaboot 5 months ago
parent
commit
679ae92e08
  1. 3
      AGENTS.md
  2. 4
      docs/session_summaries/2026-01-09-hierarchical-coordinate-frames-phase1-2.md
  3. 4
      docs/session_summaries/2026-01-20-hohmann-transfer-fix.md
  4. 4
      docs/session_summaries/2026-01-28-documentation-update-relative-rendering.md
  5. 120
      tests/informational/test_time_step_stability.cpp

3
AGENTS.md

@ -17,6 +17,9 @@
- Minimal comments - code should be self-documenting - Minimal comments - code should be self-documenting
- No decorative comment blocks (===, ---, etc.) - No decorative comment blocks (===, ---, etc.)
- Only comment non-obvious logic - Only comment non-obvious logic
- No trailing whitespace in any files (including markdown)
- Pre-commit hook automatically strips it
- For markdown line breaks, use <br> tag instead of two trailing spaces
## File Reading Policy ## File Reading Policy
- Ask before reading files unless immediately necessary for current task - Ask before reading files unless immediately necessary for current task

4
docs/session_summaries/2026-01-09-hierarchical-coordinate-frames-phase1-2.md

@ -1,7 +1,7 @@
# Session Summary: Hierarchical Coordinate Frames (Phases 1-2) # Session Summary: Hierarchical Coordinate Frames (Phases 1-2)
**Date:** 2026-01-09 **Date:** 2026-01-09
**Branch:** moon_testing **Branch:** moon_testing
**Goal:** Implement hierarchical coordinate frames to improve moon orbital stability **Goal:** Implement hierarchical coordinate frames to improve moon orbital stability
## Overview ## Overview

4
docs/session_summaries/2026-01-20-hohmann-transfer-fix.md

@ -7,7 +7,7 @@
- **Files Modified:** - **Files Modified:**
- `src/mission_planning.h` - Added deprecation comment - `src/mission_planning.h` - Added deprecation comment
- `src/mission_planning.cpp` - Added deprecation comment - `src/mission_planning.cpp` - Added deprecation comment
- **Reason:** Spacecraft positions and velocities are now set via TOML config files with `semi_major_axis` parameter. Config-based initialization via `simulation::initialize_bodies()` is the standard approach. - **Reason:** Spacecraft positions and velocities are now set via TOML config files with `semi_major_axis` parameter. Config-based initialization via `simulation::initialize_bodies()` is the standard approach.
### 2. Fixed Retrograde Velocity Bug in `simulation.cpp` ### 2. Fixed Retrograde Velocity Bug in `simulation.cpp`
@ -17,7 +17,7 @@
### 3. Fixed `apply_transfer_burn()` Function ### 3. Fixed `apply_transfer_burn()` Function
- **File Modified:** `src/mission_planning.cpp:142-166` - **File Modified:** `src/mission_planning.cpp:142-166`
- **Change:** - **Change:**
- Changed delta-v calculation to apply directly to local velocity - Changed delta-v calculation to apply directly to local velocity
- Simplified velocity update logic to maintain consistency with local coordinate frames - Simplified velocity update logic to maintain consistency with local coordinate frames
- **Reason:** The original implementation calculated delta-v using global velocity vectors, which caused incorrect burn direction and delta-v magnitude. - **Reason:** The original implementation calculated delta-v using global velocity vectors, which caused incorrect burn direction and delta-v magnitude.

4
docs/session_summaries/2026-01-28-documentation-update-relative-rendering.md

@ -14,10 +14,10 @@ Updated rendering documentation to accurately reflect the actual implementation
**Updated sections:** **Updated sections:**
- **Overview**: Changed from "logarithmic distance scaling" to "linear distance scaling, relative rendering with child indicators" - **Overview**: Changed from "logarithmic distance scaling" to "linear distance scaling, relative rendering with child indicators"
- **RenderState struct**: - **RenderState struct**:
- Removed: `previous_selected_body`, `was_following_body` - Removed: `previous_selected_body`, `was_following_body`
- Added: `last_target_index`, `camera_target_enabled` - Added: `last_target_index`, `camera_target_enabled`
- **Scaling Factors**: - **Scaling Factors**:
- Changed from logarithmic scaling with separate size scale (0.02) - Changed from logarithmic scaling with separate size scale (0.02)
- To linear scaling with unified distance/size scale (1e-9 for both) - To linear scaling with unified distance/size scale (1e-9 for both)
- Removed `scale_radius()` log10 formula - Removed `scale_radius()` log10 formula

120
tests/informational/test_time_step_stability.cpp

@ -30,57 +30,57 @@ double calculate_orbital_period(CelestialBody* body, CelestialBody* parent) {
double r = vec3_magnitude(relative_pos); double r = vec3_magnitude(relative_pos);
Vec3 relative_vel = vec3_sub(body->global_velocity, parent->global_velocity); Vec3 relative_vel = vec3_sub(body->global_velocity, parent->global_velocity);
double v = vec3_magnitude(relative_vel); double v = vec3_magnitude(relative_vel);
double specific_energy = (v * v) / 2.0 - G * parent->mass / r; double specific_energy = (v * v) / 2.0 - G * parent->mass / r;
double semi_major_axis = -G * parent->mass / (2.0 * specific_energy); double semi_major_axis = -G * parent->mass / (2.0 * specific_energy);
double period_seconds = 2.0 * M_PI * sqrt(pow(semi_major_axis, 3.0) / (G * parent->mass)); double period_seconds = 2.0 * M_PI * sqrt(pow(semi_major_axis, 3.0) / (G * parent->mass));
return period_seconds; return period_seconds;
} }
bool is_dt_stable(SimulationState* sim, const TestBody& test_body, double dt, int num_orbits) { bool is_dt_stable(SimulationState* sim, const TestBody& test_body, double dt, int num_orbits) {
SimulationState* test_sim = create_simulation(sim->max_bodies, sim->max_craft, sim->max_maneuvers, dt); SimulationState* test_sim = create_simulation(sim->max_bodies, sim->max_craft, sim->max_maneuvers, dt);
REQUIRE(load_system_config(test_sim, "tests/informational/test_time_step_stability.toml")); REQUIRE(load_system_config(test_sim, "tests/informational/test_time_step_stability.toml"));
int body_index = test_body.body_index; int body_index = test_body.body_index;
int parent_index = test_body.parent_index; int parent_index = test_body.parent_index;
double initial_energy = calculate_system_total_energy(test_sim); double initial_energy = calculate_system_total_energy(test_sim);
Vec3 initial_pos_relative = vec3_sub( Vec3 initial_pos_relative = vec3_sub(
test_sim->bodies[body_index].global_position, test_sim->bodies[body_index].global_position,
test_sim->bodies[parent_index].global_position test_sim->bodies[parent_index].global_position
); );
double initial_distance = vec3_magnitude(initial_pos_relative); double initial_distance = vec3_magnitude(initial_pos_relative);
double period = calculate_orbital_period(&test_sim->bodies[body_index], &test_sim->bodies[parent_index]); double period = calculate_orbital_period(&test_sim->bodies[body_index], &test_sim->bodies[parent_index]);
double max_time = period * num_orbits; double max_time = period * num_orbits;
bool completed = true; bool completed = true;
while (test_sim->time < max_time) { while (test_sim->time < max_time) {
update_simulation(test_sim); update_simulation(test_sim);
if (test_sim->bodies[body_index].parent_index != parent_index) { if (test_sim->bodies[body_index].parent_index != parent_index) {
completed = false; completed = false;
break; break;
} }
} }
double final_energy = calculate_system_total_energy(test_sim); double final_energy = calculate_system_total_energy(test_sim);
double energy_drift_percent = fabs((final_energy - initial_energy) / initial_energy) * 100.0; double energy_drift_percent = fabs((final_energy - initial_energy) / initial_energy) * 100.0;
Vec3 final_pos_relative = vec3_sub( Vec3 final_pos_relative = vec3_sub(
test_sim->bodies[body_index].global_position, test_sim->bodies[body_index].global_position,
test_sim->bodies[parent_index].global_position test_sim->bodies[parent_index].global_position
); );
double final_distance = vec3_magnitude(final_pos_relative); double final_distance = vec3_magnitude(final_pos_relative);
double distance_drift_percent = fabs((final_distance - initial_distance) / initial_distance) * 100.0; double distance_drift_percent = fabs((final_distance - initial_distance) / initial_distance) * 100.0;
bool stable = completed && (energy_drift_percent < ENERGY_TOLERANCE) && (distance_drift_percent < 5.0); bool stable = completed && (energy_drift_percent < ENERGY_TOLERANCE) && (distance_drift_percent < 5.0);
destroy_simulation(test_sim); destroy_simulation(test_sim);
return stable; return stable;
} }
@ -88,13 +88,13 @@ double find_max_stable_dt(SimulationState* sim, const TestBody& test_body) {
double low = MIN_DT; double low = MIN_DT;
double high = MAX_DT; double high = MAX_DT;
double max_stable = low; double max_stable = low;
printf("Testing %s (period ~%.2f days):\n", test_body.name, test_body.expected_period_days); printf("Testing %s (period ~%.2f days):\n", test_body.name, test_body.expected_period_days);
for (int iter = 0; iter < 10; iter++) { for (int iter = 0; iter < 10; iter++) {
double mid = (low + high) / 2.0; double mid = (low + high) / 2.0;
bool stable = is_dt_stable(sim, test_body, mid, NUM_ORBITS); bool stable = is_dt_stable(sim, test_body, mid, NUM_ORBITS);
if (stable) { if (stable) {
max_stable = mid; max_stable = mid;
low = mid; low = mid;
@ -103,10 +103,10 @@ double find_max_stable_dt(SimulationState* sim, const TestBody& test_body) {
high = mid; high = mid;
printf(" dt=%.0fs: UNSTABLE\n", mid); printf(" dt=%.0fs: UNSTABLE\n", mid);
} }
if (high - low < 5.0) break; if (high - low < 5.0) break;
} }
printf(" Maximum stable dt: %.0f seconds\n\n", max_stable); printf(" Maximum stable dt: %.0f seconds\n\n", max_stable);
return max_stable; return max_stable;
} }
@ -116,33 +116,33 @@ void print_summary(const StabilityResult* results, int num_results, double min_s
printf("===============================================================================\n"); printf("===============================================================================\n");
printf(" TIME STEP STABILITY TEST RESULTS\n"); printf(" TIME STEP STABILITY TEST RESULTS\n");
printf("===============================================================================\n\n"); printf("===============================================================================\n\n");
printf("STABILITY CRITERIA:\n"); printf("STABILITY CRITERIA:\n");
printf(" - Energy drift < %.1f%% over %d orbits\n", ENERGY_TOLERANCE, NUM_ORBITS); printf(" - Energy drift < %.1f%% over %d orbits\n", ENERGY_TOLERANCE, NUM_ORBITS);
printf(" - Distance drift < 5.0%%\n"); printf(" - Distance drift < 5.0%%\n");
printf(" - No SOI transitions (parent changes)\n\n"); printf(" - No SOI transitions (parent changes)\n\n");
printf("PER-BODY RESULTS:\n"); printf("PER-BODY RESULTS:\n");
printf("+----------------------+----------------+------------------+----------------+\n"); printf("+----------------------+----------------+------------------+----------------+\n");
printf("| Body | Period (days) | Max Stable dt (s) | Stability Status |\n"); printf("| Body | Period (days) | Max Stable dt (s) | Stability Status |\n");
printf("+----------------------+----------------+------------------+----------------+\n"); printf("+----------------------+----------------+------------------+----------------+\n");
for (int i = 0; i < num_results; i++) { for (int i = 0; i < num_results; i++) {
const StabilityResult& r = results[i]; const StabilityResult& r = results[i];
double ratio = default_dt / r.max_stable_dt; double ratio = default_dt / r.max_stable_dt;
const char* status = ratio < 0.5 ? "Very Stable" : ratio < 0.8 ? "Stable" : "Limited Margin"; const char* status = ratio < 0.5 ? "Very Stable" : ratio < 0.8 ? "Stable" : "Limited Margin";
printf("| %-20s | %14.2f | %16.0f | %-14s |\n", r.name, r.period_days, r.max_stable_dt, status); printf("| %-20s | %14.2f | %16.0f | %-14s |\n", r.name, r.period_days, r.max_stable_dt, status);
} }
printf("+----------------------+----------------+------------------+----------------+\n\n"); printf("+----------------------+----------------+------------------+----------------+\n\n");
printf("OVERALL ANALYSIS:\n"); printf("OVERALL ANALYSIS:\n");
printf(" Minimum stable time step: %.0f seconds\n", min_stable_dt); printf(" Minimum stable time step: %.0f seconds\n", min_stable_dt);
printf(" Recommended safe dt: %.0f seconds (0.7x safety margin)\n", min_stable_dt * 0.7); printf(" Recommended safe dt: %.0f seconds (0.7x safety margin)\n", min_stable_dt * 0.7);
printf(" Current default dt: %.0f seconds\n", default_dt); printf(" Current default dt: %.0f seconds\n", default_dt);
printf(" Current dt stability: %.0fx\n", default_dt / min_stable_dt); printf(" Current dt stability: %.0fx\n", default_dt / min_stable_dt);
printf("\n"); printf("\n");
if (default_dt < min_stable_dt * 0.7) { if (default_dt < min_stable_dt * 0.7) {
printf("STATUS: Current time step (60s) is VERY STABLE with good margin.\n"); printf("STATUS: Current time step (60s) is VERY STABLE with good margin.\n");
printf(" Can be increased significantly if needed.\n\n"); printf(" Can be increased significantly if needed.\n\n");
@ -153,7 +153,7 @@ void print_summary(const StabilityResult* results, int num_results, double min_s
printf("STATUS: Current time step (60s) is near stability limit.\n"); printf("STATUS: Current time step (60s) is near stability limit.\n");
printf(" Consider reducing for safety.\n\n"); printf(" Consider reducing for safety.\n\n");
} }
printf("RECOMMENDATIONS:\n"); printf("RECOMMENDATIONS:\n");
printf(" - For MESSENGER-like close orbits: Keep dt <= %.0f seconds\n", min_stable_dt); printf(" - For MESSENGER-like close orbits: Keep dt <= %.0f seconds\n", min_stable_dt);
printf(" - For planetary missions: Current dt=60s is excellent\n"); printf(" - For planetary missions: Current dt=60s is excellent\n");
@ -165,60 +165,60 @@ void print_summary(const StabilityResult* results, int num_results, double min_s
TEST_CASE("Time step stability - Mercury orbiter (MESSENGER-like)", "[timestep][stability]") { TEST_CASE("Time step stability - Mercury orbiter (MESSENGER-like)", "[timestep][stability]") {
const double BASE_DT = 60.0; const double BASE_DT = 60.0;
SimulationState* sim = create_simulation(10, 0, 0, BASE_DT); SimulationState* sim = create_simulation(10, 0, 0, BASE_DT);
TestBody mercury_orbiter = {"Mercury_Orbiter", 1, 0, 0.5}; TestBody mercury_orbiter = {"Mercury_Orbiter", 1, 0, 0.5};
double max_dt = find_max_stable_dt(sim, mercury_orbiter); double max_dt = find_max_stable_dt(sim, mercury_orbiter);
INFO("Mercury orbiter maximum stable dt: " << max_dt << " seconds"); INFO("Mercury orbiter maximum stable dt: " << max_dt << " seconds");
REQUIRE(max_dt >= MIN_DT); REQUIRE(max_dt >= MIN_DT);
destroy_simulation(sim); destroy_simulation(sim);
} }
TEST_CASE("Time step stability - Io (Jupiter's moon)", "[timestep][stability]") { TEST_CASE("Time step stability - Io (Jupiter's moon)", "[timestep][stability]") {
const double BASE_DT = 60.0; const double BASE_DT = 60.0;
SimulationState* sim = create_simulation(10, 0, 0, BASE_DT); SimulationState* sim = create_simulation(10, 0, 0, BASE_DT);
TestBody io = {"Io", 3, 2, 1.77}; TestBody io = {"Io", 3, 2, 1.77};
double max_dt = find_max_stable_dt(sim, io); double max_dt = find_max_stable_dt(sim, io);
INFO("Io maximum stable dt: " << max_dt << " seconds"); INFO("Io maximum stable dt: " << max_dt << " seconds");
REQUIRE(max_dt >= MIN_DT); REQUIRE(max_dt >= MIN_DT);
destroy_simulation(sim); destroy_simulation(sim);
} }
TEST_CASE("Time step stability - Moon (Earth's moon)", "[timestep][stability]") { TEST_CASE("Time step stability - Moon (Earth's moon)", "[timestep][stability]") {
const double BASE_DT = 60.0; const double BASE_DT = 60.0;
SimulationState* sim = create_simulation(10, 0, 0, BASE_DT); SimulationState* sim = create_simulation(10, 0, 0, BASE_DT);
TestBody moon = {"Moon", 5, 4, 27.3}; TestBody moon = {"Moon", 5, 4, 27.3};
double max_dt = find_max_stable_dt(sim, moon); double max_dt = find_max_stable_dt(sim, moon);
INFO("Moon maximum stable dt: " << max_dt << " seconds"); INFO("Moon maximum stable dt: " << max_dt << " seconds");
REQUIRE(max_dt >= MIN_DT); REQUIRE(max_dt >= MIN_DT);
destroy_simulation(sim); destroy_simulation(sim);
} }
TEST_CASE("Find minimum stable time step across all bodies", "[timestep][stability]") { TEST_CASE("Find minimum stable time step across all bodies", "[timestep][stability]") {
const double BASE_DT = 60.0; const double BASE_DT = 60.0;
SimulationState* sim = create_simulation(10, 0, 0, BASE_DT); SimulationState* sim = create_simulation(10, 0, 0, BASE_DT);
TestBody bodies[] = { TestBody bodies[] = {
{"Mercury_Orbiter", 1, 0, 0.5}, {"Mercury_Orbiter", 1, 0, 0.5},
{"Io", 3, 2, 1.77}, {"Io", 3, 2, 1.77},
{"Moon", 5, 4, 27.3} {"Moon", 5, 4, 27.3}
}; };
StabilityResult results[3]; StabilityResult results[3];
double max_dt = MAX_DT; double max_dt = MAX_DT;
printf("\n=== Finding minimum stable dt across all bodies ===\n\n"); printf("\n=== Finding minimum stable dt across all bodies ===\n\n");
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
results[i].name = bodies[i].name; results[i].name = bodies[i].name;
results[i].period_days = bodies[i].expected_period_days; results[i].period_days = bodies[i].expected_period_days;
@ -227,70 +227,70 @@ TEST_CASE("Find minimum stable time step across all bodies", "[timestep][stabili
max_dt = results[i].max_stable_dt; max_dt = results[i].max_stable_dt;
} }
} }
printf("\n=== RESULTS ===\n"); printf("\n=== RESULTS ===\n");
printf("Minimum stable time step: %.0f seconds\n", max_dt); printf("Minimum stable time step: %.0f seconds\n", max_dt);
printf("Recommended safe time step: %.0f seconds (%.0fx safety margin)\n", max_dt * 0.7, 1.0/0.7); printf("Recommended safe time step: %.0f seconds (%.0fx safety margin)\n", max_dt * 0.7, 1.0/0.7);
INFO("Minimum stable dt: " << max_dt << " seconds"); INFO("Minimum stable dt: " << max_dt << " seconds");
print_summary(results, 3, max_dt, BASE_DT); print_summary(results, 3, max_dt, BASE_DT);
REQUIRE(max_dt >= MIN_DT); REQUIRE(max_dt >= MIN_DT);
destroy_simulation(sim); destroy_simulation(sim);
} }
TEST_CASE("Verify current default dt (60s) stability", "[timestep][stability]") { TEST_CASE("Verify current default dt (60s) stability", "[timestep][stability]") {
const double DT = 60.0; const double DT = 60.0;
const int NUM_ORBITS = 10; const int NUM_ORBITS = 10;
SimulationState* sim = create_simulation(10, 0, 0, DT); SimulationState* sim = create_simulation(10, 0, 0, DT);
REQUIRE(load_system_config(sim, "tests/informational/test_time_step_stability.toml")); REQUIRE(load_system_config(sim, "tests/informational/test_time_step_stability.toml"));
struct BodyTest { struct BodyTest {
int body_index; int body_index;
int parent_index; int parent_index;
const char* name; const char* name;
}; };
BodyTest tests[] = { BodyTest tests[] = {
{1, 0, "Mercury_Orbiter"}, {1, 0, "Mercury_Orbiter"},
{3, 2, "Io"}, {3, 2, "Io"},
{5, 4, "Moon"} {5, 4, "Moon"}
}; };
for (int t = 0; t < 3; t++) { for (int t = 0; t < 3; t++) {
int body_index = tests[t].body_index; int body_index = tests[t].body_index;
int parent_index = tests[t].parent_index; int parent_index = tests[t].parent_index;
const char* name = tests[t].name; const char* name = tests[t].name;
double period = calculate_orbital_period(&sim->bodies[body_index], &sim->bodies[parent_index]); double period = calculate_orbital_period(&sim->bodies[body_index], &sim->bodies[parent_index]);
double max_time = period * NUM_ORBITS; double max_time = period * NUM_ORBITS;
double initial_energy = calculate_system_total_energy(sim); double initial_energy = calculate_system_total_energy(sim);
INFO("Testing " << name << " with dt=" << DT << "s for " << NUM_ORBITS << " orbits"); INFO("Testing " << name << " with dt=" << DT << "s for " << NUM_ORBITS << " orbits");
bool completed = true; bool completed = true;
while (sim->time < max_time) { while (sim->time < max_time) {
update_simulation(sim); update_simulation(sim);
if (sim->bodies[body_index].parent_index != parent_index) { if (sim->bodies[body_index].parent_index != parent_index) {
completed = false; completed = false;
break; break;
} }
} }
double final_energy = calculate_system_total_energy(sim); double final_energy = calculate_system_total_energy(sim);
double energy_drift_percent = fabs((final_energy - initial_energy) / initial_energy) * 100.0; double energy_drift_percent = fabs((final_energy - initial_energy) / initial_energy) * 100.0;
INFO(name << " completed: " << (completed ? "yes" : "no")); INFO(name << " completed: " << (completed ? "yes" : "no"));
INFO(name << " energy drift: " << energy_drift_percent << "%"); INFO(name << " energy drift: " << energy_drift_percent << "%");
REQUIRE(completed); REQUIRE(completed);
REQUIRE(energy_drift_percent < ENERGY_TOLERANCE); REQUIRE(energy_drift_percent < ENERGY_TOLERANCE);
} }
destroy_simulation(sim); destroy_simulation(sim);
} }

Loading…
Cancel
Save