Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 08:02:21

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include "Acts/Material/detail/AverageMaterials.hpp"
0010 
0011 #include "Acts/Material/Material.hpp"
0012 
0013 #include <cmath>
0014 
0015 Acts::MaterialSlab Acts::detail::combineSlabs(const MaterialSlab& slab1,
0016                                               const MaterialSlab& slab2) {
0017   return combineSlabs(slab1, slab2.material(), slab2.thickness());
0018 }
0019 
0020 Acts::MaterialSlab Acts::detail::combineSlabs(const MaterialSlab& slab1,
0021                                               const Material& material2,
0022                                               float thickness2_) {
0023   const auto& mat1 = slab1.material();
0024   const auto& mat2 = material2;
0025 
0026   // NOTE 2020-08-26 msmk
0027   // the following computations provide purely geometrical averages of the
0028   // material properties. what we are really interested in are the material
0029   // properties that best describe the material interaction of the averaged
0030   // material. It is unclear if the latter can be computed in a general fashion,
0031   // so we stick to the purely geometric averages for now.
0032 
0033   // NOTE 2021-03-24 corentin
0034   // In the case of the atomic number, the averaging is based on the log
0035   // to properly account for the energy loss in multiple material.
0036 
0037   // use double for (intermediate) computations to avoid precision loss
0038   const double thickness1 = static_cast<double>(slab1.thickness());
0039   const double thickness2 = static_cast<double>(thickness2_);
0040 
0041   if (thickness1 == 0) {
0042     return MaterialSlab(material2, thickness2_);
0043   }
0044   if (thickness2 == 0) {
0045     return slab1;
0046   }
0047 
0048   // the thickness properties are purely additive
0049   const double thickness = thickness1 + thickness2;
0050 
0051   // if the two materials are the same there is no need for additional
0052   // computation
0053   if (mat1 == mat2) {
0054     return {mat1, static_cast<float>(thickness)};
0055   }
0056 
0057   // molar amount-of-substance assuming a unit area, i.e. volume = thickness*1*1
0058   const double molarDensity1 = static_cast<double>(mat1.molarDensity());
0059   const double molarDensity2 = static_cast<double>(mat2.molarDensity());
0060 
0061   const double molarAmount1 = molarDensity1 * thickness1;
0062   const double molarAmount2 = molarDensity2 * thickness2;
0063   const double molarAmount = molarAmount1 + molarAmount2;
0064 
0065   // handle vacuum specially
0066   if (molarAmount <= 0) {
0067     return {Material::Vacuum(), static_cast<float>(thickness)};
0068   }
0069 
0070   // radiation/interaction length follows from consistency argument
0071   const double thicknessX01 = static_cast<double>(slab1.thicknessInX0());
0072   const double thicknessX02 = thickness2 / static_cast<double>(mat2.X0());
0073   const double thicknessL01 = static_cast<double>(slab1.thicknessInL0());
0074   const double thicknessL02 = thickness2 / static_cast<double>(mat2.L0());
0075 
0076   const double thicknessInX0 = thicknessX01 + thicknessX02;
0077   const double thicknessInL0 = thicknessL01 + thicknessL02;
0078 
0079   const float x0 = static_cast<float>(thickness / thicknessInX0);
0080   const float l0 = static_cast<float>(thickness / thicknessInL0);
0081 
0082   // assume two slabs of materials with N1,N2 atoms/molecules each with atomic
0083   // masses A1,A2 and nuclear charges. We have a total of N = N1 + N2
0084   // atoms/molecules and should have average atomic masses and nuclear charges
0085   // of
0086   //
0087   //     A = (N1*A1 + N2*A2) / (N1+N2) = (N1/N)*A1 + (N2/N)*A2 = W1*A1 + W2*A2
0088   //
0089   // the number of atoms/molecules in a given volume V with molar density rho is
0090   //
0091   //     N = V * rho * Na
0092   //
0093   // where Na is the Avogadro constant. the weighting factors follow as
0094   //
0095   //     Wi = (Vi*rhoi*Na) / (V1*rho1*Na + V2*rho2*Na)
0096   //        = (Vi*rhoi) / (V1*rho1 + V2*rho2)
0097   //
0098   // which can be computed from the molar amount-of-substance above.
0099   const double ar1 = static_cast<double>(mat1.Ar());
0100   const double ar2 = static_cast<double>(mat2.Ar());
0101 
0102   const double molarWeight1 = molarAmount1 / molarAmount;
0103   const double molarWeight2 = molarAmount2 / molarAmount;
0104   const float ar = static_cast<float>(molarWeight1 * ar1 + molarWeight2 * ar2);
0105 
0106   // In the case of the atomic number, its main use is the computation
0107   // of the mean excitation energy approximated in ATL-SOFT-PUB-2008-003 as :
0108   //     I = 16 eV * Z^(0.9)
0109   // This mean excitation energy will then be used to compute energy loss
0110   // which will be proportional to :
0111   //     Eloss ~ ln(1/I)*thickness
0112   // In the case of two successive material :
0113   //     Eloss = El1 + El2
0114   //           ~ ln(Z1)*t1 + ln(Z2)*t2
0115   //           ~ ln(Z)*t
0116   // To respect this the average atomic number thus need to be defined as :
0117   //     ln(Z)*t = ln(Z1)*t1 + ln(Z2)*t2
0118   //           Z = Exp( ln(Z1)*t1/t + ln(Z2)*t2/t )
0119   const double z1 = static_cast<double>(mat1.Z());
0120   const double z2 = static_cast<double>(mat2.Z());
0121 
0122   const double thicknessWeight1 = thickness1 / thickness;
0123   const double thicknessWeight2 = thickness2 / thickness;
0124   float z = 0.f;
0125   if (z1 > 0. && z2 > 0.) {
0126     z = static_cast<float>(std::exp(thicknessWeight1 * std::log(z1) +
0127                                     thicknessWeight2 * std::log(z2)));
0128   } else {
0129     z = static_cast<float>(thicknessWeight1 * z1 + thicknessWeight2 * z2);
0130   }
0131 
0132   // compute average molar density by dividing the total amount-of-substance by
0133   // the total volume for the same unit area, i.e. volume = totalThickness*1*1
0134   const float molarDensity = static_cast<float>(molarAmount / thickness);
0135 
0136   const double molarElectronAmount1 = mat1.molarElectronDensity() * thickness1;
0137   const double molarElectronAmount2 = mat2.molarElectronDensity() * thickness2;
0138   const double molarElectronAmount =
0139       molarElectronAmount1 + molarElectronAmount2;
0140   const float molarElectronDensity =
0141       static_cast<float>(molarElectronAmount / thickness);
0142 
0143   float meanExcitationEnergy = 0.f;
0144   if (mat1.meanExcitationEnergy() > 0 && mat2.meanExcitationEnergy() > 0) {
0145     meanExcitationEnergy = static_cast<float>(
0146         std::exp(thicknessWeight1 * std::log(mat1.meanExcitationEnergy())) *
0147         std::exp(thicknessWeight2 * std::log(mat2.meanExcitationEnergy())));
0148   } else {
0149     meanExcitationEnergy =
0150         static_cast<float>(thicknessWeight1 * mat1.meanExcitationEnergy() +
0151                            thicknessWeight2 * mat2.meanExcitationEnergy());
0152   }
0153 
0154   return {
0155       Material::fromMolarDensity(x0, l0, ar, z, molarDensity,
0156                                  molarElectronDensity, meanExcitationEnergy),
0157       static_cast<float>(thickness)};
0158 }