Relativity-Simulator

Technical Architecture & System Design

1. Architectural Principles & System Paradigms

The Relativistic Engine is an ISO C++23 simulation and visualization framework for relativistic mechanics and raytracing. The system is structured into four functional layers:

Fundamental Design Paradigms


2. Core Subsystems & Computational Hierarchy

The system architecture is organized into modular subsystems operating across decoupled boundaries:

2.1. Tensor Algebra & Curvature Evaluation Layer

2.2. Spacetime Metric Modules

Every spacetime implementation satisfies the SpacetimeMetric concept (include/relativistic/metrics/spacetime_concept.hpp), requiring metric_tensor, inverse_metric, christoffel_symbols, and speed_of_light:

2.3. Differential Solvers & Geodesic Integrators

2.4. Post-Newtonian Dynamics & Gravimetry

2.5. Dark Matter & Modified Gravity

2.6. Relativistic Hydrodynamics (GRHD/GRMHD)

2.7. Polarized Radiative Transfer & Optics

2.8. Uncertainty Quantification & Metrology

2.9. Render Pipeline

GeodesicComputePipeline (include/relativistic/render/geodesic_compute_pipeline.hpp) owns a front and a back framebuffer of GpuPixelOutput. In windowed mode a worker thread renders the most recent pending request (GpuCameraPushConstants and body list); a request arriving while a frame is rendering raises a cancellation flag, so superseded frames are abandoned. In headless mode dispatch renders synchronously. After each frame the pipeline classifies pixels (horizon absorbed, celestial escape, accretion disk hit, step saturated), computes iteration statistics and records tile statistics of the adaptive sky prepass.

Path selection. The Vulkan path is attempted when GPU compute is enabled and the executor is ready, and all of the following hold:

Otherwise SoftwareComputeEngine::dispatch_fp64 or dispatch_double_single renders on the CPU thread pool, with the precision taken from the custom parameter precision_mode. Capture rendering uses the same selection through render_capture and render_capture_band.

Vulkan execution (include/relativistic/render/vulkan_context.hpp, include/relativistic/render/vulkan_compute_executor.hpp).

Viewport frame logic (ViewportPrimaryWindow, include/relativistic/ui/viewport_primary_window.hpp).

2.10. Capture Subsystem

Coordinator. CaptureCoordinator (include/relativistic/capture/capture_coordinator.hpp) manages one capture session at a time. update(dt) is called once per UI frame and prepares frames; a worker thread executes the queued tasks (frame rendering, file writing, ffmpeg assembly). The capture constants are derived from the last constants dispatched by the viewport, with the current camera, exposure and mass parameters substituted (ViewportPrimaryWindow::build_capture_constants).

Frame rendering.

Deterministic mode. For each frame the coordinator processes script events, then for each of the temporal_samples sub-frame times: advances the simulation (ticks_per_frame multiplied by the script simulation rate and divided by the sample count, accumulated as a fractional tick count), applies the script pose, and stores constants and bodies. The shutter offsets are centered on the frame time and span shutter_fraction of the frame interval. At most two frames are queued at once. World advance modes: frozen, fixed ticks per frame, or simulation seconds per video second (ticks per frame = s / (fps * tick_dt)). With the trigger PathDuration, the frame index is mapped linearly onto the script duration. Pause and resume events freeze and release the world. Manual stepping renders one frame per request (at most 64 pending). The live viewport render is suppressed unless preview_in_viewport is set.

Real-time mode. The front framebuffer is copied at the frame interval. Pacing either drops late frames or duplicates the last frame to fill gaps; frames waiting for disk writing are limited by the queue depth. The viewport resolution multiplier is locked, and frames whose size changes are resampled by nearest neighbor to the first frame size.

Session end. The recorder is finalized, the camera, field of view, exposure, camera mode, warp factor and pause state are restored (the camera only when restore_camera_after_capture is set or a script was used), sequence_info.txt is written, and ffmpeg is invoked through std::system when assembly is enabled and the session completed. Frames are deleted after a successful assembly when requested. Output files are described in FILE_FORMATS.md.

Motion script (include/relativistic/capture/motion_script.hpp). A script is an ordered list of segments with a global easing and an end behavior (clamp, loop, ping-pong).

Expression language (include/relativistic/capture/expression.hpp). Expressions compile to a stack program. Variables: t (segment time), u (progress), d (duration), g (script time), a, b, c, k (parameters), r (radius), x, y, z (position), v (speed), m (driven measurement), p (script progress), s (segment index). Constants: pi, tau, e. Operators: + - * / % ^, < > <= >= == !=. Functions: sin cos tan asin acos atan sinh cosh tanh exp log ln log2 log10 sqrt cbrt abs floor ceil round fract sign rad deg saturate smooth noise tri saw square atan2 min max hypot step pow mod clamp mix if smoothstep. Division by zero evaluates to 0 and non-finite results are replaced by 0.

Recording and preview. PhysicsRecorder derives kinematic and relativistic channels from the live camera pose and the orchestrator state for each frame (FILE_FORMATS.md). build_path_preview (include/relativistic/capture/path_preview_builder.hpp) samples the script into a PathPreview (vertices, reference path without shake and transitions, markers, camera frustum) drawn by the schematic renderer.

2.11. Profiling, Logging and Persistence

PerformanceProfiler (include/relativistic/orchestrator/performance_profiler.hpp) keeps a ring of FrameSample records (default capacity 3600, minimum 16). Stage durations are accumulated through ScopedStageTimer and attached to the next recorded frame. The stages are FrameTotal, RenderDispatch, TextureUpload, HudOverlay, CameraUpdate, SchematicOverlay, PostProcessing, FramebufferReadback, GpuDispatchExecution, CpuDispatchExecution, AdaptiveTilePrepassSky, PixelClassification and CameraConstantsBuild.

Logging. EngineLog (include/relativistic/core/engine_log.hpp) keeps the last 4096 entries with a timestamp and severity (info, warning, error) and mirrors them to standard output or standard error.

Settings. UserSettings and CaptureStudioSettings are loaded at startup and saved by UiManager::export_runtime_settings. The crash guard and load policy are described in FILE_FORMATS.md.

2.12. Schematic View and HUD

SchematicViewRenderer (include/relativistic/ui/schematic_view_renderer.hpp) draws bodies, the central object, background grid, field lines, trails, orbit predictions, vectors and the capture path preview as a projected overlay, using the camera tetrad and the selected projection (pinhole when human_perspective_mode is set). Objects are depth-sorted and path segments are split at occlusion boundaries so that spheres cover the segments behind them. Orbit predictions integrate a two-body orbit around the central mass with a velocity Verlet scheme. Spacetime source bodies are drawn with a horizon disc, ISCO and disk outer rings and a spin axis. The overlay can be shown on top of the ray-traced image, optionally applying a first-order deflection $4M/b$ (limited to 0.35 rad) to body positions.

The HUD (HudLayoutConfig, include/relativistic/ui/hud_layout_config.hpp) consists of 25 elements with anchor, offset, scale, color, background, display mode (compact, standard, extended), horizontal layout, draw priority, decimal precision and warning and critical color rules. Automatic arrangement stacks elements per anchor without gaps. Frame time readouts use a rolling average over rolling_average_frame_count samples (2 to 120).

2.13. Physical Constants Engine

ConstantsEngine (include/relativistic/core/physical_constants_engine.hpp) stores the simulation values of $c$, $G$, $h$, $k_B$, $N_A$, $K_e$ and $K_{cd}$. It is constructed with the Planck preset ($c = G = k_B = K_e = K_{cd} = 1$, $h = 2\pi$, $N_A$ unchanged); UiManager then applies the preset stored in the user settings (SI by default). Setting any constant switches the preset to Custom. Each value is clamped to at least $10^{-300}$. The post-Newtonian solver uses the engine values of $c$ and $G$.

The scale factors between simulation units and SI units are:

\[T_0 = \sqrt{\frac{G_{SI}}{G}\frac{h_{SI}}{h}\left(\frac{c}{c_{SI}}\right)^5}, \quad L_0 = \frac{c_{SI} T_0}{c}, \quad M_0 = \frac{G}{G_{SI}}\frac{L_0^3}{T_0^2}\] \[Q_0 = \sqrt{\frac{K_e}{K_{e,SI}}\frac{M_0 L_0^3}{T_0^2}}, \quad K_0 = \frac{k_B}{k_{B,SI}}\frac{M_0 L_0^2}{T_0^2}, \quad N_0 = \frac{N_A}{N_{A,SI}}, \quad I_0 = K_{cd}, \quad A_0 = \frac{Q_0}{T_0}\]

Derived simulation quantities include the reduced Planck constant $h / 2\pi$, the Stefan-Boltzmann constant $2\pi^5 k_B^4 / (15 h^3 c^2)$, the vacuum permittivity $1 / (4\pi K_e)$, the vacuum permeability $4\pi K_e / c^2$, the magnetic coupling $K_e / c^2$ and astronomical quantities (solar mass, astronomical unit, Earth and Jupiter masses, parsec, light year, particle masses, solar Schwarzschild radius, Thomson cross section, Wien constant) divided by the corresponding scale. The Constants window accepts unit-aware expressions checked against the expected dimension (Units::ExpressionEvaluator::evaluate_expect_dimension).


3. Concurrency, Execution Pipeline & Scheduling

The system employs a multi-threaded execution model designed to eliminate synchronization stalls & lock contention:

3.1. Deterministic Simulation Scheduler

The execution flow is governed by Scheduler, maintaining a fixed logical clock frequency (10 to 1000 Hz) decoupled from visual presentation refresh rates:

3.2. Asynchronous Lock-Free Command Queue

Communication between the master terminal/UI & the simulation core is mediated by SpscQueue (Single-Producer Single-Consumer lock-free ring buffer):

3.3. Thread Pool & Raytracing Work Distribution

3.4. Orchestrator State Advance

SimulationOrchestrator::advance_simulation(dt) (include/relativistic/orchestrator/simulation_orchestrator.hpp) performs the following steps:

  1. The central body is synchronized: a body named Central Object of mass $M$, radius $2M$ and spin $(0, 0, aM)$ is registered as the spacetime source, unless an enabled body of mass above $0.5M$ already sits at the origin.
  2. The interaction configuration is passed to the N-body system.
  3. The N-body system is advanced. The step is divided into $\min(500, \lceil \Delta t / \Delta t_s \rceil)$ sub-steps with $\Delta t_s = 0.05 / \omega_{\max}$ and $\omega_{\max} = \sqrt{\max(M, 10^{-4}) / \max(r_{\min}^3, 10^{-6})}$, where $r_{\min}$ is the smallest radius of an enabled body. Each sub-step applies the symplectic Forest-Ruth post-Newtonian integrator when the integrator name contains Symplectic or Gauss, and the fourth-order Runge-Kutta post-Newtonian integrator otherwise, followed by the interaction step and the horizon absorption step. Bodies disabled before the step keep their state.
  4. In the Rocket Thrust camera mode, the camera position is integrated with the camera velocity while the scheduler is not paused.
  5. The state version counter is incremented. The user interface compares this counter to detect parameter changes and to trigger a re-render.

Horizon absorption (handle_horizon_absorption) operates on the central mass with spin clamped to $\pm 0.999M$ and the outer horizon radius $r_h = M + \sqrt{M^2 - a^2}$:

Command application, including the Reset semantics, is described in CLI_REFERENCE.md. Changes to $c$ and $G$ in the constants engine are copied to the N-body configuration.

3.5. Scenario Resolution and Startup

ScenarioLocator (include/relativistic/io/scenario_locator.hpp) resolves files and the scenario directory relative to the working directory and up to four parent levels (kMaxAncestorDepth). A requested path is tried as given, then prefixed by up to four .. components, then, when its parent directory is named scenarios, against the located scenario directory. portable_path stores paths inside the scenario directory in the form scenarios/<file>.

At startup UiManager::queue_startup_scenario loads the configured default_scenario_path when load_scenario_on_startup is set. If the file cannot be resolved, parsed or validated, the built-in scenarios/schwarzschild_accretion.yaml is loaded instead and becomes the stored startup path; when it is also unavailable, the simulation starts empty. The Scenario Manager scans the directory for .yaml and .yml files, marks files that fail parsing or validation as incompatible, protects the built-in startup file from deletion, and saves new presets under collision-free file and scenario names (<stem>_<n>.yaml and <name> (<n>)).


4. Precision Architecture & Numerical Stability

The engine provides multi-tiered precision configurations to balance numerical accuracy & throughput:

4.1. Hardware-Native 64-Bit Precision (FP64)

4.2. Compensated Double-Single Arithmetic (DS / fp32-fp32)

4.3. SIMD Register Vectorization


5. Control, Interface & Multi-Window Architecture

The platform provides decoupled interfaces for interactive exploration & batch execution:

5.1. Master Terminal Loop (REPL)

5.2. Graphical Multi-Window Workspace (UI Architecture)

When executing in interactive mode, UiManager coordinates GLFW windowing, OpenGL 3.3+ rendering contexts, & ImGui/ImPlot multi-viewport docking:


6. Scientific I/O & External Interoperability Pipeline

The engine interfaces with standard astronomical data formats & ephemeris services:

6.1. Ephemeris & Orbital Mechanics Ingestion

6.2. Scientific Data Serialization