|
Orrery
A GPU-accelerated N-body gravitational simulator
|
The trajectory file: what the simulation looked like, frame by frame. More...
#include <cstdint>#include <filesystem>#include <fstream>#include <span>#include <string_view>#include <vector>#include "orrery/core/particle_data.hpp"#include "orrery/core/types.hpp"#include "orrery/core/vec3_array.hpp"Go to the source code of this file.
Classes | |
| struct | orrery::sim::TrajectoryInfo |
| What a trajectory file's header says about it. More... | |
| struct | orrery::sim::TrajectoryFrame |
| One recorded instant. More... | |
| class | orrery::sim::TrajectoryWriter |
| Writes the format above. More... | |
| class | orrery::sim::TrajectoryReader |
| Reads the format above. More... | |
Variables | |
| constexpr std::string_view | orrery::sim::kTrajectoryMagic = "ORRERYTJ" |
| The eight bytes every trajectory file starts with. | |
| constexpr std::uint32_t | orrery::sim::kTrajectoryVersion = 1 |
| The version of the layout in docs/formats/trajectory.md. | |
The trajectory file: what the simulation looked like, frame by frame.
A run that keeps nothing produces one number at the end. A run that writes every step as text produces a file larger than the machine's memory and takes longer to write it than to compute it. This format is the middle: a fixed header, then one frame per recorded step, each holding the positions and optionally the velocities in the same component-array layout the solvers use. docs/formats/trajectory.md is the specification.
Three decisions in it are worth stating here because they are what separate it from a stream of write(&struct) calls.
The masses are in the header, not in the frames. They do not change during a run, and a million-particle trajectory of a thousand frames would otherwise carry eight gigabytes of a number that was already known. Having them at all is what lets a reader compute an energy from a frame without the configuration that produced it.
There is no frame count. The obvious header field cannot be written until the run has finished, and a run that is killed by a full disc, a closed lid or a scheduler is exactly the run whose output someone will want to look at. So frames are self-delimiting and a reader consumes them until the file ends. The cost is that a reader must scan to count them; the benefit is that a file from an interrupted run is a valid file that stops early rather than a file whose header describes frames that are not in it.
Every frame carries its own checksum, rather than the file carrying one. That follows from the same argument: a whole-file checksum can only be verified once the file is complete, so it would be absent from precisely the files that most need checking. A per-frame checksum means a reader can accept every frame that was written completely and reject a final one that was cut in half, and can say which is which.
A checkpoint. A trajectory frame is a record for a renderer or an analysis to read, it may hold no velocities at all, and it holds no accelerations even when it does, so a run cannot be resumed from one. sim/checkpoint.hpp exists for that, and ADR-0033 records why the two are separate formats rather than one format with the trajectory as a lossy setting of it.
|
inlineconstexpr |
The eight bytes every trajectory file starts with.
A file that does not start with these is not one of these, which is the first thing a reader checks and the reason a checkpoint pointed at a trajectory reader is rejected in the first eight bytes rather than by producing nonsense.
|
inlineconstexpr |
The version of the layout in docs/formats/trajectory.md.
A reader refuses a version it does not know rather than guessing. There is one version and this is it; the field exists so that a second one can be added without the first becoming unreadable.