Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
AverageMaterials.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file AverageMaterials.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2020 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
10 
12 
13 #include <cmath>
14 
16  const MaterialSlab& slab2) {
17  const auto& mat1 = slab1.material();
18  const auto& mat2 = slab2.material();
19 
20  // NOTE 2020-08-26 msmk
21  // the following computations provide purely geometrical averages of the
22  // material properties. what we are really interested in are the material
23  // properties that best describe the material interaction of the averaged
24  // material. It is unclear if the latter can be computed in a general fashion,
25  // so we stick to the purely geometric averages for now.
26 
27  // NOTE 2021-03-24 corentin
28  // In the case of the atomic number, the averaging is based on the log
29  // to properly account for the energy loss in multiple material.
30 
31  // use double for (intermediate) computations to avoid precision loss
32 
33  // the thickness properties are purely additive
34  double thickness = static_cast<double>(slab1.thickness()) +
35  static_cast<double>(slab2.thickness());
36 
37  // if the two materials are the same there is no need for additional
38  // computation
39  if (mat1 == mat2) {
40  return {mat1, static_cast<float>(thickness)};
41  }
42 
43  double thicknessInX0 = static_cast<double>(slab1.thicknessInX0()) +
44  static_cast<double>(slab2.thicknessInX0());
45  double thicknessInL0 = static_cast<double>(slab1.thicknessInL0()) +
46  static_cast<double>(slab2.thicknessInL0());
47 
48  // radiation/interaction length follows from consistency argument
49  float x0 = thickness / thicknessInX0;
50  float l0 = thickness / thicknessInL0;
51 
52  // molar amount-of-substance assuming a unit area, i.e. volume = thickness*1*1
53  double molarAmount1 = static_cast<double>(mat1.molarDensity()) *
54  static_cast<double>(slab1.thickness());
55  double molarAmount2 = static_cast<double>(mat2.molarDensity()) *
56  static_cast<double>(slab2.thickness());
57  double molarAmount = molarAmount1 + molarAmount2;
58 
59  // handle vacuum specially
60  if (not(0.0 < molarAmount)) {
61  return {Material(), static_cast<float>(thickness)};
62  }
63 
64  // compute average molar density by dividing the total amount-of-substance by
65  // the total volume for the same unit area, i.e. volume = totalThickness*1*1
66  float molarDensity = molarAmount / thickness;
67 
68  // assume two slabs of materials with N1,N2 atoms/molecules each with atomic
69  // masses A1,A2 and nuclear charges. We have a total of N = N1 + N2
70  // atoms/molecules and should have average atomic masses and nuclear charges
71  // of
72  //
73  // A = (N1*A1 + N2*A2) / (N1+N2) = (N1/N)*A1 + (N2/N)*A2 = W1*A1 + W2*A2
74  //
75  // the number of atoms/molecules in a given volume V with molar density rho is
76  //
77  // N = V * rho * Na
78  //
79  // where Na is the Avogadro constant. the weighting factors follow as
80  //
81  // Wi = (Vi*rhoi*Na) / (V1*rho1*Na + V2*rho2*Na)
82  // = (Vi*rhoi) / (V1*rho1 + V2*rho2)
83  //
84  // which can be computed from the molar amount-of-substance above.
85 
86  double molarWeight1 = molarAmount1 / molarAmount;
87  double molarWeight2 = molarAmount2 / molarAmount;
88  float ar = molarWeight1 * mat1.Ar() + molarWeight2 * mat2.Ar();
89 
90  // In the case of the atomic number, its main use is the computation
91  // of the mean excitation energy approximated in ATL-SOFT-PUB-2008-003 as :
92  // I = 16 eV * Z^(0.9)
93  // This mean excitation energy will then be used to compute energy loss
94  // which will be proportional to :
95  // Eloss ~ ln(1/I)*thickness
96  // In the case of two successive material :
97  // Eloss = El1 + El2
98  // ~ ln(Z1)*t1 + ln(Z2)*t2
99  // ~ ln(Z)*t
100  // To respect this the average atomic number thus need to be defined as :
101  // ln(Z)*t = ln(Z1)*t1 + ln(Z2)*t2
102  // Z = Exp( ln(Z1)*t1/t + ln(Z2)*t2/t )
103 
104  double thicknessWeight1 = slab1.thickness() / thickness;
105  double thicknessWeight2 = slab2.thickness() / thickness;
106  float z = 0;
107  if (mat1.Z() != 0 && mat2.Z() != 0) {
108  z = exp(thicknessWeight1 * log(mat1.Z()) +
109  thicknessWeight2 * log(mat2.Z()));
110  } else {
111  z = thicknessWeight1 * mat1.Z() + thicknessWeight2 * mat2.Z();
112  }
113 
114  return {Material::fromMolarDensity(x0, l0, ar, z, molarDensity),
115  static_cast<float>(thickness)};
116 }