This commit is contained in:
wyj committed 2026-08-25 22:45:43 -04:00
commit d30f9ac56c
56 files changed
+40776

No files matched your search

+115
View File
@@ -0,0 +1,115 @@
#include "frame.h"
#include "optics.h"
#include <math.h>
#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
const int width = 100, height = 100;
const PointSpreadFunction psf = {.fwhm_pixels = 2.7, .moffat_beta = 4.5};
const GeodesicTraceConfig trace = {.coordinate_time_step = 0.25,
.max_steps = 100};
const ObserverState observer = observer_fixed_at_origin();
Star star = {
.direction = {0.0, 0.0, -1.0}, .temperature_K = 7000.0, .amplitude = 1.0};
StarCatalog catalog = {.stars = &star, .count = 1};
SpacetimeSource spacetime = {0};
FrameLensMesh mesh = {0};
double *hdr = calloc((size_t)width * height * 3, sizeof *hdr);
int result = 1;
if (hdr == NULL || spacetime_create_minkowski(&spacetime, 10.0) ||
frame_lens_mesh_build_coarse(&mesh, width, height, 20, 30.0) ||
frame_lens_mesh_trace(&mesh, &spacetime, &observer, &trace))
goto done;
const size_t images =
frame_splat_catalog(&mesh, &catalog, hdr, width, height, 100.0, &psf);
if (images != 1 || hdr[3 * (50 * width + 50)] <= 0.0) {
fputs("flat-space inverse lens-map regression failed\n", stderr);
goto done;
}
/* Private HDR accumulation must preserve the serial splat result. */
double *serial_hdr = calloc((size_t)width * height * 3, sizeof *serial_hdr);
double *parallel_hdr = calloc((size_t)width * height * 3, sizeof *parallel_hdr);
if (serial_hdr == NULL || parallel_hdr == NULL) {
free(serial_hdr);
free(parallel_hdr);
goto done;
}
const int original_threads = omp_get_max_threads();
omp_set_dynamic(0);
omp_set_num_threads(1);
const size_t serial_images = frame_splat_catalog(
&mesh, &catalog, serial_hdr, width, height, 100.0, &psf);
omp_set_num_threads(4);
const size_t parallel_images = frame_splat_catalog(
&mesh, &catalog, parallel_hdr, width, height, 100.0, &psf);
omp_set_num_threads(original_threads);
for (int value = 0; value < width * height * 3; ++value)
if (fabs(serial_hdr[value] - parallel_hdr[value]) >
1e-12 * fmax(1.0, fabs(serial_hdr[value]))) {
fputs("parallel catalog splat regression failed\n", stderr);
free(serial_hdr);
free(parallel_hdr);
goto done;
}
free(serial_hdr);
free(parallel_hdr);
if (serial_images != 1 || parallel_images != serial_images) {
fputs("parallel catalog image-count regression failed\n", stderr);
goto done;
}
const LinearRgb cool = blackbody_to_linear_rgb(3000.0);
const LinearRgb hot = blackbody_to_linear_rgb(10000.0);
if (!(cool.r > cool.b && hot.b > hot.r &&
hot.r + hot.g + hot.b > cool.r + cool.g + cool.b)) {
fputs("blackbody spectral-color regression failed\n", stderr);
goto done;
}
/* The Moffat is flux-normalized and retains a measurable, continuous wing
* beyond the former Gaussian's 3-sigma raster box. */
memset(hdr, 0, (size_t)width * height * 3 * sizeof *hdr);
splat_moffat(hdr, width, height, 50.5, 50.5,
(LinearRgb){1.0, 1.0, 1.0}, 1.0, &psf);
double moffat_flux = 0.0;
for (int pixel = 0; pixel < width * height; ++pixel)
moffat_flux += hdr[3 * pixel];
if (fabs(moffat_flux - 1.0) > 0.01 ||
hdr[3 * (50 * width + 62)] <= 0.0) {
fputs("Moffat normalization or wing regression failed\n", stderr);
goto done;
}
frame_draw_mesh(&mesh, hdr, width, height, 0.5, 0.5);
if (hdr[3 * (10 * width + 20)] != 0.25) {
fputs("mesh diagnostic overlay regression failed\n", stderr);
goto done;
}
/* A fixed absolute edge tolerance used to make tiny source triangles claim
* sources far outside their field. */
FrameLensMesh fine_mesh = {0};
Star fine_stars[2] = {{.direction = {0.0, 0.0, -1.0},
.temperature_K = 7000.0,
.amplitude = 1.0},
{.direction = {0.01, 0.0, -0.9999499987499375},
.temperature_K = 7000.0,
.amplitude = 1.0}};
StarCatalog fine_catalog = {.stars = fine_stars, .count = 2};
memset(hdr, 0, (size_t)width * height * 3 * sizeof *hdr);
if (frame_lens_mesh_build_coarse(&fine_mesh, width, height, 1, 0.1) ||
frame_lens_mesh_trace(&fine_mesh, &spacetime, &observer, &trace) ||
frame_splat_catalog(&fine_mesh, &fine_catalog, hdr, width, height,
100.0, &psf) != 1) {
fputs("fine source-triangle containment regression failed\n", stderr);
frame_lens_mesh_destroy(&fine_mesh);
goto done;
}
frame_lens_mesh_destroy(&fine_mesh);
result = 0;
done:
frame_lens_mesh_destroy(&mesh);
spacetime_destroy(&spacetime);
free(hdr);
return result;
}
+43
View File
@@ -0,0 +1,43 @@
#include "geodesic.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.status != RAY_ENDPOINT_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};
const ObserverState observer = observer_fixed_at_origin();
if (spacetime_create_minkowski(&source, 10.0))
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});
spacetime_destroy(&source);
return result;
}
+52
View File
@@ -0,0 +1,52 @@
#include "geodesic.h"
#include <math.h>
#include <stdio.h>
int main(void) {
SpacetimeSource spacetime = {0};
MetricData metric;
ObserverState observer;
ObserverState inward_observer;
const GeodesicTraceConfig trace = {.coordinate_time_step = 0.1,
.max_steps = 4096,
.capture_log_alpha_p0 = 8.0};
int result = 1;
if (spacetime_create_schwarzschild_ks(&spacetime, 1.0, 256.0, 1.5) ||
spacetime_eval(&spacetime, 0.0, (double[]){2.0, 0.0, 0.0}, &metric) ||
!isfinite(metric.alpha) || !isfinite(metric.gamma[0][0]) ||
!isfinite(metric.K[0][0]) ||
observer_static_schwarzschild_ks(1.0, 30.0, &observer) ||
observer_inward_schwarzschild_ks(1.0, 30.0, 0.5,
&inward_observer) ||
fabs(inward_observer.tetrad[0][0] -
(2.0 / sqrt(3.0)) * (observer.tetrad[0][0] +
0.5 * observer.tetrad[1][0])) >
1e-12 ||
fabs(inward_observer.tetrad[1][1] -
(2.0 / sqrt(3.0)) * (0.5 * observer.tetrad[0][1] +
observer.tetrad[1][1])) >
1e-12 ||
!observer_inward_schwarzschild_ks(1.0, 30.0, 1.0,
&inward_observer))
goto done;
const RayEndpoint central = geodesic_trace_past(
&spacetime, &observer, (double[]){1.0, 0.0, 0.0}, &trace);
const RayEndpoint inside_shadow = geodesic_trace_past(
&spacetime, &observer, (double[]){cos(0.10), sin(0.10), 0.0}, &trace);
const RayEndpoint outside_shadow = geodesic_trace_past(
&spacetime, &observer, (double[]){cos(0.30), sin(0.30), 0.0}, &trace);
if (central.status != RAY_ENDPOINT_CAPTURED ||
inside_shadow.status != RAY_ENDPOINT_CAPTURED ||
outside_shadow.status != RAY_ENDPOINT_ESCAPED) {
fprintf(stderr,
"Schwarzschild KS shadow regression failed (center=%d, inside=%d, "
"outside=%d)\n",
central.status, inside_shadow.status, outside_shadow.status);
goto done;
}
result = 0;
done:
spacetime_destroy(&spacetime);
return result;
}