Files
GR-raytracing/tests/test_geodesic.c
T
wyj f7380cbf75 Feat: Rework ray termination into escaped/dark/unresolved/incomplete
Replace the position capture cutoff with a camera-relative dark threshold
shared by every backend, and carry explicit outcome/reason provenance
through the ray, RayPool, adaptive mesh, lens-map and replay paths.

- eval/eval_slab return SpacetimePointStatus; remove SPACETIME_RAY_CAPTURED
  and the Schwarzschild capture radius; decouple observer construction from
  ray position.
- RayEndpoint stores RayOutcome/RayReason plus the last trusted state;
  budget exhaustion is retryable UNRESOLVED, data/integration failures are
  INCOMPLETE.
- Normal dark terminal is L - L0 >= --dark-threshold (default 8), with L0
  taken at the camera event and kept distinct from the worldtube entry
  energy; photon energy and frequency ratio are never reset.
- Implement E/D/U triangle decisions with merged budget retries, persistent
  probe witnesses promoted in place by vertex identity, conformity settling,
  and approximate-black boundary provenance with achieved-scale statistics.
- Add RayPool continuation state and per-ray step budgets.
- Bump lens-map to v2 with explicit end/outcome/reason, approx_black,
  threshold/retry/geometry provenance and per-frame retry counts; reject v1.
- Gate production output on incomplete/error results, overridable with
  --allow-incomplete.
- Update AGENTS.md, the design document and usage docs; add the termination
  oracle and regression coverage.

make -B -j4 BUILD_TYPE=Debug test passes with bit-identical reference HDRs.
2026-10-05 06:22:47 -04:00

78 lines
3.2 KiB
C

#include "geodesic.h"
#include "observer_track.h"
#include <math.h>
#include <stdio.h>
static int nearly_equal(double a, double b) { return fabs(a - b) < 1e-12; }
static int check_ray(const SpacetimeSource *source,
const ObserverState *observer,
const double local_direction[3],
const double expected[3]) {
const GeodesicTraceConfig config = {.coordinate_time_step = 0.25,
.max_steps = 100};
RayEndpoint ray =
geodesic_trace_past(source, observer, local_direction, &config);
if (ray.outcome != RAY_OUTCOME_ESCAPED ||
!nearly_equal(ray.frequency_ratio, 1.0) ||
!nearly_equal(ray.n_infinity[0], expected[0]) ||
!nearly_equal(ray.n_infinity[1], expected[1]) ||
!nearly_equal(ray.n_infinity[2], expected[2])) {
fprintf(stderr, "flat-space geodesic regression failed\n");
return 1;
}
return 0;
}
int main(void) {
SpacetimeSource source = {0};
MetricSlab *slab = NULL;
MetricData metric;
const ObserverState observer = observer_fixed_at_origin();
if (spacetime_create_minkowski(&source, 10.0) ||
spacetime_load_slab(&source, 0.0, -1.0, &slab) ||
spacetime_slab_eval(slab, -0.5, (double[]){0.0, 0.0, 0.0}, &metric) ||
metric.alpha != 1.0 ||
!spacetime_slab_eval(slab, 0.25, (double[]){0.0, 0.0, 0.0}, &metric))
return 1;
int result = check_ray(&source, &observer, (double[]){1.0, 0.0, 0.0},
(double[]){0.0, 0.0, -1.0}) ||
check_ray(&source, &observer, (double[]){0.0, 0.0, 1.0},
(double[]){1.0, 0.0, 0.0});
const ObserverState look_at_ra_zero =
observer_fixed_at_origin_look_at(0.0, 0.0);
result = result || check_ray(&source, &look_at_ra_zero,
(double[]){1.0, 0.0, 0.0},
(double[]){1.0, 0.0, 0.0});
/* Standard ICRS has +Z at the north celestial pole and +Y at increasing
* RA. A right-handed north-up camera consequently has west to its right. */
result = result || check_ray(&source, &look_at_ra_zero,
(double[]){0.0, 1.0, 0.0},
(double[]){0.0, 0.0, 1.0}) ||
check_ray(&source, &look_at_ra_zero,
(double[]){0.0, 0.0, 1.0},
(double[]){0.0, -1.0, 0.0});
ObserverTrack accelerated = {0};
ObserverState final_observer;
if (observer_track_generate_minkowski_acceleration(&accelerated, 1.52, 2.0,
1.0 / 30.0) ||
observer_track_interpolate(&accelerated, 2.0, &final_observer, NULL)) {
result = 1;
} else {
const RayEndpoint forward = geodesic_trace_past(
&source, &final_observer, (double[]){1.0, 0.0, 0.0},
&(GeodesicTraceConfig){.coordinate_time_step = 0.25, .max_steps = 100});
const double expected_g = sqrt(1.0 + 3.04 * 3.04) + 3.04;
if (forward.outcome != RAY_OUTCOME_ESCAPED ||
!nearly_equal(forward.frequency_ratio, expected_g)) {
fputs("accelerated-observer Doppler regression failed\n", stderr);
result = 1;
}
}
observer_track_destroy(&accelerated);
spacetime_free_slab(slab);
spacetime_destroy(&source);
return result;
}