Skip to content

Commit

Permalink
add store api to manipulate properties
Browse files Browse the repository at this point in the history
  • Loading branch information
ManuelHu committed Sep 26, 2024
1 parent 95c15b5 commit 19cadb8
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/legendoptics/copper.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
import pint
from pint import Quantity

from legendoptics import store
from legendoptics.utils import readdatafile

log = logging.getLogger(__name__)
u = pint.get_application_registry()


@store.register_pluggable
def copper_reflectivity() -> tuple[Quantity, Quantity]:
"""Reflectivity of copper surfaces.
Expand Down
9 changes: 9 additions & 0 deletions src/legendoptics/fibers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
import pint
from pint import Quantity

from legendoptics import store
from legendoptics.utils import InterpolatingGraph, readdatafile

log = logging.getLogger(__name__)
u = pint.get_application_registry()


@store.register_pluggable
def fiber_cladding2_refractive_index() -> float:
"""Refractive index of second fiber cladding material [SaintGobainDataSheet]_.
Expand All @@ -26,6 +28,7 @@ def fiber_cladding2_refractive_index() -> float:
return 1.42


@store.register_pluggable
def fiber_cladding1_refractive_index() -> float:
"""Refractive index of first fiber cladding material [SaintGobainDataSheet]_.
Expand All @@ -34,6 +37,7 @@ def fiber_cladding1_refractive_index() -> float:
return 1.49


@store.register_pluggable
def fiber_core_refractive_index() -> float:
"""Refractive index of fiber core material [SaintGobainDataSheet]_.
Expand All @@ -42,6 +46,7 @@ def fiber_core_refractive_index() -> float:
return 1.6


@store.register_pluggable
def fiber_wls_absorption(
abs_at_400nm: Quantity = 0.7 * u.mm,
) -> tuple[Quantity, Quantity]:
Expand Down Expand Up @@ -76,6 +81,7 @@ def fiber_wls_absorption(
return wvl, absorp


@store.register_pluggable
def fiber_wls_emission() -> tuple[Quantity, Quantity]:
"""[SaintGobainDataSheet]_ reports the emission spectrum for BCF-91A.
Expand All @@ -84,6 +90,7 @@ def fiber_wls_emission() -> tuple[Quantity, Quantity]:
return readdatafile("psfibers_wlscomponent.dat")


@store.register_pluggable
def fiber_wls_timeconstant() -> Quantity:
"""WLS time constant [SaintGobainDataSheet]_.
Expand All @@ -92,6 +99,7 @@ def fiber_wls_timeconstant() -> Quantity:
return 12 * u.ns


@store.register_pluggable
def fiber_absorption_length() -> Quantity:
"""Absorption length of fiber [SaintGobainDataSheet]_. Note this is a macroscopical value for a 1 mm fiber.
Expand All @@ -105,6 +113,7 @@ def fiber_absorption_length() -> Quantity:
return 3.5 * u.m


@store.register_pluggable
def fiber_absorption_path_length() -> Quantity:
"""Absorption length of fiber [SaintGobainDataSheet]_, corrected for the geometry of a 1 mm square fiber.
Expand Down
2 changes: 2 additions & 0 deletions src/legendoptics/germanium.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
import pint
from pint import Quantity

from legendoptics import store
from legendoptics.utils import readdatafile

log = logging.getLogger(__name__)
u = pint.get_application_registry()


@store.register_pluggable
def germanium_reflectivity() -> tuple[Quantity, Quantity]:
"""Reflectivity of germanium surfaces.
Expand Down
12 changes: 12 additions & 0 deletions src/legendoptics/lar.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import pint
from pint import Quantity

from legendoptics import store
from legendoptics.scintillate import ScintConfig, ScintParticle
from legendoptics.utils import (
InterpolatingGraph,
Expand Down Expand Up @@ -113,6 +114,7 @@ def lar_dielectric_constant_cern2020(
return (3 + 2 * x) / (3 - x)


@store.register_pluggable
def lar_dielectric_constant(
λ: Quantity, method: ArDielectricMethods = "cern2020"
) -> Quantity:
Expand All @@ -130,6 +132,7 @@ def lar_dielectric_constant(
raise ValueError(msg)


@store.register_pluggable
def lar_refractive_index(
λ: Quantity, method: ArDielectricMethods = "cern2020"
) -> Quantity:
Expand All @@ -145,6 +148,7 @@ def lar_refractive_index(
return np.sqrt(lar_dielectric_constant(λ, method))


@store.register_pluggable
def lar_emission_spectrum(λ: Quantity) -> Quantity:
"""Return the LAr emission spectrum, adapted from [Heindl2010]_.
Expand All @@ -160,6 +164,7 @@ def lar_emission_spectrum(λ: Quantity) -> Quantity:
)(λ)


@store.register_pluggable
def lar_fano_factor() -> float:
"""Fano factor.
Expand All @@ -171,6 +176,7 @@ def lar_fano_factor() -> float:
return 0.11


@store.register_pluggable
def lar_rayleigh(
λ: Quantity,
temperature: Quantity = 90 * u.K,
Expand Down Expand Up @@ -211,6 +217,7 @@ def lar_rayleigh(
return (1 / inv_l).to("cm") # simplify units


@store.register_pluggable
def lar_abs_length(λ: Quantity) -> Quantity:
"""Absorption length (not correctly scaled).
Expand All @@ -227,6 +234,7 @@ def lar_abs_length(λ: Quantity) -> Quantity:
return np.minimum(absl, 100000 * u.cm) # avoid large numbers


@store.register_pluggable
def lar_peak_attenuation_length(
attenuation_method: ArLifetimeMethods | Quantity = "legend200-llama",
) -> Quantity:
Expand All @@ -244,6 +252,7 @@ def lar_peak_attenuation_length(
return attenuation_method


@store.register_pluggable
def lar_lifetimes(
triplet_lifetime_method: float | ArLifetimeMethods = "legend200-llama",
) -> ArScintLiftime:
Expand All @@ -265,6 +274,7 @@ def lar_lifetimes(
return ArScintLiftime(singlet=5.95 * u.ns, triplet=triplet)


@store.register_pluggable
def lar_scintillation_params(
flat_top_yield: Quantity = 31250 / u.MeV,
) -> ScintConfig:
Expand Down Expand Up @@ -308,6 +318,7 @@ def lar_scintillation_params(
)


@store.register_pluggable
def pyg4_lar_attach_rindex(
lar_mat, reg, lar_dielectric_method: ArDielectricMethods = "cern2020"
) -> None:
Expand All @@ -332,6 +343,7 @@ def pyg4_lar_attach_rindex(
lar_mat.addVecPropertyPint("RINDEX", λ_full.to("eV"), rindex)


@store.register_pluggable
def pyg4_lar_attach_attenuation(
lar_mat,
reg,
Expand Down
3 changes: 3 additions & 0 deletions src/legendoptics/nylon.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
import pint
from pint import Quantity

from legendoptics import store
from legendoptics.utils import readdatafile

log = logging.getLogger(__name__)
u = pint.get_application_registry()


@store.register_pluggable
def nylon_refractive_index() -> float:
"""Refractive index in near-UV range, from [Benziger2007]_.
Expand All @@ -31,6 +33,7 @@ def nylon_refractive_index() -> float:
return 1.53


@store.register_pluggable
def nylon_absorption() -> tuple[Quantity, Quantity]:
"""Values reported in [Agostini2018]_.
Expand Down
9 changes: 9 additions & 0 deletions src/legendoptics/pen.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import pint
from pint import Quantity

from legendoptics import store
from legendoptics.scintillate import ScintConfig, ScintParticle
from legendoptics.utils import (
InterpolatingGraph,
Expand All @@ -30,6 +31,7 @@
u = pint.get_application_registry()


@store.register_pluggable
def pen_quantum_efficiency() -> float:
"""Quantum efficiency, from [Araujo2022]_ at LAr temperature.
Expand All @@ -38,6 +40,7 @@ def pen_quantum_efficiency() -> float:
return 0.69


@store.register_pluggable
def pen_refractive_index() -> float:
"""Refractive index from [Hong2017]_.
Expand All @@ -46,6 +49,7 @@ def pen_refractive_index() -> float:
return 1.51


@store.register_pluggable
def pen_scint_timeconstant() -> Quantity:
"""Time constant, from [Manzanillas2022]_.
Expand All @@ -54,6 +58,7 @@ def pen_scint_timeconstant() -> Quantity:
return 25.3 * u.ns


@store.register_pluggable
def pen_scint_light_yield() -> Quantity:
"""PEN scintillation yield for electrons, from [Manzanillas2022]_.
Expand All @@ -62,6 +67,7 @@ def pen_scint_light_yield() -> Quantity:
return 5440 / u.MeV


@store.register_pluggable
def pen_wls_emission() -> tuple[Quantity, Quantity]:
"""WLS Emission spectrum, from [Mary1997]_.
Expand All @@ -70,6 +76,7 @@ def pen_wls_emission() -> tuple[Quantity, Quantity]:
return readdatafile("pen_wlscomponent.dat")


@store.register_pluggable
def pen_absorption() -> tuple[Quantity, Quantity]:
"""Bulk absorption reported in [Manzanillas2022]_.
Expand All @@ -80,6 +87,7 @@ def pen_absorption() -> tuple[Quantity, Quantity]:
return wvl, absorp


@store.register_pluggable
def pen_wls_absorption() -> tuple[Quantity, Quantity]:
"""WLS absorption of PEN.
Expand All @@ -99,6 +107,7 @@ def pen_wls_absorption() -> tuple[Quantity, Quantity]:
return wvl, absorp


@store.register_pluggable
def pen_scintillation_params() -> ScintConfig:
"""Get a :class:`ScintConfig` object for PEN.
Expand Down
2 changes: 2 additions & 0 deletions src/legendoptics/silicon.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
import pint
from pint import Quantity

from legendoptics import store
from legendoptics.utils import readdatafile

log = logging.getLogger(__name__)
u = pint.get_application_registry()


@store.register_pluggable
def silicon_complex_rindex() -> tuple[Quantity, Quantity, Quantity]:
"""Real and imaginary parts as tuple(wavelength, Re, Im). Measurements from [Phillip1960]_.
Expand Down
78 changes: 78 additions & 0 deletions src/legendoptics/store.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""
A store that allows to manipulate and swap individual material properties with custom
implementations.
:meth:`register_pluggable` is a decorator to use above all functions defining material
properties.
Users can then replace the original implementation by their own implementations using
``replace_implementation(new_impl: Callable)``, and also switch back to the original
implementation using ``reset_implementation()`` on the decorated function object. The
original implementation is always available as ``original_impl()``.
Apart from the decorator, this store provides functions to get and reset the status of
all registered pluggable functions.
"""

from __future__ import annotations

from functools import wraps
from types import MethodType
from typing import Callable

_optical_property_store = []


def register_pluggable(fn: Callable) -> Callable:
"""Decorator that registers this function as a pluggable property function."""

# create the new wrapper object.
@wraps(fn)
def wrap(*args, **kwargs):
return wrap._impl(*args, **kwargs)

wrap._impl = fn
wrap._orig_impl = fn

# add "instance methods" of the new wrapper.
def reset_implementation(self) -> None:
"""Reset to the original function implementation."""
self._impl = self._orig_impl

def replace_implementation(self, new_impl: Callable) -> None:
"""Replace the underlying function implementation."""
self._impl = new_impl

def is_original(self) -> bool:
"""Is the underlying function the original implementation"""
return self._impl == self._orig_impl

def original_impl(self) -> Callable:
"""The original function implementation."""
return self._orig_impl

wrap.reset_implementation = MethodType(reset_implementation, wrap)
wrap.replace_implementation = MethodType(replace_implementation, wrap)
wrap.is_original = MethodType(is_original, wrap)
wrap.original_impl = MethodType(original_impl, wrap)

# store the wrapper to use it in the functions of this module.
_optical_property_store.append(wrap)

return wrap


def reset_all_to_original() -> None:
"""Reset all pluggable material property functions to their original implementations."""
for entry in _optical_property_store:
entry.reset_implementation()


def is_all_original() -> bool:
"""Get whether all pluggable material property functions use their original implementation."""
return all(p.is_original() for p in _optical_property_store)


def get_replaced() -> list[str]:
"""Get the names of all replaced pluggable material property functions."""
return [p.__name__ for p in _optical_property_store if not p.is_original()]
Loading

0 comments on commit 19cadb8

Please sign in to comment.