Skip to main content
Yanked

This release has been yanked by its maintainers, and will be ignored by installers, except when explicitly specified.
Consider using release 0.3.0a5 instead.

LightHit

LightHit computes the expected optical-module response to light propagating in a homogeneous scattering medium. It solves the time-dependent radiative transfer equation (RTE), including unscattered, once-scattered and multiply-scattered light, and returns expected photoelectrons per module and per time bin.

The medium, detector and source are ordinary Python objects. There is no global initialisation state, so several configurations can safely coexist in one program.

LightHit is currently an alpha release. Validate numerical settings for your medium and geometry before using its results in an analysis.

Installation

python -m pip install lighthit

Numba acceleration is strongly recommended for tracks, showers and large detector arrays:

python -m pip install 'lighthit[accelerate]'

For the installed synthetic demonstration, viewer and wheel self-tests:

python -m pip install 'lighthit[demo,test]'
lighthit-selftest

Python 3.11 or newer is required.

Quick start

The example below defines a spectral medium, two optical modules and a monochromatic isotropic flash.

import numpy as np
import lighthit as lh

# Optical properties tabulated versus wavelength.
medium = lh.SpectralMedium(
    wavelength_nm=[400.0, 450.0, 500.0],
    absorption_per_m=[0.030, 0.020, 0.040],
    scattering_per_m=[0.030, 0.022, 0.017],
    phase_index=[1.344, 1.339, 1.336],
    group_index=[1.386, 1.374, 1.367],
    g=0.9,
)

# Response functions receive NumPy arrays and return arrays of the same shape.
def angular_acceptance(head_on_cosine):
    return np.ones_like(np.asarray(head_on_cosine, dtype=float))

def spectral_efficiency(wavelength_nm):
    return np.full_like(np.asarray(wavelength_nm, dtype=float), 0.20)

detector = lh.DetectorArray(
    positions_m=[[20.0, 0.0, 0.0], [35.0, 0.0, 0.0]],
    orientations=[[-1.0, 0.0, 0.0], [-1.0, 0.0, 0.0]],
    effective_area_m2=0.05,
    angular_acceptance=angular_acceptance,
    spectral_efficiency=spectral_efficiency,
)

source = lh.IsotropicFlash.monochromatic(
    position_m=[0.0, 0.0, 0.0],
    photons=1.0e8,
    wavelength_nm=450.0,
)

# build() returns a reusable kernel and prepares only the tables this source
# needs. The same kernel can then transport more compatible events.
kernel = lh.build(medium, detector, source)
response = kernel.transport(source)

print(response.charge_pe)   # integrated expected photoelectrons, one per OM
print(response.bins_pe)     # expected photoelectrons, shape (OM, time bin)

The first build may take noticeably longer than later event calculations. Set KernelConfig(cache_directory=...) to reuse transport tables across processes.

Sources

The high-level dispatcher accepts:

flash = lh.IsotropicFlash.monochromatic(
    position_m=[0, 0, 0], photons=1e8, wavelength_nm=450
)

track = lh.CherenkovTrack(
    start_m=[0, 0, -20],
    direction=[0.2, 0.1, 0.97],
    length_m=40,
    beta=1.0,
)

shower = lh.G4Shower.from_hdf5("event.h5", event=0)

synthetic_shower = lh.SyntheticShower.gaussian(
    lh.SourcePose(
        position_m=[25.0, -10.0, -720.0],
        direction=[0.4, 0.2, -0.89442719],
        time_ns=0.0,
    ),
    charged_track_length_m=300.0,
)

For tracks and showers, kernel.transport(source) automatically selects the source engine and uses the detector's angular response. Research methods are never selected unless explicitly enabled.

Configuring the calculation

Numerical settings are collected in KernelConfig:

config = lh.KernelConfig(
    wavelength_nodes=9,
    threshold_pe=0.01,
    cache_directory="lighthit-cache",
)

kernel = lh.build(medium, detector, track, config=config)
response = kernel.transport(track)

For production jobs, prepare the cache explicitly and forbid event processing from starting an expensive build unexpectedly:

build_config = lh.KernelConfig(cache_directory="lighthit-cache")
kernel = lh.TransportKernel(medium, detector, build_config)
kernel.build(source=track, progress="console")
print(kernel.last_build_report.as_dict())

run_config = lh.KernelConfig(
    cache_directory="lighthit-cache",
    cache_policy="require",
)
kernel = lh.TransportKernel(medium, detector, run_config)
response = kernel.transport(track)

Repeated build calls reuse compatible tables and report that there is nothing to do. Pass force=True only for an intentional rebuild. Detector coordinates can be inspected without detector-specific assumptions:

geometry = lh.describe_geometry(detector)
print(geometry.centroid_m)
print(geometry.bounds_min_m, geometry.bounds_max_m)

Important inputs are explicit:

  • SpectralMedium contains absorption and scattering coefficients in m⁻¹, phase and group refractive indices, and the Henyey–Greenstein parameter g.
  • DetectorArray contains OM positions, orientations, effective areas, angular acceptance and wavelength-dependent detection efficiency.
  • A detector orientation points from the OM toward a head-on source; angular_acceptance(+1) is the head-on response.
  • spectral_efficiency should include all wavelength-dependent detection factors required by the application, such as quantum efficiency and optical transmission.

Reading the response

kernel.transport(...) returns a TransportResponse.

Attribute Meaning
charge_pe Integrated expected photoelectrons, shape (OM,)
bins_pe Expected photoelectrons per relative time bin, shape (OM, bin)
charge_components_pe Integrated contributions [ballistic, one, two-or-more]
components_pe The same three components per time bin
active OMs for which the full time spectrum was evaluated
relative_time_edges_ns Bin edges relative to each OM's time origin
time_origin_ns One absolute time origin per OM
metadata Method, backend and numerical diagnostics

Absolute bin edges for every module are

absolute_edges_ns = (
    response.time_origin_ns[:, None]
    + response.relative_time_edges_ns[None, :]
)

The integrated charge is evaluated independently at zero frequency. A finite time window and a finite frequency grid mean that bins_pe.sum(axis=1) need not equal charge_pe exactly. Signed ringing is reported rather than silently clipped or renormalised.

Responses can be stored as compressed NumPy files:

response.save("response.npz")

Interactive viewer

Install the optional viewer dependency:

python -m pip install 'lighthit[viewer]'

Turn a response into a standalone interactive HTML file:

payload = lh.viewer_payload(
    response,
    source=source,
    event_id="flash-450nm",
    label="450 nm calibration flash",
)

viewer_path = lh.write_event_viewer(payload, "lighthit-viewer.html")
print(viewer_path)

Compatible event payloads can share one viewer:

combined = lh.merge_event_viewers(laser_payload, track_payload, shower_payload)
lh.write_event_viewer(combined, "multi-event-viewer.html")

Open lighthit-viewer.html in a web browser. It contains the detector geometry, integrated charge by scattering order, a selectable per-OM time histogram and a time animation. A portable lighthit-viewer.json companion is written beside the HTML file.

Installed synthetic demonstration

The wheel includes one public-data-only Baikal-like example. Its geometry and OM response are illustrative and are not a Baikal calibration:

lighthit-demo geometry
lighthit-demo cache --profile quick --cache ./demo-cache --sources all
lighthit-demo run --profile quick --cache ./demo-cache \
  --output ./demo-output --sources all

The repository, but not the wheel, contains detector-specific laser, track and G4-shower examples with explicit source positions and directions.

Scope

The current model assumes a homogeneous, unbounded medium with elastic Henyey–Greenstein scattering. It does not include boundaries, layered media, structural shadowing, polarisation, detector electronics, trigger or noise.

License

LightHit is distributed under the BSD 3-Clause License.

Metadata

Release files for lighthit 0.2.0a7

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lighthit 0.2.0a7
File Size Uploaded Provenance Actions
lighthit-0.2.0a7.tar.gz 123.2 kB Without provenance Details for lighthit-0.2.0a7.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for lighthit 0.2.0a7
File Interpreter ABI Platform Actions
lighthit-0.2.0a7-py3-none-any.whl Python 3 none any Details for lighthit-0.2.0a7-py3-none-any.whl

Total release size: 256.7 kB

Release files / lighthit-0.2.0a7.tar.gz

Download URL lighthit-0.2.0a7.tar.gz
Size 123.2 kB
Tags Source
SHA-256 checksum
How to use checksums
8cdef936a19d94344e7f3c6eaf0a404297ee7efa4bbba8f1e47f7552fcd51567
BLAKE2b-256 checksum
How to use checksums
8a5b8c610a3e366637a126dec2a4c1685027877aca9cfc572234515051e4c3ac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.5

Release files / lighthit-0.2.0a7-py3-none-any.whl

Download URL lighthit-0.2.0a7-py3-none-any.whl
Size 133.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a7e9e0f2bdf5a39ac60645ad04d061530deb2d4fc081a7034a35009f9a518eb8
BLAKE2b-256 checksum
How to use checksums
b130946fedf1f390078ccaed49ebfe15038fbf9ddd192b729a83e89042c145e3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.5

Security for lighthit 0.2.0a7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page