Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-07 10:11:34

0001 //------------------------------- -*- C++ -*- -------------------------------//
0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details
0003 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0004 //---------------------------------------------------------------------------//
0005 //! \file celeritas/inp/OpticalPhysics.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include <algorithm>
0010 #include <map>
0011 #include <optional>
0012 #include <variant>
0013 
0014 #include "corecel/cont/Range.hh"
0015 #include "corecel/inp/Distributions.hh"
0016 #include "corecel/inp/Grid.hh"
0017 #include "celeritas/Quantities.hh"
0018 #include "celeritas/Types.hh"
0019 #include "celeritas/io/ImportOpticalMaterial.hh"
0020 #include "celeritas/io/ImportOpticalModel.hh"
0021 #include "celeritas/optical/Types.hh"
0022 
0023 namespace celeritas
0024 {
0025 class SurfaceModel;
0026 
0027 namespace inp
0028 {
0029 //---------------------------------------------------------------------------//
0030 // OPTICAL PHOTON GENERATION
0031 //---------------------------------------------------------------------------//
0032 //! Limit steps to ensure high-quality cherenkov emission (NOT YET USED)
0033 struct CherenkovStepLimit
0034 {
0035     //! Limit based on speed loss
0036     units::LightSpeed max_speed_loss{};
0037 
0038     //! Limit based on average number of photons to be emitted
0039     real_type max_photons{};
0040 };
0041 
0042 //! Configure cherenkov emission
0043 struct CherenkovProcess
0044 {
0045     //! Limit steps
0046     std::optional<CherenkovStepLimit> step_limit;
0047 };
0048 
0049 //---------------------------------------------------------------------------//
0050 //! Sampling quantity given as an argument to optical distributions
0051 enum class SpectrumArgument
0052 {
0053     wavelength,  //!< Units: [len]
0054     energy,  //!< Units: [MeV]
0055     size_
0056 };
0057 
0058 //! Allowable scintillation spectrum distributions
0059 using SpectrumDistribution = std::variant<NormalDistribution, Grid>;
0060 
0061 /*!
0062  * A single unnormalized scintillation distribution in energy and time.
0063  *
0064  * The distribution is scaled by \c yield, with a biexponential time
0065  * distribution, and a wavelength/energy spectrum.
0066  */
0067 struct ScintillationSpectrum
0068 {
0069     //! Expected number of photons per energy deposition [1/MeV]
0070     double yield{};
0071 
0072     //! Exponential rise constant (none if zero) [time]
0073     double rise_time{};
0074     //! Exponential decay constant [time]
0075     double fall_time{};
0076 
0077     //! Normalized emission spectrum form (gaussian, continuous grid)
0078     SpectrumDistribution spectrum_distribution;
0079     //! Emission spectrum type/units (wavelength, energy)
0080     SpectrumArgument spectrum_argument;
0081 };
0082 
0083 //! A scintillation material can have one or more intensity component spectra
0084 struct ScintillationMaterial
0085 {
0086     //! Accumulate multiple spectrum components in a material
0087     std::vector<ScintillationSpectrum> components;
0088     //! Multiplicative stdev constant for sampling the number of total photons
0089     double resolution_scale{1};
0090 
0091     //! Whether any scintillation is present
0092     explicit operator bool() const { return !components.empty(); }
0093 };
0094 
0095 //! Emit optical photons proportional to local energy deposition
0096 struct ScintillationProcess
0097 {
0098     std::map<OptMatId, ScintillationMaterial> materials;
0099 
0100     //! Whether any scintillating materials are present
0101     bool empty() const { return materials.empty(); }
0102 };
0103 
0104 //---------------------------------------------------------------------------//
0105 /*!
0106  * Optical photon generation from other particles.
0107  */
0108 struct OpticalGenPhysics
0109 {
0110     //! Enable Cherenkov emission
0111     std::optional<CherenkovProcess> cherenkov;
0112     //! Enable optical scintillation
0113     std::optional<ScintillationProcess> scintillation;
0114 
0115     //! Whether optical generation is enabled
0116     explicit operator bool() const { return cherenkov || scintillation; }
0117 };
0118 
0119 //---------------------------------------------------------------------------//
0120 // VOLUMETRIC (BULK) MODELS
0121 //---------------------------------------------------------------------------//
0122 /*!
0123  * Absorption properties for a single material.
0124  */
0125 struct AbsorptionMaterial
0126 {
0127     //! Mean free path grid [MeV, len]
0128     inp::Grid mfp;
0129 
0130     //! Whether all data are assigned and valid
0131     explicit operator bool() const { return static_cast<bool>(mfp); }
0132 };
0133 
0134 //---------------------------------------------------------------------------//
0135 /*!
0136  * Mie scattering properties (Henyey-Greenstein model) for a single material.
0137  */
0138 struct MieMaterial
0139 {
0140     //! Henyey-Greenstein "g" parameter for forward scattering
0141     double forward_g{};
0142 
0143     //! Henyey-Greenstein "g" parameter for backward scattering
0144     double backward_g{};
0145 
0146     //! Fraction of forward vs backward scattering
0147     double forward_ratio{};
0148 
0149     //! Mean free path grid [MeV, len]
0150     inp::Grid mfp;
0151 
0152     //! Whether all data are assigned and valid
0153     explicit operator bool() const
0154     {
0155         return forward_ratio >= 0 && forward_ratio <= 1 && forward_g >= -1
0156                && forward_g <= 1 && backward_g >= -1 && backward_g <= 1 && mfp;
0157     }
0158 };
0159 
0160 //---------------------------------------------------------------------------//
0161 /*!
0162  * Rayleigh scattering properties for calculating the MFP analytically.
0163  */
0164 struct OpticalRayleighAnalytic
0165 {
0166     //! Scale the scattering length
0167     std::optional<double> scale_factor;
0168 
0169     //! Isothermal compressibility [len-time^2/mass]
0170     double compressibility{0};
0171 
0172     //! Whether all data are assigned and valid
0173     explicit operator bool() const
0174     {
0175         return (!scale_factor || *scale_factor > 0) && compressibility > 0;
0176     }
0177 };
0178 
0179 //---------------------------------------------------------------------------//
0180 /*!
0181  * Rayleigh scattering properties for a single material.
0182  */
0183 struct OpticalRayleighMaterial
0184 {
0185     //! Mean free path grid [MeV, len]
0186     std::variant<inp::Grid, OpticalRayleighAnalytic> mfp;
0187 
0188     //! Whether all data are assigned and valid
0189     explicit operator bool() const
0190     {
0191         return std::visit([](auto const& v) { return static_cast<bool>(v); },
0192                           mfp);
0193     }
0194 };
0195 
0196 //---------------------------------------------------------------------------//
0197 /*!
0198  * Wavelength shifting properties for a single material.
0199  *
0200  * The component vector represents the relative population as a function of the
0201  * re-emission energy. It is used to define an inverse CDF needed to sample the
0202  * re-emitted optical photon energy.
0203  */
0204 struct WavelengthShiftMaterial
0205 {
0206     //! Mean number of reemitted photons
0207     double mean_num_photons{};
0208 
0209     //! Time delay between absorption and reemission
0210     double time_constant{};
0211 
0212     //! Reemission population [MeV, unitless]
0213     inp::Grid component;
0214 
0215     //! Mean free path grid [MeV, len]
0216     inp::Grid mfp;
0217 
0218     //! Whether all data are assigned and valid
0219     explicit operator bool() const
0220     {
0221         return mean_num_photons > 0 && time_constant > 0 && component && mfp;
0222     }
0223 };
0224 
0225 //! Absorption
0226 struct OpticalBulkAbsorption
0227 {
0228     //! Model data for each material
0229     std::map<OptMatId, AbsorptionMaterial> materials;
0230 
0231     //! Whether all data are assigned and valid
0232     explicit operator bool() const { return !materials.empty(); }
0233 };
0234 
0235 //! Mie scattering
0236 struct OpticalBulkMie
0237 {
0238     //! Model data for each material
0239     std::map<OptMatId, MieMaterial> materials;
0240 
0241     //! Whether all data are assigned and valid
0242     explicit operator bool() const { return !materials.empty(); }
0243 };
0244 
0245 //! Rayleigh scattering
0246 struct OpticalBulkRayleigh
0247 {
0248     //! Model data for each material
0249     std::map<OptMatId, OpticalRayleighMaterial> materials;
0250 
0251     //! Whether all data are assigned and valid
0252     explicit operator bool() const { return !materials.empty(); }
0253 };
0254 
0255 //! Wavelength shifting
0256 struct OpticalBulkWavelengthShift
0257 {
0258     using Dist = optical::WlsDistribution;
0259 
0260     //! Model data for each material
0261     std::map<OptMatId, WavelengthShiftMaterial> materials;
0262 
0263     //! Time profile distribution type
0264     Dist time_profile{Dist::size_};
0265 
0266     //! Whether all data are assigned and valid
0267     explicit operator bool() const
0268     {
0269         return !materials.empty() && time_profile != Dist::size_;
0270     }
0271 };
0272 
0273 //---------------------------------------------------------------------------//
0274 /*!
0275  * Optical physics for bulk materials.
0276  *
0277  * Each optical material can have one or more model that applies to it.
0278  */
0279 struct OpticalBulkPhysics
0280 {
0281     //! Absorption models for all materials
0282     OpticalBulkAbsorption absorption;
0283 
0284     //! Mie scattering models for materials
0285     OpticalBulkMie mie;
0286 
0287     //! Rayleigh scattering models for all materials
0288     OpticalBulkRayleigh rayleigh;
0289 
0290     //! WLS models (TODO: merge into a single WLS with arbitrary components)
0291     OpticalBulkWavelengthShift wls;
0292     OpticalBulkWavelengthShift wls2;
0293 
0294     //! Whether optical physics is enabled
0295     explicit operator bool() const
0296     {
0297         return absorption || mie || rayleigh || wls || wls2;
0298     }
0299 };
0300 
0301 //---------------------------------------------------------------------------//
0302 // SURFACE DESCRIPTION: Reflectivity and models for surface normals.
0303 //---------------------------------------------------------------------------//
0304 /*!
0305  * Model reflectivity as a user-prescribed function of energy.
0306  *
0307  * As per Geant4 conventions:
0308  * - Reflectivity is the probability that a photon undergoes its usual surface
0309  * interaction.
0310  * - Transmittance is the probability that a photon crosses the surface without
0311  * change.
0312  * - Efficiency is the quantum efficiency of a detector at the surface. It is
0313  * the probability that a photon absorbed will be registered by the detector.
0314  *
0315  * Only reflectivity and transmittance are defined by the user, while the
0316  * remaining probability is the chance for the photon to be absorbed. If the
0317  * photon is absorbed and an efficiency grid is defined, then the efficiency
0318  * probability is used to determine if the photon is detected.
0319  *
0320  * The reflectivity and transmittance grids are sampled together and must sum
0321  * to between 0 and 1 at every grid point.
0322  *
0323  * The efficiency grid is sampled independently (if provided), and must be in
0324  * the range [0,1].
0325  */
0326 struct GridReflection
0327 {
0328     //! Grid values [MeV -> unitless]
0329     Grid reflectivity;
0330     Grid transmittance;
0331 
0332     Grid efficiency;  //!< optional
0333 
0334     // Whether the data are assigned
0335     explicit operator bool() const { return reflectivity && transmittance; }
0336 };
0337 
0338 //---------------------------------------------------------------------------//
0339 /*!
0340  * Model reflectivity analytically using the Fresnel equations.
0341  */
0342 struct FresnelReflection
0343 {
0344 };
0345 
0346 //---------------------------------------------------------------------------//
0347 /*!
0348  * A polished (perfectly smooth) surface.
0349  *
0350  * For smooth surfaces, the facet normal is the macroscopic normal.
0351  */
0352 struct NoRoughness
0353 {
0354 };
0355 
0356 //---------------------------------------------------------------------------//
0357 /*!
0358  * Global surface normal with smearing.
0359  *
0360  * Roughness range is \f$ [0, 1] \f$, where 0 is specular and 1 is diffuse.
0361  * This parameter is the complement of the \em polish as defined in Geant4:
0362  * \code roughness = 1 - GetPolish(); \endcode .
0363  *
0364  * See \c celeritas::optical::SmearRoughnessSampler .
0365  *
0366  * \note This model is used by the GLISUR subroutine in Geant3 and the
0367  * corresponding "glisur" surface model in Geant4.
0368  */
0369 struct SmearRoughness
0370 {
0371     double roughness{-1};  //!< Scale from 0 = specular to 1 = diffuse
0372 
0373     // Whether the data are assigned
0374     explicit operator bool() const { return roughness >= 0 && roughness <= 1; }
0375 };
0376 
0377 //---------------------------------------------------------------------------//
0378 /*!
0379  * Approximate the microfacet normal distributions as Gaussian.
0380  *
0381  * See \c celeritas::optical::GaussianRoughnessSampler .
0382  *
0383  * \note This model is used by the "unified" surface model in Geant4.
0384  */
0385 struct GaussianRoughness
0386 {
0387     //! Standard deviation of the microfacet slope distribution
0388     double sigma_alpha{0};
0389 
0390     // Whether the roughness has a meaningful value
0391     explicit operator bool() const { return sigma_alpha > 0; }
0392 };
0393 
0394 //---------------------------------------------------------------------------//
0395 // SURFACE PHYSICS: interaction mechanisms / reflection models.
0396 //---------------------------------------------------------------------------//
0397 /*!
0398  * Parameterization of the UNIFIED reflection model.
0399  *
0400  * The reflection grids store the probability of each angular distribution in
0401  * the UNIFIED model:
0402  * - \c specular_spike : Reflection probability at the average surface normal.
0403  * - \c specular_lobe : Reflection probability at the micro facet normal.
0404  * - \c backscatter : Probability of backscattering after reflecting within a
0405  *   deep groove.
0406  *
0407  * The sum of all three parameters must be <= 1 at every grid point, with the
0408  * remainder being the probability of diffuse scattering.
0409  *
0410  * \todo We could require these to all be on the same energy grid for improved
0411  * performance and error checking.
0412  */
0413 struct ReflectionForm
0414 {
0415     //!@{
0416     //! \name Type aliases
0417     using Mode = optical::ReflectionMode;
0418     using ReflectionGrids = EnumArray<Mode, Grid>;
0419     //!@}
0420 
0421     //! Probability of reflection for each reflection mode
0422     ReflectionGrids reflection_grids;
0423 
0424     // Whether all grids are specified
0425     explicit operator bool() const
0426     {
0427         return std::all_of(
0428             reflection_grids.begin(),
0429             reflection_grids.end(),
0430             [](auto const& grid) { return static_cast<bool>(grid); });
0431     }
0432 
0433     //! Return a specular spike reflection form
0434     static ReflectionForm from_spike()
0435     {
0436         return ReflectionForm::from_mode(Mode::specular_spike);
0437     }
0438 
0439     //! Return a specular lobe reflection form
0440     static ReflectionForm from_lobe()
0441     {
0442         return ReflectionForm::from_mode(Mode::specular_lobe);
0443     }
0444 
0445     //! Return a Lambertian (diffuse) reflection form
0446     static ReflectionForm from_lambertian()
0447     {
0448         return ReflectionForm::from_mode(Mode::diffuse_lobe);
0449     }
0450 
0451     //! Construct a reflection form with only one active grid
0452     static ReflectionForm from_mode(Mode only_mode)
0453     {
0454         ReflectionForm result;
0455         for (auto mode : range(optical::ReflectionMode::size_))
0456         {
0457             result.reflection_grids[mode]
0458                 = Grid::from_constant(mode == only_mode ? 1 : 0);
0459         }
0460         return result;
0461     }
0462 };
0463 
0464 //---------------------------------------------------------------------------//
0465 /*!
0466  * Analytic interactions between dielectric and dielectric or metal materials.
0467  */
0468 struct DielectricInteraction
0469 {
0470     //! Exiting angular distributions
0471     ReflectionForm reflection;
0472 
0473     //! Whether the interface is dielectric-dielectric or dielectric-metal
0474     bool is_metal{false};
0475 
0476     //! Return a dielectric-dielectric interaction
0477     static DielectricInteraction from_dielectric(ReflectionForm r)
0478     {
0479         return {std::move(r), false};
0480     }
0481 
0482     //! Return a dielectric-metal interaction
0483     static DielectricInteraction from_metal(ReflectionForm r)
0484     {
0485         return {std::move(r), true};
0486     }
0487 };
0488 
0489 //---------------------------------------------------------------------------//
0490 /*!
0491  * Surface roughness description.
0492  */
0493 struct RoughnessModels
0494 {
0495     //! Perfectly smooth surfaces
0496     std::map<PhysSurfaceId, NoRoughness> polished;
0497     //! Surfaces using the "smear" roughness model
0498     std::map<PhysSurfaceId, SmearRoughness> smear;
0499     //! Surfaces using the "gaussian" roughness model
0500     std::map<PhysSurfaceId, GaussianRoughness> gaussian;
0501 
0502     // Whether any models are present
0503     explicit operator bool() const
0504     {
0505         return !polished.empty() || !smear.empty() || !gaussian.empty();
0506     }
0507 };
0508 
0509 //---------------------------------------------------------------------------//
0510 /*!
0511  * Reflectivity mechanism.
0512  */
0513 struct ReflectivityModels
0514 {
0515     //! Surfaces using an energy-dependent, user-specified grid
0516     std::map<PhysSurfaceId, GridReflection> grid;
0517     //! Surfaces using the analytic Fresnel equations
0518     std::map<PhysSurfaceId, FresnelReflection> fresnel;
0519 
0520     // Whether any models are present
0521     explicit operator bool() const
0522     {
0523         return !grid.empty() || !fresnel.empty();
0524     }
0525 };
0526 
0527 //---------------------------------------------------------------------------//
0528 /*!
0529  * Interaction models for different interface types.
0530  *
0531  * This will be extended to allow user-provided interaction kernels.
0532  */
0533 struct InteractionModels
0534 {
0535     //! Composite reflection distributions at a dielectric interface
0536     std::map<PhysSurfaceId, DielectricInteraction> dielectric;
0537 
0538     //! Trivial interactions independent of other surface physics
0539     std::map<PhysSurfaceId, optical::TrivialInteractionMode> trivial;
0540 
0541     //! Strictly reflective interactions of a single type
0542     std::map<PhysSurfaceId, optical::ReflectionMode> only_reflection;
0543 
0544     // Whether any models are present
0545     explicit operator bool() const
0546     {
0547         return !dielectric.empty() || !trivial.empty()
0548                || !only_reflection.empty();
0549     }
0550 };
0551 
0552 //---------------------------------------------------------------------------//
0553 /*!
0554  * Surface physics definition.
0555  *
0556  * Maps all optical surfaces with interaction models and surface
0557  * parameters.
0558  *
0559  * Interstitial materials are the interstitial materials per geometric surface.
0560  * The last entry is used as the default surface.
0561  */
0562 struct OpticalSurfacePhysics
0563 {
0564     //!@{
0565     //! \name type aliases
0566     using DetectionEfficiency = std::map<PhysSurfaceId, Grid>;
0567     using VecInterstitialMaterials = std::vector<OptMatId>;
0568     //!@}
0569 
0570     std::vector<VecInterstitialMaterials> materials;
0571 
0572     //! Microfacet distribution models
0573     RoughnessModels roughness;
0574     //! Reflectivity models
0575     ReflectivityModels reflectivity;
0576     //! Reflection+refraction+absorption models
0577     InteractionModels interaction;
0578 
0579     // Whether the data are assigned
0580     explicit operator bool() const
0581     {
0582         return roughness && reflectivity && interaction && !materials.empty();
0583     }
0584 };
0585 
0586 //---------------------------------------------------------------------------//
0587 }  // namespace inp
0588 }  // namespace celeritas