Source code for optiland.nonsequential.results.ray_database
"""Ray database result for Non-Sequential Raytracing.
Kramer Harrison, 2026
"""
from __future__ import annotations
from typing import TYPE_CHECKING
import numpy as np
if TYPE_CHECKING:
from pathlib import Path
[docs]
class RayDatabase:
"""Phase-space record of individual rays at a detector surface.
Attributes:
x: Ray positions x [mm].
y: Ray positions y [mm].
z: Ray positions z [mm].
L: Ray direction cosine x (unit vector).
M: Ray direction cosine y.
N: Ray direction cosine z.
flux: Per-ray flux [W].
wavelength: Per-ray wavelength [µm].
"""
def __init__(
self,
x: np.ndarray,
y: np.ndarray,
z: np.ndarray,
L: np.ndarray,
M: np.ndarray,
N: np.ndarray,
flux: np.ndarray,
wavelength: np.ndarray,
) -> None:
"""Initialize RayDatabase.
Args:
x: Positions x [mm].
y: Positions y [mm].
z: Positions z [mm].
L: Direction cosine x.
M: Direction cosine y.
N: Direction cosine z.
flux: Per-ray flux [W].
wavelength: Per-ray wavelength [µm].
"""
self.x = x
self.y = y
self.z = z
self.L = L
self.M = M
self.N = N
self.flux = flux
self.wavelength = wavelength
@property
def num_rays(self) -> int:
"""Number of stored rays."""
return len(self.x)
@property
def total_flux(self) -> float:
"""Total flux recorded at this detector [W].
Exposed so a ray-database detector contributes to the tracer's flux
ledger like every other detector; without it the detected flux is
undercounted and ``flux_conservation_error`` misreports.
"""
return float(np.sum(self.flux))
[docs]
def to_dataframe(self):
"""Return ray data as a pandas DataFrame.
Returns:
DataFrame with columns x, y, z, L, M, N, flux, wavelength.
Raises:
ImportError: If pandas is not installed.
"""
import pandas as pd # noqa: PLC0415
return pd.DataFrame(
{
"x": self.x,
"y": self.y,
"z": self.z,
"L": self.L,
"M": self.M,
"N": self.N,
"flux": self.flux,
"wavelength": self.wavelength,
}
)
[docs]
def save(self, path: str | Path) -> None:
"""Save ray database to a .npz file.
Args:
path: Output file path.
"""
np.savez(
path,
x=self.x,
y=self.y,
z=self.z,
L=self.L,
M=self.M,
N=self.N,
flux=self.flux,
wavelength=self.wavelength,
)