1 package org.lcsim.detector.material;
2
3 import static java.lang.Math.abs;
4
5 import java.util.ArrayList;
6 import java.util.List;
7
8
9
10
11
12
13
14
15 public class MaterialMixture
16 extends MaterialElement
17 {
18
19 public static final double MAX_RADIATION_LENGTH = java.lang.Double.MAX_VALUE;
20
21
22 public static final double MAX_NUCLEAR_INTERACTION_LENGTH = java.lang.Double.MAX_VALUE;
23
24
25 private double Zeff;
26
27
28 private double Aeff;
29
30
31 private int nComponentsAdded=0;
32
33
34 private int nComponentsMax=0;
35
36
37 private int nElements=0;
38
39
40 private List<MaterialElement> elementList = new ArrayList<MaterialElement>();
41
42
43 private List<Double> massFractionList = new ArrayList<Double>();
44
45
46 private List<Integer> atomCountList = new ArrayList<Integer>();
47
48
49 public MaterialMixture(
50 String name,
51 int nComponents,
52 double density,
53 State state)
54 {
55 this.name = name;
56 this.density = density;
57 this.state = state;
58
59 if ( nComponents <= 0 )
60 {
61 throw new IllegalArgumentException("nComponents must be >= 0.");
62 }
63
64 this.nComponentsMax = nComponents;
65 }
66
67 public double getZ()
68 {
69 return Zeff;
70 }
71
72 public double getA()
73 {
74 return Aeff;
75 }
76
77
78 public int getNComponentsMax()
79 {
80 return nComponentsMax;
81 }
82
83
84 public int getNComponents()
85 {
86 return nComponentsAdded;
87 }
88
89
90 public int getNElements()
91 {
92 return nElements;
93 }
94
95
96 private double computeNuclearInteractionLengthForMixture()
97 {
98 double NILinv = 0.0;
99
100 for (int i = 0; i < nElements; i++)
101 {
102 MaterialElement me = elementList.get(i);
103 NILinv += massFractionList.get(i) / me.getNuclearInteractionLength();
104 }
105
106 nuclearInteractionLength = (NILinv <= 0.0 ? MAX_NUCLEAR_INTERACTION_LENGTH : 1.0/NILinv);
107 nuclearInteractionLengthWithDensity = nuclearInteractionLength / getDensity();
108
109 return NILinv;
110 }
111
112
113 private double computeRadiationLengthForMixture()
114 {
115 double rlinv = 0.0;
116
117 for (int i = 0; i < nElements; i++)
118 {
119 MaterialElement me = elementList.get(i);
120 rlinv += massFractionList.get(i) / me.getRadiationLength();
121 }
122
123 radiationLength = (rlinv <= 0.0 ? MAX_RADIATION_LENGTH : 1.0/rlinv);
124 radiationLengthWithDensity = radiationLength / getDensity();
125
126 return radiationLength;
127 }
128
129
130 private void computeDerivedQuantities()
131 {
132 computeZeff();
133 computeAeff();
134
135
136 computeRadiationLengthForMixture();
137 computeNuclearInteractionLengthForMixture();
138 computeCriticalEnergy();
139 computeMoliereRadius();
140 }
141
142
143 public int getNumberOfElements()
144 {
145 return nElements;
146 }
147
148
149 public List<MaterialElement> getElements()
150 {
151 return elementList;
152 }
153
154
155 public List<Integer> getAtomCounts()
156 {
157 return atomCountList;
158 }
159
160
161 public List<Double> getMassFractions()
162 {
163 return massFractionList;
164 }
165
166
167
168
169
170
171 public void addElement(MaterialElement element, int nAtoms)
172 {
173 if ( nElements < nComponentsMax )
174 {
175 elementList.add(element);
176 atomCountList.add( nElements, nAtoms);
177 nComponentsAdded = ++nElements;
178 }
179 else
180 {
181 throw new RuntimeException("Attempting to add more than declared number of elements for this material: " + getName());
182 }
183
184 if ( isFilled() )
185 {
186 double Amol = 0;
187
188 int atomCnt = 0;
189 for ( MaterialElement e : elementList )
190 {
191 Amol += atomCountList.get(atomCnt) * e.getA();
192 ++atomCnt;
193 }
194
195 int massCnt = 0;
196 for ( MaterialElement ee : elementList )
197 {
198 massFractionList.add(atomCountList.get(massCnt) * ee.getA() / Amol);
199 ++massCnt;
200 }
201
202 computeDerivedQuantities();
203 }
204 }
205
206
207
208
209 public void addElement(MaterialElement element,
210 double fraction)
211 {
212 if ( nComponentsAdded < nComponentsMax )
213 {
214 int elCnt = 0;
215 while (( elCnt < nElements ) && element != elementList.get(elCnt)) elCnt++;
216
217
218 if (elCnt < nElements)
219 {
220 double currFrac = massFractionList.get(elCnt);
221 currFrac += fraction;
222 massFractionList.add(elCnt, currFrac);
223 }
224
225 else
226 {
227 elementList.add(element);
228 massFractionList.add(elCnt, fraction);
229 ++nElements;
230 }
231 ++nComponentsAdded;
232 }
233 else
234 {
235 throw new RuntimeException("Attempting to add more than declared number of components to material: " + getName() );
236 }
237
238 if ( isFilled() )
239 {
240 checkMassSum();
241 computeDerivedQuantities();
242 }
243 }
244
245
246 public void addMaterial(
247 MaterialMixture material,
248 double fraction)
249 {
250 if ( atomCountList.size() > 0 )
251 {
252 throw new RuntimeException("Material is already defined by atoms: " + getName());
253 }
254
255 if ( nComponentsAdded < nComponentsMax )
256 {
257
258 for ( int i = 0; i < material.getNumberOfElements(); i++)
259 {
260 MaterialElement element = material.getElements().get(i);
261 int elCnt = 0;
262
263 while ((elCnt < nElements) && (element != elementList.get(elCnt)))
264 {
265 ++elCnt;
266 }
267
268
269 if ( elCnt < nElements )
270 {
271 double currFrac = massFractionList.get(elCnt);
272 currFrac += fraction * material.getMassFractions().get(i) ;
273 massFractionList.set(elCnt, currFrac);
274 }
275
276 else
277 {
278
279
280 elementList.add(element);
281
282
283
284
285
286 massFractionList.add(elCnt, fraction * material.getMassFractions().get(i));
287 ++nElements;
288 }
289 }
290 ++nComponentsAdded;
291 }
292 else
293 {
294 throw new RuntimeException("Attempting to add more than declared number of components for material: " + getName() );
295 }
296
297 if ( isFilled() )
298 {
299 checkMassSum();
300 computeDerivedQuantities();
301 }
302 }
303
304
305 public boolean isFilled()
306 {
307 return (nComponentsAdded == nComponentsMax);
308 }
309
310
311 private void checkMassSum()
312 {
313 double weightSum = 0;
314 for ( int i = 0; i < massFractionList.size(); i++)
315 {
316 weightSum += massFractionList.get(i);
317 }
318
319 if ( abs(1 - weightSum) > 0.001 )
320 {
321 throw new RuntimeException("Mass fractions do not sum to 1 within 0.001 tolerance for this material: " + getName() );
322 }
323 }
324
325
326 private double computeZeff()
327 {
328 double ZsumNotNorm = 0;
329 double atomCntFracSum = 0;
330
331 int nelem = this.getNElements();
332 for ( int i = 0; i < nelem; i++ )
333 {
334 MaterialElement me = this.getElements().get(i);
335 double massFrac = this.getMassFractions().get(i);
336 double atomCntFrac = massFrac / me.getA();
337 ZsumNotNorm += atomCntFrac * me.getZ();
338 atomCntFracSum += atomCntFrac;
339 }
340
341 double ZsumNorm = ZsumNotNorm / atomCntFracSum;
342 Zeff = ZsumNorm;
343 return Zeff;
344 }
345
346
347 private double computeAeff()
348 {
349 double AsumNotNorm = 0;
350 double atomCntFracSum = 0;
351
352 int nelem = this.getNElements();
353 for ( int i = 0; i < nelem; i++ )
354 {
355 MaterialElement me = this.getElements().get(i);
356 double massFrac = this.getMassFractions().get(i);
357 double atomCntFrac = massFrac / me.getA();
358 AsumNotNorm += atomCntFrac * me.getA();
359 atomCntFracSum += atomCntFrac;
360 }
361 double ZsumNorm = AsumNotNorm / atomCntFracSum;
362 Aeff = ZsumNorm;
363 return Aeff;
364 }
365
366
367 public String toString()
368 {
369 StringBuffer sb = new StringBuffer();
370 sb.append(super.toString());
371 sb.append("numberOfComponents = " + nComponentsAdded + "; ");
372 sb.append("maxNumberOfComponents = " + nComponentsMax + "; ");
373 sb.append("numberOfElements = " + nElements);
374
375 for ( int i = 0; i < this.getNElements(); i++)
376 {
377 sb.append('\n');
378 sb.append('\t');
379 sb.append(this.getElements().get(i).getName() + " ");
380 sb.append(this.getMassFractions().get(i) * 100);
381 }
382
383 return sb.toString();
384 }
385 }