7.30 Example — MultiCore
PDF section 7.30.
Note
The standalone Examp-MultiCore.py script from the 2021 PDF is no
longer in KrakenOS/Examples/. The current example that exercises
multi-source parallel illumination is
KrakenOS/Examples/Examp_Multi_Source_Illumination.py — it is not a
direct port, but it covers the same use case of distributing many ray
bundles across independent workers.
The original example demonstrates dispatching a large ray-tracing run
across multiple CPU cores via Python’s multiprocessing, splitting the
ray fan into per-worker chunks and merging the resulting raykeeper
contents.
Figure 37. MultiCore parallel ray trace.
"""Trace two independent physical illumination sources through one scene.
This example mirrors the UI preset:
Layouts -> Sources / Illumination -> Multi-Source Illumination Example
The legacy Object row is reference geometry only. Two layout-defined
``SceneSource3D`` records launch from different Y positions and converge toward
the same detector plane. This is the first non-UI-panel multi-source contract:
layouts can declare ``SETTINGS["scene_sources"]`` and the trace/render pipeline
will preserve per-source IDs on every ray.
"""
from __future__ import annotations
import numpy as np
import KrakenOS as Kos
from KrakenOS.common_optical_layouts.multi_source_illumination_example import SETTINGS, SURFACES, TITLE
from KrakenOS.UI.layout_editor import _build_system_from_specs
from KrakenOS.UI.render_layout_snapshot import _rows_from_layout_info, _snapshot_editor
WAVELENGTH_UM = float(SETTINGS["wavelength"])
def trace_demo() -> tuple[Kos.system, Kos.raykeeper, object]:
rows = _rows_from_layout_info({"surfaces": SURFACES, "settings": SETTINGS})
editor = _snapshot_editor(rows, SETTINGS)
system = _build_system_from_specs(SURFACES)
rays = Kos.raykeeper(system)
max_radius = max((max(row.diameter / 2.0, 0.5) for row in rows), default=1.0)
editor._trace_preview_rays(system, rays, WAVELENGTH_UM, max_radius, allow_full_pupil=False)
return system, rays, editor
def _value(rays, name: str, index: int, default=""):
values = getattr(rays, name, None)
if values is None or index >= len(values):
return default
arr = np.asarray(values[index]).reshape(-1)
if arr.size == 0:
return default
return arr[-1]
def main() -> int:
system, rays, editor = trace_demo()
sources = editor._collect_scene_sources(wavelength=WAVELENGTH_UM)
image_index = len(system.SDT) - 1
print(TITLE)
print("source | model | origin mm | direction | rays | power")
print("--- | --- | --- | --- | --- | ---")
for source in sources:
origin = tuple(float(value) for value in np.asarray(source.origin, dtype=float).reshape(-1)[:3])
direction = tuple(float(value) for value in np.asarray(source.direction, dtype=float).reshape(-1)[:3])
print(
f"{source.source_id} | {source.model} | {origin} | {direction} | "
f"{source.ray_count} | {source.power}"
)
print()
print("ray | source | surfaces | reaches detector | power/ray")
print("--- | --- | --- | --- | ---")
for ray_index, surfaces in enumerate(getattr(rays, "SURFACE", [])):
surface_ids = [int(value) for value in np.asarray(surfaces, dtype=int).ravel()]
source_id = str(_value(rays, "SOURCE_ID", ray_index, ""))
source_weight = float(_value(rays, "SOURCE_WEIGHT", ray_index, 0.0) or 0.0)
reaches_detector = bool(surface_ids and surface_ids[-1] == image_index)
print(f"{ray_index} | {source_id} | {surface_ids} | {reaches_detector} | {source_weight:.6g}")
return 0
if __name__ == "__main__":
raise SystemExit(main())