Add error-controlled DP5(4) integration and trusted first-crossing localization, including non-monotonic energy thresholds and representable-time stepping. Preserve adaptive state and independent step/time retry grants across RayPool, refinement and movie scheduling. Expose numerical controls, record actual persistent-sample costs, and add v3 lens-map provenance with legacy v2 RK4 import. Use DP54 by default and select an 8M Schwarzschild maximum step from bounded scans and a two-run 4K comparison. Retain the conservative minimum-step guard and document critical-ray and backend capability limits. Archive self-contained benchmark inputs and raw output; keep fixed RK4 HDR references explicit. Validation: make -B -j4 BUILD_TYPE=Debug test passed; explicit RK4 HDR references have zero differences. Bounded convergence checks, benchmark reproduction, Release build and focused reviews passed. No numerical-relativity backend is added.
85 lines
3.5 KiB
Python
Executable File
85 lines
3.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Bounded 320x180 adaptive-mesh candidates (8 cases; no 4K, no references).
|
|
|
|
Usage:
|
|
run_mesh.py [OUT_DIR]
|
|
|
|
OUT_DIR defaults to <repo>/local/adaptive_bounds_mesh. The renderer binary is
|
|
expected to already exist at build/Release/schwarzschild_sky; build it with
|
|
(make command is also in README.md):
|
|
|
|
make -j4 BUILD_TYPE=Release SPACETIME=schwarzschild backend
|
|
|
|
No survey catalog is used: the script writes a one-point synthetic dim catalog
|
|
with the required column header (longitude_deg,latitude_deg,temperature_K,
|
|
amplitude) and a 1e-30 amplitude star. No external CSV is read.
|
|
"""
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import shlex
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
from mesh_maps import load, compare # tracked sibling module (no local/ import)
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
OUT = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else \
|
|
ROOT / 'local/adaptive_bounds_mesh'
|
|
BIN = ROOT / 'build/Release/schwarzschild_sky'
|
|
BUILD_CMD = 'make -j4 BUILD_TYPE=Release SPACETIME=schwarzschild backend'
|
|
ENV = dict(os.environ, OMP_NUM_THREADS='8', OMP_DYNAMIC='FALSE')
|
|
|
|
|
|
def scene_args(scene):
|
|
if scene == 'r100':
|
|
return ['--observer-radius', '100', '--look-ra-deg', '262.5',
|
|
'--look-dec-deg', '-30', '--fov-deg', '45',
|
|
'--refine-jacobian-min', '.2']
|
|
return ['--observer-position', '2.1', '0', '0', '--observer-velocity',
|
|
'0', '0', '0', '--look-ra-deg', '0', '--look-dec-deg', '0',
|
|
'--fov-deg', '90']
|
|
|
|
|
|
def main():
|
|
if not BIN.exists():
|
|
raise SystemExit(f'missing {BIN}; build it first:\n {BUILD_CMD}')
|
|
OUT.mkdir(parents=True, exist_ok=True)
|
|
catalog = OUT / 'single_dim_star.csv'
|
|
catalog.write_text('longitude_deg,latitude_deg,temperature_K,amplitude\n'
|
|
'262.5,-30,6000,1e-30\n')
|
|
results = {}
|
|
for scene in ('r100', 'r2p1'):
|
|
meshes = {}
|
|
for upper in (.5, 2, 8, 32):
|
|
name = f'{scene}_hmax{upper:g}'
|
|
cmd = [str(BIN), '--integrator', 'dp54',
|
|
'--width', '320', '--height', '180',
|
|
'--coarse-cell-pixels', '8', '--refine-max-level', '3',
|
|
'--catalog', str(catalog), '--exposure', '1',
|
|
'--psf-min-y', '1e-20', '--psf-relative-tail', '1e-4',
|
|
'--ode-min-step', '1e-12', '--ode-max-step', str(upper),
|
|
'--verbose', '--lens-map-output', str(OUT / (name + '.grlens')),
|
|
'--output', str(OUT / (name + '.png')), *scene_args(scene)]
|
|
start = time.perf_counter()
|
|
with (OUT / (name + '.log')).open('w') as log:
|
|
log.write('OMP_NUM_THREADS=8 OMP_DYNAMIC=FALSE ' +
|
|
shlex.join(cmd) + '\n')
|
|
log.flush()
|
|
subprocess.run(cmd, cwd=ROOT, env=ENV, stdout=log,
|
|
stderr=subprocess.STDOUT, check=True, timeout=180)
|
|
info, meshes[upper] = load(OUT / (name + '.grlens'))
|
|
info['wall_seconds'] = time.perf_counter() - start
|
|
results[name] = info
|
|
print(name, json.dumps(info), flush=True)
|
|
for upper in (.5, 8, 32):
|
|
comparison = compare(meshes[2], meshes[upper])
|
|
results[f'{scene}_2_vs_{upper:g}'] = comparison
|
|
print(f'{scene} 2 vs {upper:g}', json.dumps(comparison), flush=True)
|
|
(OUT / 'summary.json').write_text(json.dumps(results, indent=2) + '\n')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|