View Javadoc

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    * This class implements the @see IMaterialMixture interface. 
10   * 
11   * @author Jeremy McCormick <jeremym@slac.stanford.edu>
12   * @version $Id: MaterialMixture.java,v 1.7 2010/04/14 18:24:53 jeremy Exp $
13   *
14   */
15  public class MaterialMixture 
16  extends MaterialElement
17  {    
18      // Max radiation length.
19      public static final double MAX_RADIATION_LENGTH = java.lang.Double.MAX_VALUE;
20      
21      // Max interaction length.
22      public static final double MAX_NUCLEAR_INTERACTION_LENGTH = java.lang.Double.MAX_VALUE;
23               
24      // Effective, computed Z value.
25      private double Zeff;
26      
27      // Effective, computed A value.
28      private double Aeff;
29      
30      // Number of components added.
31      private int nComponentsAdded=0;
32      
33      // Number of components that are allowed to be added.
34      private int nComponentsMax=0;
35      
36      // Number of elements in the mass fraction list.
37      private int nElements=0;
38      
39      // The list of elements making up the mixture.
40      private List<MaterialElement> elementList = new ArrayList<MaterialElement>();
41      
42      // List of mass fractions.
43      private List<Double> massFractionList = new ArrayList<Double>();
44      
45      // Number of atoms of each element.
46      private List<Integer> atomCountList = new ArrayList<Integer>();      
47      
48      /** Construct a material with a number of components. */
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      /** @return maximum number of 1st-level (non-recursive) components that can be defined */
78      public int getNComponentsMax()
79      {
80          return nComponentsMax;
81      }
82      
83      /** @return number of components defined so far in this material */
84      public int getNComponents()
85      {
86          return nComponentsAdded;
87      }
88      
89      /** @return number of elements defined in this material */
90      public int getNElements()
91      {
92          return nElements;
93      }
94      
95      /** @return lambda (nuclear interaction length) based on mass fractions of elements */
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     /** compute X0 (radiation length) based on mass fractions of elements */
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     /** Caches computed quantities after all components have been added to the material. */
130     private void computeDerivedQuantities()
131     {                
132         computeZeff();        
133         computeAeff();        
134         //computeEffectiveNumberOfNucleons();
135         //computeMolecularWeigth();
136         computeRadiationLengthForMixture();
137         computeNuclearInteractionLengthForMixture();
138         computeCriticalEnergy();
139         computeMoliereRadius();    			    
140     }    
141     
142     /** @return total number of (unique) elements in the element list for this material */
143     public int getNumberOfElements()
144     {
145         return nElements;
146     }
147     
148     /** @return a list of elements referenced by this material */
149     public List<MaterialElement> getElements()
150     {
151         return elementList;
152     }
153     
154     /** @return a list of atom counts for each element */
155     public List<Integer> getAtomCounts()
156     {
157     	return atomCountList;
158     }
159     
160     /** @return corresponding mass fractions for each element in the material */
161     public List<Double> getMassFractions()
162     {
163         return massFractionList;
164     }
165     
166     /**
167      * Add an element to the material by atom count.
168      *
169      * Based on Geant4's G4Material::AddElement() .
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      * Add element to the material by fraction of mass. (out of 1.0)
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             /* Element already exists.  Increase the mass fraction. */
218             if (elCnt < nElements)
219             {
220                 double currFrac = massFractionList.get(elCnt);
221                 currFrac += fraction;
222                 massFractionList.add(elCnt, currFrac);
223             }
224             /* Element does not exist.  Add a new mass fraction. */
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     /** Add material by fraction of mass. */
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             /* Loop over elements in the material. */
258             for ( int i = 0; i < material.getNumberOfElements(); i++)
259             {
260                 MaterialElement element = material.getElements().get(i);
261                 int elCnt = 0;
262                 /* Find the element. */
263                 while ((elCnt < nElements) && (element != elementList.get(elCnt)))
264                 {
265                     ++elCnt;
266                 }
267                 
268                 /* Add fraction to existing element. */
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                 /* Add a new element. */
276                 else
277                 {
278                     //System.out.println("adding mass fraction: " + element.getName() + "=" + fraction * material.getMassFractions().get(i) );
279                     
280                     elementList.add(element);
281                     
282                     /**
283                      * Add the mass fraction of this element, scaled by its percentage in the material's
284                      * mass fraction list.
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     /** @return true if all 1st-level (non-recursive) component elements and materials have been added */
305     public boolean isFilled()
306     {
307         return (nComponentsAdded == nComponentsMax);
308     }
309     
310     /** Check that the massFractions list adds up to 1.0 */
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     /** Compute effective Z for this material using element list. */
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     /** Compute effective A for this material using element list. */
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     /** Translate this material to human-readable string. */
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 }