Source code for lsurf.geometry.geometry

# The Clear BSD License
#
# Copyright (c) 2026 Tobias Heibges
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted (subject to the limitations in the disclaimer
# below) provided that the following conditions are met:
#
#      * Redistributions of source code must retain the above copyright notice,
#      this list of conditions and the following disclaimer.
#
#      * Redistributions in binary form must reproduce the above copyright
#      notice, this list of conditions and the following disclaimer in the
#      documentation and/or other materials provided with the distribution.
#
#      * Neither the name of the copyright holder nor the names of its
#      contributors may be used to endorse or promote products derived from this
#      software without specific prior written permission.
#
# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

"""
Immutable Geometry Container

Holds the result of a GeometryBuilder.build() call.
Provides convenient accessors for surfaces, detectors, and materials.
"""

from dataclasses import dataclass

from ..surfaces import Surface
from ..materials import MaterialField


[docs] @dataclass(frozen=True) class Geometry: """ Immutable container for simulation geometry. Created by GeometryBuilder.build(). Provides access to surfaces, detectors, and named media by name or index. Parameters ---------- surfaces : tuple of Surface All optical/absorber surfaces in the geometry. detectors : tuple of Surface All detector surfaces in the geometry. background_material : MaterialField The background/ambient material (from set_background() medium). media : dict Mapping from medium name to MaterialField. surface_names : dict Mapping from surface name to index in surfaces tuple. detector_names : dict Mapping from detector name to index in detectors tuple. Examples -------- >>> geometry = builder.build() >>> ocean = geometry.get_surface("ocean") >>> detector = geometry.get_detector("detector_35km") >>> atmosphere_material = geometry.get_medium("atmosphere") """ surfaces: tuple[Surface, ...] detectors: tuple[Surface, ...] background_material: MaterialField media: dict[str, MaterialField] surface_names: dict[str, int] detector_names: dict[str, int]
[docs] def get_medium(self, name: str) -> MaterialField: """ Get the material for a named medium. Parameters ---------- name : str The name of the medium. Returns ------- MaterialField The material for the medium. Raises ------ KeyError If no medium with the given name exists. """ if name not in self.media: available = ", ".join(sorted(self.media.keys())) raise KeyError(f"No medium named '{name}'. Available: {available}") return self.media[name]
[docs] def get_surface(self, name: str) -> Surface: """ Get a surface by name. Parameters ---------- name : str The name of the surface. Returns ------- Surface The surface with the given name. Raises ------ KeyError If no surface with the given name exists. """ if name not in self.surface_names: available = ", ".join(sorted(self.surface_names.keys())) raise KeyError(f"No surface named '{name}'. Available: {available}") return self.surfaces[self.surface_names[name]]
[docs] def get_surface_index(self, name: str) -> int: """ Get the index of a surface by name. Parameters ---------- name : str The name of the surface. Returns ------- int The index of the surface in the surfaces tuple. Raises ------ KeyError If no surface with the given name exists. """ if name not in self.surface_names: available = ", ".join(sorted(self.surface_names.keys())) raise KeyError(f"No surface named '{name}'. Available: {available}") return self.surface_names[name]
[docs] def get_detector(self, name: str) -> Surface: """ Get a detector by name. Parameters ---------- name : str The name of the detector. Returns ------- Surface The detector with the given name. Raises ------ KeyError If no detector with the given name exists. """ if name not in self.detector_names: available = ", ".join(sorted(self.detector_names.keys())) raise KeyError(f"No detector named '{name}'. Available: {available}") return self.detectors[self.detector_names[name]]
[docs] def get_detector_index(self, name: str) -> int: """ Get the index of a detector by name. Parameters ---------- name : str The name of the detector. Returns ------- int The index of the detector in the detectors tuple. Raises ------ KeyError If no detector with the given name exists. """ if name not in self.detector_names: available = ", ".join(sorted(self.detector_names.keys())) raise KeyError(f"No detector named '{name}'. Available: {available}") return self.detector_names[name]
[docs] def to_surface_list(self) -> list[Surface]: """ Convert all surfaces and detectors to a list for SurfacePropagator. Returns ------- list of Surface All surfaces and detectors as a mutable list. """ return list(self.surfaces) + list(self.detectors)
[docs] def __len__(self) -> int: """Return the total number of surfaces and detectors.""" return len(self.surfaces) + len(self.detectors)
[docs] def __iter__(self): """Iterate over all surfaces and detectors.""" return iter(self.surfaces + self.detectors)