Coherence Length
The coherencelength raytracer operator measures how far a vector field stays aligned along each line of sight. Use cases: magnetic-field coherence in MHD simulations, velocity-field correlations, or any other vector quantity carried by the dataset.
How it works
Each pixel fires one ray that samples the vector field cell-by-cell. The operator opens a segment at the first cell where |v| ≥ min_field_magnitude and stores that cell's direction as the seed. The segment grows while the local direction stays within angle_threshold degrees of the seed. The first cell that exceeds the threshold closes the segment, records its length, and starts a new one seeded from the new direction. Cells below min_field_magnitude are skipped; they don't contribute and they don't terminate the running segment.
Output zarr layout (under projections/):
| Key | Shape | Description |
|---|---|---|
coherencelength |
(npy, npx) |
per-pixel arithmetic mean of segment lengths along that ray (cm). Pixels with no segments are 0.0. |
coherencelength_segments/segments |
(N_total,) |
every segment's length, flat across all rays (cm) |
coherencelength_segments/offsets |
(npx·npy,) |
start index into segments for each ray |
coherencelength_segments/counts |
(npx·npy,) |
segment count for each ray (0 if the ray had no signal) |
The per-segment arrays only exist when store_segments: true.
Magnetic-Field Coherence on a Cosmological MHD Box
The camera fires an orthogonal grid of rays across a periodic snapshot to map the coherence length of MagneticField. vector_field: MagneticField tells the operator to look up MagneticFieldX/Y/Z among the loaded fields. pbc: true since the box is periodic. angle_threshold: 90° is the loosest physically meaningful setting: a segment ends only when the field flips into the opposite hemisphere. Tighten it (e.g. 30°) for stricter alignment.
Full configuration
dataset_type: 'pointcloud_voronoi'
driver_type: 'raytracer'
device: "cpu-openmp"
pointcloud_voronoi:
hilbert_bits: 8
mesh_cache_mode: "auto"
loader: gadget
gadget:
path: /path/to/snapdir_127
fields:
- PositionX
- PositionY
- PositionZ
- Density
- Temperature
- MagneticFieldX
- MagneticFieldY
- MagneticFieldZ
raytracer:
pbc: true
dist_max: 1.01
min_step: 0
max_step: 1.0e-2
outputpath: ./coherence.zr
overwrite: true
operators:
coherencelength:
mode: manual
view: orthogonal
position: [0.01, 0.5, 0.5]
direction: [1.0, 0.0, 0.0]
up: [0.0, 0.0, 1.0]
npixels: [640, 640]
widths: [0.98, 0.98]
vector_field: MagneticField
angle_threshold: 90.0
min_field_magnitude: 1.0e-20
store_segments: true
max_segments_per_ray: 1000
max_segments_per_ray bounds the per-ray segment buffer. Size it to your dataset's worst-case count; rays that exceed it lose their tail segments.
Reading the Output
import numpy as np
import zarr
z = zarr.open("coherence.zr", mode="r")
mean_map_cm = z["projections/coherencelength"][:] # (npy, npx)
seg_grp = z["projections/coherencelength_segments"]
segments_cm = seg_grp["segments"][:]
offsets = seg_grp["offsets"][:]
counts = seg_grp["counts"][:]
# Per-pixel median (vs. the per-pixel mean THOR writes directly):
npy, npx = mean_map_cm.shape
flat = np.full(npy * npx, np.nan)
for i, (off, c) in enumerate(zip(offsets, counts)):
if c:
flat[i] = np.median(segments_cm[off:off + c])
median_map = flat.reshape(npy, npx)
For pooled statistics across all sight lines (histograms, percentiles), use the flat segments array.
See Also
- Ray Tracer Driver — camera modes, view types, shared ray-tracer parameters.
- Datasets Reference —
pointcloud_voronoiconfiguration.
