|
|
|||
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
| [ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
|
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
|