Files
GR-raytracing/tests/test_geodesic.c
T

105 lines
4.3 KiB
C

#include "geodesic.h"
#include "observer_track.h"
#include <math.h>
#include <stdio.h>
#include <string.h>
static int nearly_equal(double a, double b) { return fabs(a - b) < 1e-12; }
/* Every enumerator must have a stable label, and an out-of-range value must be
* reported as UNKNOWN rather than indexing past a table. */
static int check_reason_names(void) {
const RayReason reasons[] = {
RAY_REASON_NONE, RAY_REASON_REDSHIFT_LIMIT,
RAY_REASON_BUDGET_EXHAUSTED, RAY_REASON_TIME_RANGE_EXHAUSTED,
RAY_REASON_OUT_OF_DOMAIN, RAY_REASON_INVALID_METRIC,
RAY_REASON_INTEGRATION_ERROR, RAY_REASON_UNSUPPORTED,
RAY_REASON_PROTOCOL_ERROR, RAY_REASON_IO_ERROR};
int failed = 0;
for (size_t i = 0; i < sizeof reasons / sizeof reasons[0]; ++i) {
const char *name = ray_reason_name(reasons[i]);
if (name == NULL || name[0] == '\0' || strcmp(name, "UNKNOWN") == 0) {
fprintf(stderr, "reason %d has no stable label\n", (int)reasons[i]);
failed = 1;
}
}
const int unknown = (int)RAY_REASON_IO_ERROR + 7;
if (strcmp(ray_reason_name((RayReason)unknown), "UNKNOWN") != 0) {
fputs("out-of-range reason is not UNKNOWN\n", stderr);
failed = 1;
}
return failed;
}
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_reason_names() ||
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;
}