001package com.hfg.chem;
002
003
004import java.io.BufferedReader;
005import java.io.IOException;
006import java.io.InputStream;
007import java.io.InputStreamReader;
008import java.util.HashMap;
009import java.util.Map;
010import java.util.regex.Pattern;
011
012import com.hfg.exception.ProgrammingException;
013import com.hfg.util.StringUtil;
014import com.hfg.util.io.CSV;
015import com.hfg.util.io.StreamUtil;
016
017//------------------------------------------------------------------------------
018/**
019 Isotope. Data from NIST.
020 <div>
021 @author J. Alex Taylor, hairyfatguy.com
022 </div>
023 */
024//------------------------------------------------------------------------------
025// com.hfg XML/HTML Coding Library
026//
027// This library is free software; you can redistribute it and/or
028// modify it under the terms of the GNU Lesser General Public
029// License as published by the Free Software Foundation; either
030// version 2.1 of the License, or (at your option) any later version.
031//
032// This library is distributed in the hope that it will be useful,
033// but WITHOUT ANY WARRANTY; without even the implied warranty of
034// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
035// Lesser General Public License for more details.
036//
037// You should have received a copy of the GNU Lesser General Public
038// License along with this library; if not, write to the Free Software
039// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
040//
041// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
042// jataylor@hairyfatguy.com
043//------------------------------------------------------------------------------
044
045public class Isotope extends Element
046{
047   //##########################################################################
048   // PRIVATE FIELDS
049   //##########################################################################
050
051   private Element mElement;
052   private int     mMassNum; // The number of protons plus the number of neutrons
053   private Double  mIsotopicComposition;
054
055   // This declaration has to come before the public constants below.
056   private static Map<Element, Map<Integer, Isotope>> sValueMap = new HashMap<>();
057
058   static
059   {
060      init();
061   }
062
063   //##########################################################################
064   // CONSTRUCTORS
065   //##########################################################################
066
067   //--------------------------------------------------------------------------
068   private Isotope(String inName, Element inElement, int inMassNum,
069                   double inMonoMass, double inIsotopicComposition)
070   {
071      this(inName, inElement, inMassNum, inMonoMass);
072      setIsotopicComposition(inIsotopicComposition);
073   }
074
075   //--------------------------------------------------------------------------
076   private Isotope(String inName, Element inElement, int inMassNum,
077                   double inMonoMass)
078   {
079      super(inName, inMassNum + inElement.getSymbol(), inElement.getAtomicNum(), inMonoMass, inMonoMass);
080      mElement = inElement;
081      mMassNum = inMassNum;
082
083      Map<Integer, Isotope> isotopeMap = sValueMap.get(inElement);
084      if (null == isotopeMap)
085      {
086         isotopeMap = new HashMap<>(5);
087         sValueMap.put(inElement, isotopeMap);
088      }
089
090      isotopeMap.put(inMassNum, this);
091   }
092
093   //##########################################################################
094   // PUBLIC METHODS
095   //##########################################################################
096
097   //--------------------------------------------------------------------------
098   public static Map<Integer, Isotope> valuesForElement(Element inElement)
099   {
100      return sValueMap.get(inElement);
101   }
102
103   //--------------------------------------------------------------------------
104   public static Isotope valueOf(Element inElement, int inMassNum)
105   {
106      return sValueMap.get(inElement).get(inMassNum);
107   }
108
109   //--------------------------------------------------------------------------
110   public Element getElement()
111   {
112      return mElement;
113   }
114
115   //--------------------------------------------------------------------------
116   /**
117    Returns the mass number (the number of protons plus neutrons) of the isotope.
118    @return the mass number (the number of protons plus neutrons) of the isotope
119    */
120   public int getMassNumber()
121   {
122      return mMassNum;
123   }
124
125   //--------------------------------------------------------------------------
126   @Override
127   public String getSymbol()
128   {
129      return getMassNumber() + getElement().getSymbol();
130   }
131
132   //--------------------------------------------------------------------------
133   /**
134    Specifies the isotopic composition (mole fraction) for the isotope.
135    * @return this Isotope object to enable method chaining
136    */
137   private Isotope setIsotopicComposition(double inValue)
138   {
139      mIsotopicComposition = inValue;
140      return this;
141   }
142
143   //--------------------------------------------------------------------------
144   /**
145    Specifies the isotopic composition (mole fraction) for the isotope.
146    * @return the isotopic composition (mole fraction) for the isotope
147    */
148   public Double getIsotopicComposition()
149   {
150      return mIsotopicComposition;
151   }
152
153   //##########################################################################
154   // PRIVATE METHODS
155   //##########################################################################
156
157   //--------------------------------------------------------------------------
158   private static void init()
159   {
160      Pattern noDataLinePattern = Pattern.compile("[\\s\u00A0,]+");
161
162      String rsrc = "rsrc/isotope_data.csv";
163      InputStream stream = Isotope.class.getResourceAsStream(rsrc);
164      if (null == stream)
165      {
166         throw new ProgrammingException("The rsrc " + rsrc + " couldn't be found!?");
167      }
168
169      BufferedReader reader = null;
170      try
171      {
172         reader = new BufferedReader(new InputStreamReader(stream));
173         String line;
174         int lineNum = 0;
175         Element currentElement = null;
176         while ((line = reader.readLine()) != null)
177         {
178            lineNum++;
179
180            if (!StringUtil.isSet(line) // Skip blank lines
181                  || 1 == lineNum // Skip the header line
182                || line.startsWith("#") // Skip comment lines
183                || noDataLinePattern.matcher(line).matches()) // Skip lines with all empty fields
184            {
185               continue;
186            }
187
188            String[] fields = CSV.parseLine(line);
189            if (StringUtil.isSet(fields[0]))
190            {
191               currentElement = Element.valueOf(Integer.parseInt(fields[0].trim()));
192            }
193
194            String monoMassString = StringUtil.removeWhitespace(fields[3]);
195            int uncertaintyIndex = monoMassString.indexOf("(");
196            if (uncertaintyIndex > 0)
197            {
198               monoMassString = monoMassString.substring(0, uncertaintyIndex);
199            }
200
201            String isotopicCompostionString = StringUtil.removeWhitespace(fields[4]);
202            if (StringUtil.isSet(isotopicCompostionString))
203            {
204               uncertaintyIndex = isotopicCompostionString.indexOf("(");
205               if (uncertaintyIndex > 0)
206               {
207                  isotopicCompostionString = isotopicCompostionString.substring(0, uncertaintyIndex);
208               }
209
210               new Isotope(currentElement.getName() + " " + fields[2], currentElement, Integer.parseInt(fields[2]), Double.parseDouble(monoMassString), Double.parseDouble(isotopicCompostionString));
211            }
212            else
213            {
214               new Isotope(currentElement.getName() + " " + fields[2], currentElement, Integer.parseInt(fields[2]), Double.parseDouble(monoMassString));
215            }
216         }
217      }
218      catch (IOException e)
219      {
220         throw new RuntimeException("Problem initializing isotope data!", e);
221      }
222      finally
223      {
224         StreamUtil.close(reader);
225      }
226   }
227}