Skip to content

Results

RunResult is the labelled result container: monitor names map to xarray objects carrying real coordinates.

Result container

RunResult

RunResult(
    path: Union[str, Path],
    *,
    simulation=None,
    client_state: bool = True,
)

Lazy, dict-like view of one solver output directory.

data["probe"] returns an xarray.DataArray:

  • time series: dims ('t', 'component'), t in seconds;
  • snapshots: dims ('t', 'component', 'z', 'y', 'x'), spatial coordinates in microns (Yee-node base coordinates, i * dl_um).

A result reloaded from disk reads like the one the run returned: the runners write the client state the wire document does not carry (the user-frame origin of a fitted domain, the half a symmetry plane dropped, declared ports and wavelengths) beside sim.json, and simulation restores it, so coordinates, planes made whole across a symmetry plane, transmission and reflection agree with the live result. client_state=False reads the result as the wire document alone describes it: coordinates in the corner frame, planes as recorded, no declared ports.

simulation= is for the simulation that ran. It is compared with the wire document saved with the result (sim.json or the copy in an HDF5 bundle), its run block aside since a reading does not use it and its launch amplitudes to 0.1 %, since a rebuilt launch can move that far. With no document saved, the spec a cloud client stored for the job takes its place when that spec is the input the manifest recorded. With neither, as on a machine without that cache, its grid is compared with the grid the manifest recorded. A difference raises SimulationMismatchError, which names it. A script rebuilt on a newer SDK can land here, since the same text can build a different mesh: RunResult(path) reads such a result correctly from what was saved.

manifest_path instance-attribute

manifest_path: Optional[Path] = None

manifest_sha256 instance-attribute

manifest_sha256: Optional[str] = None

manifest instance-attribute

manifest: dict = self._open(path)

dt_s property

dt_s: float

Recorded time step in seconds.

Read the run metadata first, then the first monitor entry with a time step. Raise ValueError when neither records dt_s.

provenance property

provenance: dict

Return a shallow copy of the recorded provenance, or an empty dict.

Available fields depend on the solver and manifest version. This property reports stored metadata; it does not verify a build or device.

run property

run: dict

The manifest's run block (n_steps, dt_s, wall_seconds, mcells_per_s, aborted, abort_reason).

aborted property

aborted: bool

True when the solver aborted this run.

abort_reason property

abort_reason: Optional[str]

The abort reason string (divergence / non_finite_energy), or None for a healthy run.

shut_off property

shut_off: bool

True when the run ended early via auto-shutoff after the energy rule (field energy below run.shutoff of peak, or the fp16 plateau rule) and any DFT guard passed: a clean finish, not an abort.

stop_reason property

stop_reason: Optional[str]

Stop rule: dft_decay_estimate, energy_decay, fp16_plateau, step_cap or aborted; None for older runs.

dft_shutoff property

dft_shutoff: Optional[float]

Effective DFT decay estimate threshold; None in older manifests.

steps_run property

steps_run: Optional[int]

Steps actually taken (<= run.n_steps; fewer when auto-shutoff or an abort ended the run early). None for older manifests without the key.

monitor_names property

monitor_names: List[str]

Monitor names in manifest order, as a new list.

simulation property

simulation

The Simulation this result came from: passed by the runner, or loaded from the sim.json the runners write beside the outputs (or the copy inside an HDF5 bundle). A loaded one gets back the client state its run recorded beside it (client.json): the user-frame origin, the symmetry-plane record, and the declared ports and wavelengths, whose modes are solved again on first use. A record that belongs to another simulation warns and restores nothing, and a simulation restored with a user frame, a symmetry record, wavelengths or declared ports refuses with_changes (the fields as given are not recorded); one whose record holds only far-field surfaces stays editable, keeping their recipe only through edits of nothing it was checked against. None when no simulation is available (a bare output directory).

norm_amplitude property

norm_amplitude: Optional[float]

A0, the amplitude of the first wire-order source: the engine's normalization amplitude, which this reader multiplies back into every frequency-domain array (A0 on field_dft phasors, A0^2 on flux). None when the simulation is not known, in which case the arrays keep the engine's unit-amplitude normalization (their normalization attr says so).

port_names property

port_names: List[str]

The ports of a declarative simulation, in declaration order; a port the symmetry reduction dropped is listed after them and read through its image.

keys

keys() -> List[str]

Monitor names in manifest order, as a new list.

wire

wire(name: str) -> DataArray

The recorded array in the wire's corner frame, exactly the region the engine wrote (the simulated half of a folded domain): what the analysis readers that place a monitor window on a plane work on. A flux here is the modeled part's power, without the whole-device factor data[name] carries.

transmission

transmission(port: str, **kwargs)

Modal power transmission into port from the driven port of a simulation built with ports= and source=: an xarray.DataArray over f with a wlen_um coordinate (see photonhub.analysis.transmission_spectrum, whose keywords pass through). The power the device sends back into the driven port is reflection.

Limitation: a beam-driven simulation has no transmission. With source= a GaussianBeam there is no driven port whose plane reads the launched power in the launch direction, and the beam's power_watts is not a per-frequency reference: the beam's source amplitudes are fixed at the band centre, so the power it launches drifts across the band, and a flux plane at the beam would also count whatever the device reflects back through it. This method therefore raises for a beam-driven result. Read the absolute modal power of a port instead, data.simulation.port_monitors[name] .mode_power(data) (flux-commensurate with a PowerMonitor; on a folded domain both, and the beam's power_watts, are the whole device's power), and normalize it against a reference run of your own, for example the same beam launched into the bare background with a PowerMonitor across its path.

reflection

reflection(port: Optional[str] = None, **kwargs)

Modal power reflection at the driven port of a simulation built with ports= and source=: the driven port's monitor read against the launch direction (the power the device sends back toward the source) over the same monitor read in the launch direction (the launched power), as an xarray.DataArray over f with a wlen_um coordinate (see photonhub.analysis.reflection_spectrum, whose keywords pass through). The two readings share one plane and one mode, so R + T = 1 across the band is the energy check for a lossless device.

port defaults to the driven port, and naming any other port raises ValueError, a port mirrored by a symmetry plane included. Every port that is not driven reads outward, the power leaving the device, so its monitor read the other way measures the wave arriving from its own boundary (nothing, with no source there), not the power reflected into it. To read the reflection at another port, drive that port (source=) and run again. Like transmission it has no launched power to divide by in a beam-driven simulation, and raises (see the limitation there).

plot_field

plot_field(
    monitor,
    field="Ex",
    x=None,
    y=None,
    z=None,
    *,
    freq=None,
    val="real",
    structures=True,
    simulation=None,
    ax=None,
    cmap=None,
    unfold=True,
    **kw,
)

Heatmap of a field component on a 2D slice of self[monitor].

Thin delegation: the rendering lives in photonhub.viz.plot_field (imported lazily so matplotlib loads only when a plot is requested). field is Ex..Hz or a derived 'E'/'intensity'/'H'; freq= is required for a multi-frequency DFT monitor; val selects real/imag/abs/phase for complex data. unfold (the default) mirrors a symmetry-reduced half domain back into the whole device, with each component's parity about the plane; it needs simulation=. Returns a matplotlib Axes.

preview

preview(monitor, *, simulation=None)

Interactive Jupyter scrubber over a recorded field monitor: component, real/imag/abs/phase, frequency, and (volumetric monitors) the cut plane, with optional structure outlines (simulation=). Requires the photonhub[viz] extra and a notebook. See photonhub.viz.interactive_field.

SimulationMismatchError

Bases: ValueError

The simulation given to RunResult is not the one the result ran. Reading with it would put its grid, ports and amplitudes on another run's fields and give wrong numbers with no sign of it, so the reader refuses and names the difference. RunResult(path) reads with the saved simulation. The result itself is fine: the cloud client keeps its cached copy when this is raised.

Export

convert_to_hdf5

convert_to_hdf5(
    src: Union[str, Path],
    dest: Union[str, Path, None] = None,
) -> Path

Pack the raw-output directory src (or its manifest.json) into a single HDF5 file. dest defaults to <src>/simulation.h5. Returns the written path. Requires h5py.