Test: add fixed PSF image references

This commit is contained in:
wyj committed 2026-09-04 20:28:54 -04:00
1 parent ab56e23fc9
commit 4953775cdd
8 files changed
+165

No files matched your search

+1
View File
@@ -123,3 +123,4 @@ clean:
rm -rf build rm -rf build
-include $(RENDER_DEPS) -include $(RENDER_DEPS)
include mk/reference_images.mk
+70
View File
@@ -0,0 +1,70 @@
# PSF event-sink image references
Run date: 2026-09-04
Purpose: fixed pixel-comparison references before replacing the sink consumer
with HIP. The renderer used the default CPU `PSF_EVENT_SINK=1` path. There
was no adaptive refinement. The catalog is the repository test catalog, not
an all-sky tile snapshot. This is not a performance benchmark.
Environment:
- Git `HEAD`: `ab56e23` (worktree contains the uncommitted event-sink change)
- Build: `BUILD_TYPE=Release`, `ENABLE_HDR=1`, `ENABLE_PNG=1`, OpenMP
- Host worker setting: `OMP_NUM_THREADS=16`
- Images: 640 x 360 px; Moffat FWHM 2.7 px, beta 4.5
- Observer: radius 30, inward speed 0; ICRS look direction RA 1 deg, Dec 1 deg
The explicit `1e300` magnification cap is the practical command-line spelling
of the normal unlimited default. `--draw-mesh` and `--psf-direct` are absent,
therefore disabled. All refinement thresholds are included even though level
zero prevents refinement, so later default changes cannot affect this run.
## Minkowski
Build:
```bash
make -B SPACETIME=minkowski ENABLE_HDR=1 PSF_EVENT_SINK=1 backend
```
Render:
```bash
OMP_NUM_THREADS=16 build/Release/minkowski_sky --catalog assets/sky_grid_5deg.csv --output tests/data/psf_event_sink_reference/minkowski_ra1_dec1_640x360.png --hdr-output --width 640 --height 360 --fov-deg 30 --look-ra-deg 1 --look-dec-deg 1 --exposure 0.1 --observer-radius 30 --observer-inward-speed 0 --psf-fwhm-pixels 2.7 --psf-moffat-beta 4.5 --max-magnification 1e300 --max-cache-psf-flux 1 --psf-relative-tail 1e-8 --psf-min-y 0 --coarse-cell-pixels 16 --refine-max-level 0 --refine-angle-abs-deg 0.001 --refine-angle-rel 0.1 --refine-jacobian-min 1e-3 --refine-min-edge-pixels 0.5 --refine-min-area-pixels2 0.25 --catalog-load-workers 4
```
Result: 35 images from 5,654 catalog stars; 35 cached, 0 wing-clipped, 0 direct
fallback, 0 min-Y discarded.
Versioned reference outputs:
- `tests/data/psf_event_sink_reference/minkowski_ra1_dec1_640x360.png`
- `tests/data/psf_event_sink_reference/minkowski_ra1_dec1_640x360_HDR.fits`
## Analytic Schwarzschild
This reference uses a 60 deg horizontal field of view. At radius 30, the
former 30 deg framing mostly contained the shadow and high-order images inside
the Einstein ring, without enough field to test the first-image star field.
Build:
```bash
make -B SPACETIME=schwarzschild ENABLE_HDR=1 PSF_EVENT_SINK=1 backend
```
Render:
```bash
OMP_NUM_THREADS=16 build/Release/schwarzschild_sky --catalog assets/sky_grid_5deg.csv --output tests/data/psf_event_sink_reference/schwarzschild_ra1_dec1_fov60_640x360.png --hdr-output --width 640 --height 360 --fov-deg 60 --look-ra-deg 1 --look-dec-deg 1 --exposure 0.1 --observer-radius 30 --observer-inward-speed 0 --psf-fwhm-pixels 2.7 --psf-moffat-beta 4.5 --max-magnification 1e300 --max-cache-psf-flux 1 --psf-relative-tail 1e-8 --psf-min-y 0 --coarse-cell-pixels 16 --refine-max-level 0 --refine-angle-abs-deg 0.001 --refine-angle-rel 0.1 --refine-jacobian-min 1e-3 --refine-min-edge-pixels 0.5 --refine-min-area-pixels2 0.25 --catalog-load-workers 4
```
Result: 5,206 images from 5,654 catalog stars; 5,206 cached, 0 wing-clipped,
0 direct fallback, 0 min-Y discarded. This is a visual regression reference,
not a performance result.
Versioned reference outputs:
- `tests/data/psf_event_sink_reference/schwarzschild_ra1_dec1_fov60_640x360.png`
- `tests/data/psf_event_sink_reference/schwarzschild_ra1_dec1_fov60_640x360_HDR.fits`
+16
View File
@@ -0,0 +1,16 @@
REFERENCE_DIR := tests/data/psf_event_sink_reference
REFERENCE_TMP_DIR := /tmp/gr_psf_event_sink_reference
FLOATDIFF_SCRIPT := scripts/fits_floatdiff.py
.PHONY: test-reference-images
test: test-reference-images
test-reference-images: $(FLOATDIFF_SCRIPT) $(REFERENCE_DIR)/minkowski_ra1_dec1_640x360_HDR.fits $(REFERENCE_DIR)/schwarzschild_ra1_dec1_fov60_640x360_HDR.fits
$(MAKE) SPACETIME=minkowski ENABLE_HDR=1 backend
mkdir -p $(REFERENCE_TMP_DIR)
OMP_NUM_THREADS=16 $(BUILD_DIR)/minkowski_sky --catalog assets/sky_grid_5deg.csv --output $(REFERENCE_TMP_DIR)/minkowski_ra1_dec1_640x360.png --hdr-output --width 640 --height 360 --fov-deg 30 --look-ra-deg 1 --look-dec-deg 1 --exposure 0.1 --observer-radius 30 --observer-inward-speed 0 --psf-fwhm-pixels 2.7 --psf-moffat-beta 4.5 --max-magnification 1e300 --max-cache-psf-flux 1 --psf-relative-tail 1e-8 --psf-min-y 0 --coarse-cell-pixels 16 --refine-max-level 0 --refine-angle-abs-deg 0.001 --refine-angle-rel 0.1 --refine-jacobian-min 1e-3 --refine-min-edge-pixels 0.5 --refine-min-area-pixels2 0.25 --catalog-load-workers 4
python3 $(FLOATDIFF_SCRIPT) $(REFERENCE_DIR)/minkowski_ra1_dec1_640x360_HDR.fits $(REFERENCE_TMP_DIR)/minkowski_ra1_dec1_640x360_HDR.fits
$(MAKE) SPACETIME=schwarzschild ENABLE_HDR=1 backend
OMP_NUM_THREADS=16 $(BUILD_DIR)/schwarzschild_sky --catalog assets/sky_grid_5deg.csv --output $(REFERENCE_TMP_DIR)/schwarzschild_ra1_dec1_fov60_640x360.png --hdr-output --width 640 --height 360 --fov-deg 60 --look-ra-deg 1 --look-dec-deg 1 --exposure 0.1 --observer-radius 30 --observer-inward-speed 0 --psf-fwhm-pixels 2.7 --psf-moffat-beta 4.5 --max-magnification 1e300 --max-cache-psf-flux 1 --psf-relative-tail 1e-8 --psf-min-y 0 --coarse-cell-pixels 16 --refine-max-level 0 --refine-angle-abs-deg 0.001 --refine-angle-rel 0.1 --refine-jacobian-min 1e-3 --refine-min-edge-pixels 0.5 --refine-min-area-pixels2 0.25 --catalog-load-workers 4
python3 $(FLOATDIFF_SCRIPT) $(REFERENCE_DIR)/schwarzschild_ra1_dec1_fov60_640x360_HDR.fits $(REFERENCE_TMP_DIR)/schwarzschild_ra1_dec1_fov60_640x360_HDR.fits
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""Compare primary-image IEEE float values in two simple FITS files.
The renderer writes a single 32-bit RGB primary HDU. Keeping this dependency-
free tool in the repository makes HDR reference comparisons available on the
minimal Gentoo installation as well as in CI.
"""
import argparse
import math
import struct
import sys
def read_fits(path):
with open(path, "rb") as stream:
cards = []
while True:
block = stream.read(2880)
if len(block) != 2880:
raise ValueError(f"{path}: truncated FITS header")
cards.extend(block[i:i + 80].decode("ascii") for i in range(0, 2880, 80))
if any(card.startswith("END") for card in cards[-36:]):
break
values = {}
for card in cards:
if card[8:10] == "= ":
values[card[:8].strip()] = card[10:30].strip()
if values.get("BITPIX") != "-32" or values.get("NAXIS") != "3":
raise ValueError(f"{path}: expected a 32-bit, three-axis primary image")
shape = tuple(int(values[f"NAXIS{axis}"]) for axis in (1, 2, 3))
count = math.prod(shape)
payload = stream.read(count * 4)
if len(payload) != count * 4:
raise ValueError(f"{path}: truncated image payload")
return shape, payload
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("reference")
parser.add_argument("candidate")
parser.add_argument("--abs-tolerance", type=float, default=0.0)
parser.add_argument("--rel-tolerance", type=float, default=0.0)
args = parser.parse_args()
if args.abs_tolerance < 0 or args.rel_tolerance < 0:
parser.error("tolerances must be non-negative")
ref_shape, ref_payload = read_fits(args.reference)
candidate_shape, candidate_payload = read_fits(args.candidate)
if ref_shape != candidate_shape:
print(f"shape mismatch: {ref_shape} != {candidate_shape}", file=sys.stderr)
return 1
maximum_abs = maximum_rel = 0.0
mismatch_count = 0
worst_index = 0
for index, (reference, candidate) in enumerate(
zip(struct.iter_unpack(">f", ref_payload),
struct.iter_unpack(">f", candidate_payload))):
reference, candidate = reference[0], candidate[0]
if not math.isfinite(reference) or not math.isfinite(candidate):
equal = reference == candidate
absolute = relative = math.inf if not equal else 0.0
else:
absolute = abs(candidate - reference)
relative = absolute / max(abs(reference), 1.0e-30)
equal = absolute <= args.abs_tolerance or relative <= args.rel_tolerance
if absolute > maximum_abs:
maximum_abs, worst_index = absolute, index
maximum_rel = max(maximum_rel, relative)
mismatch_count += not equal
print(f"floatdiff: shape={ref_shape} samples={math.prod(ref_shape)} "
f"mismatches={mismatch_count} max_abs={maximum_abs:.9g} "
f"max_rel={maximum_rel:.9g} worst_sample={worst_index}")
return 1 if mismatch_count else 0
if __name__ == "__main__":
sys.exit(main())
Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB