1 package org.lcsim.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 public class Material implements IMaterial
15 {
16 public static final double DEFAULT_TEMPERATURE = 273.15;
17 public static final double DEFAULT_PRESSURE = 1.0;
18
19 private static final double MAX_X0 = java.lang.Double.MAX_VALUE;
20 private static final double MAX_LAMBDA = java.lang.Double.MAX_VALUE;
21
22 double _density;
23
24 boolean _isElement;
25
26 private double _Zeff;
27 private double _Aeff;
28
29 MaterialState _state;
30 String _name;
31 String _formula;
32
33 int _nComponents;
34 int _nComponentsMax;
35 int _nElements;
36
37 private List<MaterialElement> _elements = new ArrayList<MaterialElement>();
38 private List<Double> _massFractions = new ArrayList<Double>();
39 private List<Integer> _atoms = new ArrayList<Integer>();
40
41 double _radiationLength;
42 double _radiationLengthWithDensity;
43
44 double _nuclearInteractionLength;
45 double _nuclearInteractionLengthWithDensity;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 Material(String matName,
62 String formula,
63 double z,
64 double a,
65 double density,
66 double X0,
67 double lambda,
68 MaterialState state)
69 {
70
71
72
73 _name = matName;
74 _density = density;
75 _formula = formula;
76 _state = state;
77
78
79 _Zeff = z;
80 _Aeff = a;
81
82
83 _nComponents = 0;
84 _nComponentsMax = 1;
85 _isElement = true;
86
87
88 this.addElement(new MaterialElement(formula, z, a, X0, lambda), 1.0);
89
90
91 computeRadiationLength();
92
93
94 computeNuclearInteractionLength();
95
96
97 MaterialManager.instance().addMaterial(this);
98 }
99
100
101
102
103
104
105 public Material(String name,
106 int nComponents,
107 double density,
108 MaterialState state)
109 {
110 _name = name;
111 _density = density;
112 _state = state;
113
114
115 if (nComponents < 1)
116 {
117 throw new IllegalArgumentException("nComponents must be > 0.");
118 }
119
120 _nComponentsMax = nComponents;
121 _nComponents = _nElements = 0;
122 _isElement = false;
123
124 MaterialManager.instance().addMaterial(this);
125 }
126
127
128
129
130
131 public String getName()
132 {
133 return _name;
134 }
135
136
137
138
139
140 public double getDensity()
141 {
142 return _density;
143 }
144
145
146 public boolean isElement()
147 {
148 return _isElement;
149 }
150
151
152
153
154
155 public double getZeff()
156 {
157 return _Zeff;
158 }
159
160
161
162
163
164 public double getAeff()
165 {
166 return _Aeff;
167 }
168
169
170
171
172 protected int getNComponentsMax()
173 {
174 return _nComponentsMax;
175 }
176
177
178
179
180 protected int getNComponents()
181 {
182 return _nComponents;
183 }
184
185
186
187
188 protected int getNElements()
189 {
190 return _nElements;
191 }
192
193
194
195
196 public MaterialState getState()
197 {
198 return _state;
199 }
200
201
202
203
204 public double getRadiationLength()
205 {
206 return _radiationLength;
207 }
208
209
210
211
212 public double getNuclearInteractionLength()
213 {
214 return _nuclearInteractionLength;
215 }
216
217
218
219
220 public double getNuclearInteractionLengthWithDensity()
221 {
222 return _nuclearInteractionLengthWithDensity;
223 }
224
225
226
227
228 public double getRadiationLengthWithDensity()
229 {
230 return _radiationLengthWithDensity;
231 }
232
233
234
235
236 protected int getNumberOfElements()
237 {
238 return _nElements;
239 }
240
241
242
243
244
245 public List<MaterialElement> getElements()
246 {
247 return _elements;
248 }
249
250
251
252
253 public List<Double> getMassFractions()
254 {
255 return _massFractions;
256 }
257
258
259
260
261
262
263 protected void addElement(MaterialElement element, int nAtoms)
264 {
265 if (_nElements < _nComponentsMax)
266 {
267 _elements.add(element);
268 _atoms.add(_nElements, nAtoms);
269 _nComponents = ++_nElements;
270 }
271 else
272 {
273 throw new RuntimeException("Attempting to add more than declared number of elements for this material: " + _name);
274 }
275
276 if (isFilled())
277 {
278
279 double Amol = 0;
280
281 int atomCnt = 0;
282 for (MaterialElement e : _elements)
283 {
284 Amol += _atoms.get(atomCnt) * e.getA();
285 ++atomCnt;
286 }
287
288 int massCnt = 0;
289 for (MaterialElement ee : _elements)
290 {
291 _massFractions.add(_atoms.get(massCnt) * ee.getA() / Amol);
292 ++massCnt;
293 }
294
295 computeDerivedQuantities();
296 }
297 }
298
299
300
301
302
303 protected void addElement(MaterialElement element, double fraction)
304 {
305 if (_nComponents < _nComponentsMax)
306 {
307 int elCnt = 0;
308 while ((elCnt < _nElements) && element != _elements.get(elCnt))
309 elCnt++ ;
310
311
312 if (elCnt < _nElements)
313 {
314 double currFrac = _massFractions.get(elCnt);
315 currFrac += fraction;
316 _massFractions.add(elCnt, currFrac);
317 }
318
319 else
320 {
321 _elements.add(element);
322 _massFractions.add(elCnt, fraction);
323 ++_nElements;
324 }
325 ++_nComponents;
326 }
327 else
328 {
329 throw new RuntimeException("Attempting to add more than declared number of components to material: " + getName());
330 }
331
332 if (isFilled())
333 {
334 checkMassSum();
335 computeDerivedQuantities();
336 }
337 }
338
339
340
341
342
343
344 public void addMaterial(Material material, double fraction)
345 {
346 if (_atoms.size() > 0)
347 {
348 throw new RuntimeException("Material is already defined by atoms: " + getName());
349 }
350
351 if (_nComponents < _nComponentsMax)
352 {
353
354 for (int i = 0, n = material.getNumberOfElements(); i < n; i++ )
355 {
356 MaterialElement element = material.getElements().get(i);
357 int elCnt = 0;
358
359 while ((elCnt < _nElements) && (element != _elements.get(elCnt)))
360 {
361 ++elCnt;
362 }
363
364
365 if (elCnt < _nElements)
366 {
367 double currFrac = _massFractions.get(elCnt);
368 currFrac += fraction * material.getMassFractions().get(i);
369 _massFractions.set(elCnt, currFrac);
370 }
371
372 else
373 {
374
375
376
377 _elements.add(element);
378
379
380 _massFractions.add(elCnt, fraction * material.getMassFractions().get(i));
381 ++_nElements;
382 }
383 }
384 ++_nComponents;
385 }
386 else
387 {
388 throw new RuntimeException("Attempting to add more than allowed umber of components to material: " + getName());
389 }
390
391 if (isFilled())
392 {
393 checkMassSum();
394 computeDerivedQuantities();
395 }
396 }
397
398
399 public double getPressure()
400 {
401 return DEFAULT_PRESSURE;
402 }
403
404
405 public double getTemperature()
406 {
407 return DEFAULT_TEMPERATURE;
408 }
409
410
411
412
413
414 public String toString()
415 {
416 StringBuffer buff = new StringBuffer();
417 buff.append(getName() + "; state=" + _state.toString() + "; Z=" + _Zeff + "; A=" + _Aeff + "; dens=" + _density + "; X0=" + _radiationLength + "; lambda=" + _nuclearInteractionLength);
418
419 for (int i = 0; i < this.getNElements(); i++ )
420 {
421 buff.append("\n\t" + getElements().get(i).getName() + " " + this.getMassFractions().get(i) * 100);
422 }
423
424 return buff.toString();
425 }
426
427
428
429
430
431 protected boolean isFilled()
432 {
433 return (_nComponents == _nComponentsMax);
434 }
435
436
437
438
439
440 private void checkMassSum()
441 {
442 double weightSum = 0;
443 for (int i = 0; i < _massFractions.size(); i++ )
444 {
445 weightSum += _massFractions.get(i);
446 }
447
448 if (abs(1 - weightSum) > 0.001)
449 {
450 throw new RuntimeException("Mass fractions do not sum to 1 within 0.001 tolerance for this material: " + getName());
451 }
452 }
453
454
455
456
457
458 private double computeZeff()
459 {
460 double ZsumNotNorm = 0;
461 double atomCntFracSum = 0;
462
463 int nelem = this.getNElements();
464 for (int i = 0; i < nelem; i++ )
465 {
466 MaterialElement me = this.getElements().get(i);
467 double massFrac = this.getMassFractions().get(i);
468 double atomCntFrac = massFrac / me.getA();
469 ZsumNotNorm += atomCntFrac * me.getZ();
470 atomCntFracSum += atomCntFrac;
471 }
472
473 double ZsumNorm = ZsumNotNorm / atomCntFracSum;
474 _Zeff = ZsumNorm;
475 return _Zeff;
476 }
477
478
479
480
481
482 private double computeAeff()
483 {
484 double AsumNotNorm = 0;
485 double atomCntFracSum = 0;
486
487 int nelem = this.getNElements();
488 for (int i = 0; i < nelem; i++ )
489 {
490 MaterialElement me = this.getElements().get(i);
491 double massFrac = this.getMassFractions().get(i);
492 double atomCntFrac = massFrac / me.getA();
493 AsumNotNorm += atomCntFrac * me.getA();
494 atomCntFracSum += atomCntFrac;
495 }
496 double ZsumNorm = AsumNotNorm / atomCntFracSum;
497 _Aeff = ZsumNorm;
498 return _Aeff;
499 }
500
501
502
503
504 private void computeNuclearInteractionLength()
505 {
506 double NILinv = 0.0;
507
508 for (int i = 0; i < _nElements; i++ )
509 {
510 MaterialElement me = _elements.get(i);
511 NILinv += _massFractions.get(i) / me.getNuclearInteractionLength();
512 }
513
514 _nuclearInteractionLength = (NILinv <= 0.0 ? MAX_LAMBDA : 1.0 / NILinv);
515 _nuclearInteractionLengthWithDensity = _nuclearInteractionLength * _density;
516 }
517
518
519
520
521 private void computeRadiationLength()
522 {
523 double rlinv = 0.0;
524
525 for (int i = 0; i < _nElements; i++ )
526 {
527 MaterialElement me = _elements.get(i);
528 rlinv += _massFractions.get(i) / me.getRadiationLength();
529 }
530
531 _radiationLength = (rlinv <= 0.0 ? MAX_X0 : 1.0 / rlinv);
532 _radiationLengthWithDensity = _radiationLength * _density;
533
534
535
536
537
538
539
540
541
542 }
543
544
545
546
547
548
549 private void computeDerivedQuantities()
550 {
551 computeZeff();
552 computeAeff();
553 computeRadiationLength();
554 computeNuclearInteractionLength();
555 }
556
557
558
559
560 public double getA()
561 {
562 return _Aeff;
563 }
564
565
566
567
568 public double getZ()
569 {
570 return _Zeff;
571 }
572 }