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}