MCRT output processors
Configure processors under mcrtsimulation.outputprocessors. This page covers
photon streams, output formats, and compression. See MCRT simulation and sources
for transport and emission settings, including packet weights.
Completed MCRT output files store line_name and
line_rest_wavelength_angstrom as root HDF5 attributes in each enabled photon
stream. The wavelength is the reference used for photon frequency offsets.
Python postprocessing uses this value, so adding a line does not require a
separate Python wavelength entry. Older files use the configured line.name
or linename and the legacy wavelength table; an unknown line without metadata
raises an error.
Output processors transform or aggregate photon data. The photon processor
(raw photon dump) is the most common. For photon streams, raw is an alias of
photon, and void performs no output processing.
Common parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
type |
string | required | Processor type ("photon", "raw", "void", "histogram1d", "histogram2d", "apertureSpectrum", "sbmap", etc.) |
active |
bool | true |
Enable/disable this processor |
streams |
list of strings | processor-dependent | Restrict processor to specific photon streams; [] allows all |
assess_convergence |
bool | false |
Enable convergence assessment in processors that support it |
convergence_threshold |
float | 0.01 |
Relative change threshold for convergence |
active, streams, and convergence settings inherit driver-level values unless
overridden in a processor.
MCRT decodes all output settings before initialization. Physical-unit conversions
and checks requiring the dataset or observer context run when constructing each
processor.
Set mcrtsimulation.suffix to label output filenames. Processors within a stream
share that file.
Stream filtering
THOR runs up to three photon streams: original (escaped photons), peel (peeling-off photons), and input (initial photon state). The streams parameter selects which streams a processor receives:
mcrtsimulation:
outputprocessors:
- type: "photon"
active: true
- type: "sbmap"
streams: ["peel"] # only process peeling photons (default for sbmap)
nbins_x: 64
nbins_y: 64
left_x: 0.0
right_x: 1.0
left_y: 0.0
right_y: 1.0
weight: "weight"
- type: "histogram1d"
# no streams → active for all streams
quantity: "dlambda"
weight: "weight"
left: -1.5
right: 1.5
nbins: 30
assess_convergence: true
convergence_threshold: 0.1
sbmap defaults to streams: ["peel"]; apertureSpectrum and the histogram1d
aperture do so when peeling is enabled, using the peeling observer directions. An explicit
line_of_sight switches the default to streams: ["input"]; sbmap and
apertureSpectrum also accept lines_of_sight for multiple input views.
On the input stream, the histogram1d aperture requires a single explicit line_of_sight.
An explicit streams setting overrides these defaults.
On the peel stream, aperture processors reject explicit line_of_sight and
lines_of_sight; set observer directions under peeling.lines_of_sight.
Selecting dlambda_boxcenter in histogram1d or on either axis of histogram2d
defaults to streams: ["peel"] and requires peeling, even with an explicit frame.
apertureSpectrum also supports input and follows the frame defaults above.
Set streams: ["input"] to compute intrinsic spectra along the peeling observer
directions. Explicit streams always overrides the default.
Automatic raw output
If no raw, photon, or trace processor is listed, THOR adds a raw writer that inherits the driver's active and streams settings. With their defaults, it runs on all streams. To suppress it, list one of these types with active: false.
HDF5 compression
By default, compressed output datasets use HDF5's built-in shuffle + gzip filters. Both are part of libhdf5 itself — no filter plugin is needed, and h5py, h5ls, and any other stock HDF5 reader decompress transparently.
To use lossless LZ4 compression instead, set:
This driver-level block enables LZ4 compression for every output processor that
inherits it, including raw, photon, and trace. Per-processor settings take
precedence over inherited driver-level settings.
Available codecs are lz4, zlib, and raw. Selecting lz4 or zlib enables
compression; raw disables it. Shuffle can be none or byte; LZ4 also supports
bit. Existing compress_* settings still work; use either compression or
compress / compress_level / compress_shuffle at each configuration level.
Chunk sizing and compress_float32 remain separate options.
MCRT checks compression settings during preflight, including overrides in active output processors. Encoder availability is checked when preparing the HDF5 writer.
LZ4 output requires a Blosc-enabled reader. For Python, install hdf5plugin and
import hdf5plugin before reading with h5py. If a selected codec is unavailable
in your build, THOR warns and writes uncompressed output. Invalid settings are
errors.
Compression can reduce file size at a CPU cost. Test photon-column compression on
your workload; use compress_float32 only when reduced precision is acceptable.
The following defaults apply only when neither the driver nor the processor sets
compression or compress:
| processor class | compress default |
why |
|---|---|---|
sbmap, apertureSpectrum, histogram1d, histogram2d |
true |
Compressed once at finalization |
raw, photon, trace (and the driver's trace dump) |
false |
Written in batches during transport |
Without compress_float32, compression preserves values exactly.
mcrtsimulation:
compress_float32: true # inherited by every processor below
outputprocessors:
- type: "photon"
compress: true # opt the streamed columns in, against their class default
compress_level: 0 # narrow to float32 without deflate
- type: "sbmap"
compress_float32: false # override one key; still compressed by class default
nbins_x: 512
nbins_y: 512
left_x: 0.0
right_x: 1.0
left_y: 0.0
right_y: 1.0
| Parameter | Type | Default | Description |
|---|---|---|---|
compress |
bool | class-specific (see above) | Chunked layout + filters |
compress_level |
int | 4 |
gzip/deflate level, 0–9. 0 keeps chunking and shuffle but skips deflate. |
compress_shuffle |
bool | true |
Byte-shuffle filter. Groups the like-valued exponent bytes of a float array together so gzip can exploit them. |
compress_float32 |
bool | false |
Store float64 arrays as float32. Lossy — about 7 significant decimal digits survive. Accumulation always stays in full precision; only the written array is narrowed. Requires compress. |
compress_min_bytes |
int | 65536 |
Write-once datasets only. Maps, spectra and histograms below this size stay contiguous and unfiltered — chunk and filter overhead is not worth it. Set to 0 to always chunk. Streamed columns are exempt: they are resizable, so their final length is unknown when the dataset is created and the decision cannot depend on it. |
compress_chunk_bytes |
int | 1048576 |
Target uncompressed bytes per chunk for write-once datasets. |
compress_append_chunk_bytes |
int | 262144 |
Target bytes per chunk for streamed columns. Should stay comfortably under both HDF5's default 1 MiB chunk cache and one batch's worth of rows (nphotons_step_max), or partially-filled chunks get decompressed and recompressed on every batch that touches them. |
compress_float32 and geometry
The downcast applies to bulk payloads only (images, spectra, photon columns). The line_of_sight, e_x, and e_y frame datasets always keep full precision, because readers use them to reconstruct image-plane coordinates — a narrowed basis vector would propagate error into positions rather than only into a plotted value. Dataset attributes (bin edges, weights, radii) are never downcast either.
Chunks are sized by target bytes, trimming the slowest-varying axis first, so a
[nlos, nx, ny] map chunks into whole images or contiguous row bands of one image.
Photon output
Writes photon data for the selected streams to HDF5. Select columns under
mcrtsimulation using photon_fields for original photons, photon_fields_peel
for peeling photons, and photon_fields_input for input photons. These lists take
precedence over a raw processor's fields. For raw trace output, tfields takes
precedence over fields; an explicit tfields: [] writes no columns.
With multiple peeling observers, peel columns are grouped under los_000/,
los_001/, and so on. Single-observer peel columns and original/input columns
are stored at the file root.
| Parameter | Type | Description |
|---|---|---|
active |
bool | Enable this processor |
Histogram 1D
Experimental
This feature is experimental and may change.
Bins photon field values into a histogram (e.g., spectrum). Binning settings inherit driver-level values unless overridden. Bin counts must be positive and bounds must give finite, positive widths in the build's floating-point precision. The lower bound is inclusive; the upper bound is exclusive.
| Parameter | Type | Default | Description |
|---|---|---|---|
quantity |
string | "dlambda" |
Field to histogram (e.g., "dlambda" for a spectrum) |
weight |
string | "weight" |
Weight field (currently only "weight") |
left |
float | required | Left bin edge |
right |
float | required | Right bin edge |
nbins |
int | 100 |
Number of bins |
name |
string | (=quantity) |
Output dataset name under /histogram1d/; set it to run several histograms of the same quantity (e.g. a full-field and an aperture spectrum) in one file without colliding |
aperture_radius |
number or string | (off) | If set, restrict to photons within this radius of the aperture center — enables aperture mode. Unit-aware: a bare number is a box fraction; a string takes a unit, e.g. "7 pkpc" or "1 arcsec" (see below) |
aperture_center |
vec2 | [0.5, 0.5] |
Aperture center in image-plane coordinates (default the projection center) |
center |
vec3 | [0.5, 0.5, 0.5] |
Projection center for the image-plane mapping (shared with sbmap) |
north |
vec3 | [0, 0, 1] |
Image orientation hint (shared with sbmap) |
assess_convergence |
bool | false |
Check convergence |
convergence_threshold |
float | 0.01 |
Convergence tolerance |
The optional aperture restricts the spectrum to a circular region of the image plane, using the same centered projection convention as sbmap (box center → image (0.5, 0.5)). With multiple peeling observers it applies per-observer (each photon is masked against its own line-of-sight frame) and the output is stacked [nlos, nbins], matching the full-field spectrum. This produces the aperture-integrated line profile during the run, without dumping raw photons for offline masking.
The single aperture_radius key is unit-aware:
| Form | Meaning |
|---|---|
aperture_radius: 0.02 |
Box fraction — same coordinates as the sbmap left/right bounds (0.02 = 2%-of-box radius) |
aperture_radius: "7 pkpc" |
Physical kpc — converted via the dataset's length_unit (already physical cm, so no redshift factor) |
aperture_radius: "1 arcsec" |
Angular — converted to physical kpc via the flat-ΛCDM angular-diameter distance at the snapshot redshift, using the dataset's cosmology (Ω_m, Ω_Λ, h from the simulation header). Requires a cosmological dataset (z > 0) |
kpc is accepted as an alias for pkpc. An unknown unit, or arcsec on a non-cosmological dataset, is a config error.
Histogram 2D
Set type: histogram2d to bin two photon fields into a weighted 2D histogram.
Both axes follow the 1D histogram inheritance and bin-edge rules.
| Parameter | Type | Default | Description |
|---|---|---|---|
quantity_x / quantity_y |
string | required | Photon field for each axis |
left_x / right_x |
float | required | Bounds in quantity_x units |
left_y / right_y |
float | required | Bounds in quantity_y units |
nbins_x / nbins_y |
int | 100 |
Number of bins on each axis |
weight |
string | weight |
Only weight is supported |
Output is written to /histogram2d/<quantity_x>_vs_<quantity_y> with shape
[nbins_x, nbins_y]. Multiple peeling observers produce
[nlos, nbins_x, nbins_y], with one histogram per observer.
With photon-rate weights, if both axes describe photon frequency or wavelength, they must use the same reference frame.
Aperture spectrum (apertureSpectrum)
Experimental
This feature is experimental and may change.
Produces a spectrum for several concentric circular apertures at once, in a single binning pass. It is the multi-aperture generalization of the histogram1d aperture: instead of one aperture_radius, you give a list of aperture_radii and get one enclosed spectrum per radius (all flux within that radius), written to /aperturespectrum/.
| Parameter | Type | Default | Description |
|---|---|---|---|
aperture_radii |
list | required | Aperture radii. Each entry is unit-aware exactly like the histogram1d aperture_radius (bare number = box fraction, or "7 pkpc" / "1 arcsec"). Sorted ascending and de-duplicated at init |
nbins |
int | 100 |
Number of spectral bins |
left / right |
number | required | Spectral binning range (in quantity units) |
quantity |
string | dlambda |
Photon field to bin. On the input stream, dlambda projects the emitting-cell velocity separately for each LOS |
units |
string | weight |
Energy-weighted sums without physical calibration, or erg/s, erg/s/cm2, erg/s/cm2/Angstrom, erg/s/cm2/arcsec2, or erg/s/cm2/arcsec2/Angstrom |
observer_redshift |
float | Snapshot redshift | Observing redshift for distance, surface-brightness dimming, and observed wavelength-bin width |
luminosity_distance_cm |
float | From redshift and cosmology | Explicit positive luminosity distance in cm; required for flux at effectively zero redshift |
luminosity_unit_erg_s |
float | 1e42 |
Luminosity represented by one weight unit at the line rest frequency; calibrate this if packet weights use another convention |
weight |
string | weight |
Photon weight field (only weight supported) |
aperture_center |
vec2 | [0.5, 0.5] |
Aperture center in image-plane coordinates |
center |
vec3 | [0.5, 0.5, 0.5] |
Projection center for the image-plane mapping (shared with sbmap/histogram1d) |
north |
vec3 | [0, 0, 1] |
Image orientation hint (shared with sbmap) |
line_of_sight |
vec3 | (from peeling) | Explicit observer direction for non-peel streams |
lines_of_sight |
list of vectors | absent | Observer directions for non-peel streams; mutually exclusive with line_of_sight |
name |
string | (=quantity) |
Output dataset name under /aperturespectrum/ |
Each photon is projected onto its observer's image plane and assigned to the radial bucket between the sorted radii (a single upper_bound); a photon beyond the largest radius is dropped. At finalize the buckets are summed inner→outer, so aperture k holds the flux enclosed within radius r_k — the same "inside radius" meaning as the single histogram1d aperture, for every radius simultaneously. Doing the bucket sort once avoids re-masking the photon stream per aperture.
Like the histogram1d aperture, it needs a fixed observer frame. With peeling enabled and no explicit frame, it defaults to the peel stream (peeling.lines_of_sight); an explicit line_of_sight or lines_of_sight defaults to the input stream. See Stream filtering for frame restrictions when overriding streams. Output is [n_apertures, nbins] for a single observer, or stacked [nlos, n_apertures, nbins] with N>1 observers or any explicit lines_of_sight broadcast list (even a one-entry list → [1, n_apertures, nbins]); per-observer frames are in /aperturespectrum/line_of_sight, /e_x, /e_y. The sorted radii and projection geometry are stored as dataset attributes (aperture_radii, n_apertures, aperture_center, center, north).
Physical units are available for peel and input. The peel stream describes
radiation directed toward an observer after transport. Input projections describe
intrinsic emission, assuming isotropic emission without attenuation or scattering.
Each input view receives the full projected source luminosity; THOR does not divide
it among viewing directions. Physical units are unavailable for original, whose
packets sample different escape directions. The radiation_origin attribute
records how to interpret the output.
With units: erg/s, each bin contains the isotropic-equivalent luminosity enclosed
by the aperture. Flux modes divide this luminosity by \(4\pi D_L^2\). Mean
surface-brightness modes divide it by
\(4\pi A(1+z_\mathrm{obs})^4(206265)^2\), where \(A\) is the full circular aperture area
in physical cm², including any area outside the dataset. Because brightness is
averaged over the whole aperture, increasing its radius need not increase the
brightness.
Per-Å modes also divide by the observed wavelength-bin width,
\(\Delta\lambda_\mathrm{obs}=(1+z_\mathrm{obs})(\mathrm{right}-\mathrm{left})/\mathrm{nbins}\).
Set quantity to dlambda or dlambda_boxcenter; peel spectra also support
dlambda_peel_shifted. The left and right attributes describe the transport
wavelength offsets in Å. Flux and surface-brightness modes preserve the absolute
normalization; neither divides by the spectrum sum.
THOR treats \(|z_\mathrm{obs}|<10^{-10}\) as zero. Luminosity and mean surface
brightness remain valid at zero redshift. Flux requires an explicit
luminosity_distance_cm. At positive redshift, THOR can calculate the distance
from a valid flat matter-plus-Λ cosmology. You can also supply a distance for local
or non-cosmological datasets.
Angular apertures in physical modes use the selected observer distance, with
\(D_A=D_L/(1+z)^2\). Thus arcsec radii also require an explicit distance at zero
redshift. Apertures in pkpc or box fractions need no distance for luminosity or
surface brightness. Observer overrides change output calibration and angular
aperture sizes. They leave photon transport and the gas state unchanged.
mcrtsimulation:
outputprocessors:
- type: apertureSpectrum
units: erg/s/cm2/arcsec2/Angstrom
aperture_radii: [10 pkpc, 30 pkpc]
left: -5
right: 5
nbins: 80
THOR writes one spectrum per configured processor. Its dataset, such as
/aperturespectrum/dlambda, contains values in the selected units and records
units, observer_redshift, luminosity_unit_erg_s, and
weight_to_spectrum_factors. It also records the available physical scale,
distance, aperture areas, and observed bin width.
Bin sums use the build's FP32 or FP64 precision. Scaling to physical units produces
float64 output arrays. With compress_float32: true, these are stored as float32
if all values fit its numeric range; otherwise, they remain float64. Lossless
compression remains available. Uncalibrated output records units: weight.
In MPI runs, aperture spectra sum contributions from all ranks during
finalization. Only rank 0 writes the complete spectra and observer frames,
in data.0.h5. Each spectrum records mpi_aggregation: sum and
mpi_rank_count. The Python read_aperture_spectrum() reader reads these
complete products directly and still sums older rank-local spectrum files.
It rejects mixed global and rank-local products to prevent double-counting.
For intrinsic aperture spectra and IFU cubes, quantity: dlambda uses each
observer's direction and the emitter velocity:
dfreq_los = dfreq_rest + (nu0 + dfreq_rest) × (v · los) / c.
With write_inputphotons_restframe: false, THOR first inverts the Doppler shift
along the original launch direction, then applies the observer shift. This
does not include the final peel-flight Hubble shift used by dlambda_peel_shifted.
quantity: dlambda_boxcenter adds the signed Hubble shift from the emission
position to [0.5, 0.5, 0.5] after the LOS Doppler correction. With periodic
boundaries, aperture position and frequency use the source's primary box image;
the crop's center does not change the frequency reference. Intrinsic spectra
use the emitting cell's bulk velocity and unattenuated weights.
dlambda_peel_shifted supports only peel photons.
mcrtsimulation:
outputprocessors:
- type: "apertureSpectrum"
quantity: "dlambda"
lines_of_sight: [[1, 0, 0], [0, 1, 0], [-1, 0, 0]]
aperture_radii: ["1 pkpc", "5 pkpc"]
nbins: 200
left: -10.0
right: 10.0
mcrtsimulation:
outputprocessors:
- type: "apertureSpectrum"
quantity: "dlambda_peel_shifted"
aperture_radii: ["1 arcsec", "2 arcsec", "5 arcsec"]
nbins: 200
left: -10.0
right: 10.0
Surface brightness map (sbmap)
Experimental
This feature is experimental and may change.
Projects photon positions onto a 2D image plane perpendicular to the line of sight. Produces an HDF5 dataset at /sbmap/image.
| Parameter | Type | Default | Description |
|---|---|---|---|
nbins_x |
int | 100 |
Number of pixels in x |
nbins_y |
int | 100 |
Number of pixels in y |
left_x |
float | required | Left edge of image in x |
right_x |
float | required | Right edge of image in x |
left_y |
float | required | Left edge of image in y |
right_y |
float | required | Right edge of image in y |
weight |
string | "weight" |
Weight field (currently only "weight" supported) |
units |
string | "weight" |
Energy-weighted sums without physical calibration, or "erg/s/cm2/arcsec2" for absolute line-integrated surface brightness |
spectral |
mapping | absent | Enable wavelength-resolved image output; see Wavelength cubes |
observer_redshift |
float | Dataset redshift | Observing redshift for cosmological surface-brightness dimming; physical units only |
luminosity_unit_erg_s |
float | 1e42 |
Luminosity represented by one weight unit at the line rest frequency; physical units only |
north |
vec3 | [0, 0, 1] |
North hint vector for image orientation |
center |
vec3 | [0.5, 0.5, 0.5] |
Projection center; maps to image coordinate (0.5, 0.5) for every observer |
line_of_sight |
vec3 | (from photons) | Explicit observer direction for non-peel streams |
lines_of_sight |
list of vectors | absent | Observer directions for non-peel streams; mutually exclusive with line_of_sight |
los_indices |
list of ints | (all observers) | Select peeling observers by original zero-based index, preserving the listed order |
los_stride |
int | (all observers) | Select original observer indices 0, N, 2N, ...; must be positive |
streams |
list | ["peel"], or ["input"] with an explicit frame |
Allowed streams; peel-only by default, input-only when line_of_sight/lines_of_sight is set (an explicit streams overrides) |
The image frame uses the line-of-sight direction and north hint. Peel maps use
peeling.lines_of_sight. Setting line_of_sight or lines_of_sight on the
processor defaults to the input stream, imaging intrinsic source photons.
A lines_of_sight list projects each source photon into every frame and produces
[nlos, nbins_x, nbins_y] output. Peel maps reject lines_of_sight overrides and
ignore line_of_sight with a warning.
Image coordinates use a centered convention: image_x = (pos - center)·e_x + 0.5
(and likewise for y), so the projection center maps to (0.5, 0.5). Both axes
require explicit bounds. With the default north and box center, [0, 1] covers
an axis-aligned unit-box projection. Off-axis views need wider bounds to avoid
clipping the projected corners. Zoom by narrowing left/right around 0.5 or
moving center onto the target. THOR warns at finalization if every photon
received by an observer falls outside the binning range.
With multiple peeling observer directions (peeling.lines_of_sight, N>1), one image slab per observer is accumulated and written as a stacked [nlos, nbins_x, nbins_y] dataset, with per-observer frames stored as [nlos, 3] datasets (/sbmap/line_of_sight, /sbmap/e_x, /sbmap/e_y).
To write absolute surface brightness, add these settings to the sbmap processor:
THOR needs the physical domain length to convert pixel areas. Physical units support two streams:
peel: radiation directed toward an observer after transport.input: intrinsic emission, assuming isotropic emission without attenuation or scattering. Each view receives the full projected source luminosity.
The radiation_origin attribute records which interpretation applies. Physical units are unavailable for original, whose packets sample different escape directions.
/sbmap/image contains surface brightness integrated over the line. The selected
units apply to this single image dataset.
Bin sums use the build's FP32 or FP64 precision. Scaling to physical units produces
float64 output arrays. With compress_float32: true, these are stored as float32
if all values fit its numeric range; otherwise, they remain float64. Lossless
compression remains available.
Each pixel accumulates energy-converted weights,
\(W = \sum_i w_i\nu_{\mathrm{recorded},i}/\nu_0\).
Standard emitters use a luminosity scale of \(10^{42}\) erg/s; set
luminosity_unit_erg_s if your packet normalization differs. With the default
scale and projected physical pixel area \(A\) in cm², the surface brightness is
This preserves the absolute luminosity without normalizing by the map total. The distance cancels from the conversion, so no cosmological distance integration is needed. At zero observing redshift, the brightness has no cosmological dimming. Changing observer_redshift affects only the output conversion; the gas state and photon transport stay the same.
The image records units, length_unit_cm, observer_redshift, pixel_area_cm2, luminosity_unit_erg_s, and weight_to_image_factor so you can reproduce the conversion. Uncalibrated output records units: weight and the physical length when available.
los_indices and los_stride are mutually exclusive, per-processor selectors. They reduce only the SB-map slabs retained in memory and written to disk; peeling photons are still generated and transported for every configured observer, so other processors such as apertureSpectrum continue to receive all observers. Explicit indices must be unique, non-negative, in range, and non-empty. These selectors are supported only on the peel stream.
In MPI runs, SB maps sum contributions from all ranks during finalization.
Only rank 0 writes /sbmap/image and its observer frames, in data.0.h5.
Other rank files retain their raw photon outputs but contain no SB map.
The image records mpi_aggregation: sum and mpi_rank_count; do not sum this
image again across rank files. Empty ranks contribute zero pixels. Other histograms retain their existing
per-rank output behavior.
For a subset selected from a multi-observer context, /sbmap/image keeps the shape [K, nbins_x, nbins_y] and the frame datasets keep [K, 3], including when K is one. The image dataset receives a los_indices attribute whose entries give the original observer index for each output slab.
Wavelength cubes
Add spectral to a peel- or input-stream sbmap:
spectral:
frame: box_center # default: peel_endpoint on peel, box_center on input
wavelength_range_angstrom: [1024, 1240]
bin_width_angstrom: 2
For unequal bins, supply explicit edges instead of the range and width:
Edges must be finite, positive and strictly increasing, with at least two
entries. Do not combine wavelength_edges_angstrom with
wavelength_range_angstrom or bin_width_angstrom.
/sbmap/cube has shape [nx, ny, nwavelength], or
[nlos, nx, ny, nwavelength] for stacked observers. /sbmap/wavelength_edges_angstrom
stores the absolute wavelength edges. Bins include the left edge, exclude the
right, and use the supplied edges exactly. With range-and-width configuration,
the final bin is clipped to the requested range. Both the cube and
/sbmap/image exclude photons outside this range; summing over wavelength
reproduces the image.
spectral.frame selects the frequency and position reference:
peel_endpointprojects the transport endpoint and includes the final peel-flight Hubble shift. It supports only the peel stream.box_centerprojects the launch event in the primary periodic box image and references its frequency to[0.5, 0.5, 0.5], independently of the image crop. Peel wrapping leaves the pixel and frequency label unchanged; peel distance still affects attenuation. This is a frozen-snapshot view of one box image.
For box_center, the linear Hubble law gives
νcentre = νlaunch + (H L / c) ν₀ (xlaunch − 0.5) · n;
the fractional law gives νcentre = νlaunch exp[(H L / c) (xlaunch − 0.5) · n].
Here L is proper box length, H is hubble_flow, and n points toward the
observer. The launch frequency includes the LOS Doppler shift and prior transport.
For intrinsic cubes, enable save_inputphotons and set streams: ["input"].
Supply unit line_of_sight/lines_of_sight vectors or reuse the peeling
directions. Each LOS receives the full isotropic, unattenuated source weight,
with the cell-velocity and box-centre corrections used by intrinsic aperture spectra.
Both frames precede observer redshift: multiply edges by 1 + observer_redshift
for observed wavelengths. Values are bin-integrated brightness in the image's
units; divide by each bin's rest-frame or observed width for the corresponding per-Å
density. Raw peel output exposes the same box-centre conversion as
dlambda_boxcenter; include origin to retain the launch position.
Cube memory per rank is nlos × nx × ny × nwavelength × sizeof(FP) bytes,
in addition to image and transport memory. Compression affects disk size only.
MPI sums the cube onto rank zero. Convergence checks use the band-integrated image.
For angular pixels, use the dataset's proper scale; observer_redshift changes
brightness calibration, not physical size. A periodic peel distance cutoff does
not define a common observer plane.