/*-
* Copyright 2015 Diamond Light Source Ltd.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package uk.ac.diamond.scisoft.xpdf;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.january.dataset.Dataset;
import org.eclipse.january.dataset.DatasetFactory;
import org.eclipse.january.dataset.DoubleDataset;
import org.eclipse.january.dataset.Maths;
import com.github.tschoonj.xraylib.Xraylib;
import com.github.tschoonj.xraylib.compoundData;
/**
*
* @author Timothy Spain timothy.spain@diamond.ac.uk
* @since 2015-09-14
*
*/
public class XPDFComposition {
/**
* Map from the atomic numbers present in the compound, to the count of
* atoms present in the fundamental formula
*/
private Map<Integer, Double> atomCount;
private double electronOverlap;
private static String normalNumbers = "0123456789.";
private static String subscriptNumbers = "₀₁₂₃₄₅₆₇₈₉.";
// /**
// * Mean atomic mass of the elements.
// */
// private static final double[] atomicMass = { 1.008664,
// 1.00794,4.002602,6.941,9.012182,10.811,12.011,14.00674,
// 15.9994,18.9984032,20.1797,22.989768,24.305,26.981539,
// 28.0855,30.973762,32.066,35.4527,39.948,39.0983,40.078,
// 44.95591,47.88,50.9415,51.996,54.93805,55.847,58.9332,
// 58.6934,63.546,65.39,69.723,72.61,74.92159,78.96,79.904,
// 83.8,85.4678,87.62,88.90585,91.224,92.90638,95.94,97.9072,
// 101.07,102.9055,106.42,107.8682,112.411,114.818,118.71,
// 121.757,127.6,126.90447,131.29,132.90543,137.327,138.9055,
// 140.115,140.90765,144.24,144.9127,150.36,151.965,157.25,
// 158.92534,162.5,164.93032,167.26,168.93421,173.04,174.967,
// 178.49,180.9479,183.84,186.207,190.23,192.22,195.08,
// 196.96654,200.59,204.3833,207.2,208.98037,208.9824,209.9871,
// 222.0176,223.0185,226.0254,227.0278,232.0381,231.03588,
// 238.0289,237.0482,244.0642,243.0614,247.0703,247.0703,
// 251.0796,252.0816,257.0951
// };
// /**
// * Chemical symbols of the elements.
// */
// @SuppressWarnings("unused")
// private static final String[] elementSymbol = { "n",
// "H","He","Li","Be","B","C","N","O","F","Ne","Na","Mg","Al","Si",
// "P","S","Cl","Ar","K","Ca","Sc","Ti","V","Cr","Mn","Fe","Co",
// "Ni","Cu","Zn","Ga","Ge","As","Se","Br","Kr","Rb","Sr","Y","Zr",
// "Nb","Mo","Tc","Ru","Rh","Pd","Ag","Cd","In","Sn","Sb","Te","I",
// "Xe","Cs","Ba","La","Ce","Pr","Nd","Pm","Sm","Eu","Gd","Tb","Dy",
// "Ho","Er","Tm","Yb","Lu","Hf","Ta","W","Re","Os","Ir","Pt","Au",
// "Hg","Tl","Pb","Bi","Po","At","Rn","Fr","Ra","Ac","Th","Pa","U",
// "Np","Pu","Am","Cm","Bk","Cf","Es","Fm"
// };
// /**
// * English names of the elements.
// */
// @SuppressWarnings("unused")
// private static final String[] elementName = { "neutronium",
// "Hydrogen","Helium","Lithium","Beryllium","Boron","Carbon",
// "Nitrogen","Oxygen","Fluorine","Neon","Sodium","Magnesium",
// "Aluminium","Silicon","Phosphorus","Sulphur","Chlorine","Argon",
// "Potassium","Calcium","Scandium","Titanium","Vanadium","Chromium",
// "Manganese","Iron","Cobalt","Nickel","Copper","Zinc","Gallium",
// "Germanium","Arsenic","Selenium","Bromine","Krypton","Rubidium",
// "Strontium","Yttrium","Zirconium","Niobium","Molybdenum",
// "Technetium","Ruthenium","Rhodium","Palladium","Silver","Cadmium",
// "Indium","Tin","Antimony","Tellurium","Iodine","Xenon","Caesium",
// "Barium","Lanthanum","Cerium","Praseodymium","Neodymium",
// "Promethium","Samarium","Europium","Gadolinium","Terbium",
// "Dysprosium","Holmium","Erbium","Thulium","Ytterbium","Lutetium",
// "Hafnium","Tantalum","Tungsten","Rhenium","Osmium","Iridium",
// "Platinum","Gold","Mercury","Thallium","Lead","Bismuth","Polonium",
// "Astatine","Radon","Francium","Radium","Actinium","Thorium",
// "Protoactinium","Uranium","Neptunium","Plutonium","Americium",
// "Curium","Berkelium","Californium","Einsteinium","Fermium"
// };
/**
* Copy constructor.
*/
public XPDFComposition(XPDFComposition inComp) {
this.atomCount = new HashMap<Integer, Double>(inComp.atomCount);
this.electronOverlap = inComp.electronOverlap;
}
/**
* Construct from a chemical formula.
* @param materialFormula
* The chemical formula of the substance in ASCII
* format (for example, water is H2O, hexane is C6H14)
*/
public XPDFComposition(String materialFormula) {
this.atomCount = new HashMap<Integer, Double>();
if (materialFormula == null || materialFormula.length() == 0)
return;
// replace subscripts with their ASCII equivalents, which look nice, but Xraylib does not like.
String asciified = new String(materialFormula);
for (int i = 0; i < normalNumbers.length(); i++)
asciified = asciified.replace(subscriptNumbers.charAt(i), normalNumbers.charAt(i));
// I keep using hyphens to denote empty compositions, and xraylib keeps not liking them.
asciified = asciified.replace("-", "");
// Get xraylib to do the heavy lifting
compoundData cD = Xraylib.CompoundParser(materialFormula);
// cD has a list of elements and mass fractions
double[] numberFractions = new double[cD.nElements];
double sumNum = 0;
for (int i = 0; i < cD.nElements; i++) {
numberFractions[i] = cD.massFractions[i]/Xraylib.AtomicWeight(cD.Elements[i]);
sumNum += numberFractions[i];
}
// double sumAtoms = 0;
for (int i = 0; i< cD.nElements; i++) {
numberFractions[i] *= cD.nAtomsAll/sumNum;
// sumAtoms += numberFractions[i];
this.atomCount.put(cD.Elements[i], numberFractions[i]);
}
}
/**
* Constructor from a map of atomic number and atom count.
* @param atomCount
* Map of atomic number to count in the substance.
*/
public XPDFComposition(Map<Integer, Double> atomCount) {
this.atomCount = atomCount;
this.electronOverlap = 0.0;
}
/**
* Returns the mean atomic mass of this compound.
* @return mean atomic mass of the compound, averaged over all atoms.
*/
public double getMeanAtomicMass() {
double massSum = 0.0;
double atomSum = 0.0;
for (Map.Entry<Integer, Double> element : atomCount.entrySet()) {
massSum += element.getValue() * Xraylib.AtomicWeight(element.getKey());
atomSum += element.getValue();
}
return massSum/atomSum;
}
public double getFormulaMass() {
double massSum = 0.0;
for (Map.Entry<Integer, Double> element : atomCount.entrySet()) {
massSum += element.getValue() * Xraylib.AtomicWeight(element.getKey());
}
return massSum;
}
/**
* Calculates the composition dependent part of the Krogh-Moe summation.
* @return the part of the Krogh-Moe sum that depends on the composition,
* including the electron overlap.
*/
public double getKroghMoeSummand() {
double KMSum = 0.0;
double atomSum = 0;
for (Map.Entry<Integer, Double> iZN1 : atomCount.entrySet()) {
atomSum += iZN1.getValue();
for (Map.Entry<Integer, Double> iZN2 : atomCount.entrySet()) {
KMSum += iZN1.getValue()*iZN2.getValue()*(iZN1.getKey()*iZN2.getKey() - electronOverlap);
}
}
return KMSum/(atomSum*atomSum);
}
// /**
// * Transforms an ASCII chemical formula into a atomic number to atom count map.
// * @param chemicalFormula
// * A string holding the ASCII chemcial formula.
// * @return the map from atomic number to count of atoms in the compound.
// */
// private static Map<Integer, Integer> mapFormula(String chemicalFormula) {
// // Local class for parsing chemical formulae into a map from atomic number to multiplicity
// return CompoundParser.indexElements(CompoundParser.countAtoms(CompoundParser.tokenize(chemicalFormula)));
// }
/**
* eturns the mass attenuation of the compound at the given energy.
* @param beamEnergy
* The energy of the incident beam in keV.
* @return the mass attenuation in cm²/g
*/
public double getMassAttenuation(double beamEnergy) {
double massAttenuation = 0.0;
double formulaMass = 0.0;
for (Map.Entry<Integer, Double> stoichiometry : atomCount.entrySet()) {
massAttenuation += Xraylib.AtomicWeight(stoichiometry.getKey())*stoichiometry.getValue()*XPDFMassAttenuation.get(beamEnergy, stoichiometry.getKey());
formulaMass += Xraylib.AtomicWeight(stoichiometry.getKey())*stoichiometry.getValue();
}
massAttenuation /= formulaMass;
return massAttenuation;
}
/**
* Calculates the value of g0-1 for the compound.
* @return g0-1
*/
public double getG0Minus1() {
int nSum = 0, dSum = 0;
double aCount = 0;
for (Map.Entry<Integer, Double> iZN : atomCount.entrySet()) {
nSum += iZN.getValue()*iZN.getKey();
dSum += iZN.getValue()*Math.pow(iZN.getKey(), 2);
aCount += iZN.getValue();
}
double g0Minus1 = Math.pow(nSum, 2)/(double) dSum;
// divide by the number of atoms to normalize correctly
g0Minus1 /= aCount;
return g0Minus1;
}
/**
* Return the self-scattering, given the energy of the beam and angles of interest.
* @param coordinates
* the coordinates of the detector, and energy of the beam.
* @return the self-scattering cross-section in units of square classical electron radii.
*/
public Dataset getSelfScattering(XPDFCoordinates coordinates) {
XPDFElectronCrossSections eXSections = new XPDFElectronCrossSections();
eXSections.setCoordinates(coordinates);
// Dataset elasticSelfScattering = Maths.multiply(eXSections.getThomsonCrossSection(), this.getElasticScatteringFactorSquared(coordinates.getX()));
// Dataset inelasticSelfScattering = Maths.multiply(eXSections.getKleinNishinaCrossSection(), this.getInelasticScatteringFactor(coordinates.getX()));
Dataset elasticSelfScattering = Maths.multiply(XPDFElectronCrossSections.getThomsonCrossSection(coordinates), this.getElasticScatteringFactorSquared(coordinates.getX()));
Dataset inelasticSelfScattering = Maths.multiply(XPDFElectronCrossSections.getKleinNishinaCrossSection(coordinates, coordinates.getEnergy()), this.getInelasticScatteringFactor(coordinates.getX()));
return Maths.add(elasticSelfScattering, inelasticSelfScattering);
}
/**
* Calculates the elastic electron scattering of the compound.
* @param x
* sin 2θ/λ
* @return the mass weighted mean elastic scattering form factor.
*/
public Dataset getElasticScatteringFactor(Dataset x) {
Dataset fofx = DatasetFactory.zeros(x, DoubleDataset.class);
double totalAtoms = 0;
for (Map.Entry<Integer, Double> stoichiometry : atomCount.entrySet()) {
fofx.iadd(Maths.multiply(stoichiometry.getValue(), XPDFElementalFormFactors.fofx(stoichiometry.getKey(), x)));
totalAtoms += stoichiometry.getValue();
}
fofx.idivide(totalAtoms);
return fofx;
}
/**
* Calculates the squared elastic electron scattering form factor of the compound.
* @param x
* sin 2θ/λ
* @return the mass weighted mean squared elastic scattering form factor.
*/
public Dataset getElasticScatteringFactorSquared(Dataset x) {
Dataset fsquaredofx = DatasetFactory.zeros(x, DoubleDataset.class);
double totalAtoms = 0;
for (Map.Entry<Integer, Double> stoichiometry : atomCount.entrySet()) {
fsquaredofx.iadd(Maths.multiply(stoichiometry.getValue(), Maths.square(XPDFElementalFormFactors.fofx(stoichiometry.getKey(), x))));
totalAtoms += stoichiometry.getValue();
}
fsquaredofx.idivide(totalAtoms);
return fsquaredofx;
}
/**
* Calculates the inelastic electron scattering form factor of the compound.
* @param x
* sin 2θ/λ
* @return the mass weighted mean inelastic scattering form factor.
*/
public Dataset getInelasticScatteringFactor(Dataset x) {
Dataset Sofx = DatasetFactory.zeros(x, DoubleDataset.class);
double totalAtoms = 0;
for (Map.Entry<Integer, Double> stoichiometry : atomCount.entrySet()) {
Sofx.iadd(Maths.multiply(stoichiometry.getValue(), XPDFElementalFormFactors.sofx(stoichiometry.getKey(), x)));
totalAtoms += stoichiometry.getValue();
}
Sofx.idivide(totalAtoms);
return Sofx;
}
/**
* Determines whether two compositions are similar enough to count as equal
* for the purposes of absorption.
* @param inCompo
* the composition to compare with this
* @return are the two compositions equivalent?
*/
public boolean isEqualToForAbsorption(XPDFComposition inCompo) {
if (inCompo == null)
return false;
final double maximumDifference = 1e-6;
// check there are the same number of elements in each compound
if (atomCount.size() != inCompo.atomCount.size())
return false;
// Check all elements present in this are present in inCompo and vice versa
if (!inCompo.atomCount.keySet().containsAll(atomCount.keySet()) ||
!atomCount.keySet().containsAll(inCompo.atomCount.keySet()))
return false;
for (Map.Entry<Integer, Double> entry : atomCount.entrySet())
if (entry.getValue() - inCompo.atomCount.get(entry.getKey()) > maximumDifference)
return false;
return true;
}
/**
* Returns the fraction of atoms with in the compound with atomic number z.
* <p>
* Given an atomic number, z, return the fraction of atoms are of this
* element. If z is not part of the composition, then return 0.
* @param z
* the atomic number to be queried.
* @return the fraction of atoms with this atomic number.
*/
public double atomFraction(int z) {
if (atomCount.containsKey(z)) {
double num = atomCount.get(z);
double den = 0;
for (double nZ : atomCount.values())
den += nZ;
return num/den;
} else
return 0.0;
}
/**
* Weights the composition by a constant factor
* @param weight
* the weigthing factor to apply
*/
public void weight(double weight) {
for (Map.Entry<Integer, Double> entry : atomCount.entrySet()) {
atomCount.put(entry.getKey(), entry.getValue()*weight);
}
}
/**
* Adds all the atoms of another composition to this one.
* @param compo2
* the composition whose atoms to add
*/
public void add(XPDFComposition compo2) {
for (Map.Entry<Integer, Double> entry : compo2.atomCount.entrySet()) {
if (atomCount.containsKey(entry.getKey()))
atomCount.put(entry.getKey(), atomCount.get(entry.getKey()) + entry.getValue());
else
atomCount.put(entry.getKey(), entry.getValue());
}
}
@Override
public String toString() {
return getHallNotation(true);
}
/**
* Returns a list of data about the strongest x-ray fluorescences.
* @param energy
* energy of the exciting beam in keV.
* @param nLines
* the maximum number of lines to return.
* @return the fluorescence data as a List of {@link XPDFFluorescentLine}s.
*/
public List<XPDFFluorescentLine> getFluorescences(double energy, int nLines) {
// TODO: xraylib fluorescence lines
List<XPDFFluorescentLine> fluorescences = new ArrayList<XPDFFluorescentLine>();
// int[] lineIndices = new int[] {Xraylib.KA1_LINE, Xraylib.KA2_LINE, Xraylib.KB1_LINE, Xraylib.LA1_LINE, Xraylib.LA2_LINE};
// Loop over elements present
for (int z : atomCount.keySet()) {
// Loop over fluorescence line indices
for (int line : getLineIndices(z)) {
double lineEnergy = Xraylib.LineEnergy(z, line);
double lineBarns = Xraylib.CSb_FluorLine_Kissel_Cascade(z, line, energy);
final double minEnergy = 1.0;
final double minBarns = 10.0;
if (lineEnergy > minEnergy && lineBarns > minBarns)
fluorescences.add(new XPDFFluorescentLine(lineEnergy, lineBarns, z));
}
}
// fluorescences.clear();
//
// // Beginning of hard-coded data
// if (atomCount.keySet().size() == 2 &&
// atomCount.containsKey(58) && atomCount.get(58) == 1 &&
// atomCount.containsKey(8) && atomCount.get(8) == 2)
// // ceria, CeO2
// fluorescences = Arrays.asList(
// new XPDFFluorescentLine(34.7196, 444.94994401, 58),
// new XPDFFluorescentLine(34.2788, 243.00829748, 58),
// new XPDFFluorescentLine(39.2576, 87.69485581, 58),
// new XPDFFluorescentLine(4.8401, 48.11886857, 58),
// new XPDFFluorescentLine(5.2629, 28.32794357, 58));
//
// else if (atomCount.keySet().size() == 3 &&
// atomCount.containsKey(56) && atomCount.get(56) == 1 &&
// atomCount.containsKey(22) && atomCount.get(22) == 1 &&
// atomCount.containsKey(8) && atomCount.get(8) == 3)
// // barium titanate, BaTiO3
// fluorescences = Arrays.asList(
// new XPDFFluorescentLine(32.1936, 388.27213844, 56),
// new XPDFFluorescentLine(31.817, 210.42217958, 56),
// new XPDFFluorescentLine(36.378, 75.39646264, 56),
// new XPDFFluorescentLine(4.4663, 37.42569601, 56),
// new XPDFFluorescentLine(4.8275, 21.92040699, 56));
//
// else if (atomCount.keySet().size() == 1 &&
// atomCount.containsKey(74))
// // tungsten, W
// fluorescences = Arrays.asList(
// new XPDFFluorescentLine(59.3182, 1019.37506384, 74),
// new XPDFFluorescentLine(57.981, 586.69404138, 74),
// new XPDFFluorescentLine(67.244, 219.28240133, 74),
// new XPDFFluorescentLine(8.3976, 239.26301778, 74),
// new XPDFFluorescentLine(9.6724, 156.7324558, 74));
//
// else if (atomCount.keySet().size() == 1 &&
// atomCount.containsKey(28))
// // nickel, Ni
// fluorescences = Arrays.asList(
// new XPDFFluorescentLine(7.4781, 12.68440462, 28));
//
// else
// fluorescences.clear();
//
// // end of hard-coded data
return fluorescences;
}
private List<Integer> getLineIndices(int z) {
List<Integer> lineIndices = new ArrayList<Integer>();//Arrays.asList(new Integer[] {Xraylib.KA1_LINE, Xraylib.KA2_LINE, Xraylib.KB1_LINE, Xraylib.LA1_LINE, Xraylib.LA2_LINE});
if (z > 4)
lineIndices.add(Xraylib.KA2_LINE);
if (z > 6)
lineIndices.add(Xraylib.KA1_LINE);
if (z > 14)
lineIndices.add(Xraylib.KB1_LINE);
if (z > 22)
lineIndices.add(Xraylib.LB1_LINE);
if (z > 24)
lineIndices.add(Xraylib.LA1_LINE);
return lineIndices;
}
public double getPhotoionizationAttenuation(double beamEnergy) {
double massAttenuation = 0.0;
double formulaMass = 0.0;
for (Map.Entry<Integer, Double> stoichiometry : atomCount.entrySet()) {
massAttenuation += Xraylib.AtomicWeight(stoichiometry.getKey())*stoichiometry.getValue()*XPDFMassAttenuation.getPhoto(beamEnergy, stoichiometry.getKey());
formulaMass += Xraylib.AtomicWeight(stoichiometry.getKey())*stoichiometry.getValue();
}
massAttenuation /= formulaMass;
return massAttenuation;
}
/**
* Returns the chemical formula of this phase.
* <p>
* Returns the chemical formula of this phase in Hall notation.
* @return chemical formula in Hall notation.
*/
public String getHallNotation(boolean useSubscripts) {
final int nElements = 100; // Up to and including fermium
final String[] elementSymbol = { "n",
"H","He","Li","Be","B","C","N","O","F","Ne",
"Na","Mg","Al","Si","P","S","Cl","Ar","K","Ca",
"Sc","Ti","V","Cr","Mn","Fe","Co","Ni","Cu","Zn",
"Ga","Ge","As","Se","Br","Kr","Rb","Sr","Y","Zr",
"Nb","Mo","Tc","Ru","Rh","Pd","Ag","Cd","In","Sn",
"Sb","Te","I","Xe","Cs","Ba","La","Ce","Pr","Nd",
"Pm","Sm","Eu","Gd","Tb","Dy","Ho","Er","Tm","Yb",
"Lu","Hf","Ta","W","Re","Os","Ir","Pt","Au","Hg",
"Tl","Pb","Bi","Po","At","Rn","Fr","Ra","Ac","Th",
"Pa","U","Np","Pu","Am","Cm","Bk","Cf","Es","Fm"};
final int[] alphabeticElements = {
89, 47, 13, 95, 18, 33, 85, 79, 5, 56,
4, 83, 97, 35, 6, 20, 48, 58, 98, 17,
96, 27, 24, 55, 29, 66, 68, 99, 63, 9,
26, 100, 87, 31, 64, 32, 1, 2, 72, 80,
67, 53, 49, 77, 19, 36, 57, 3, 71, 12,
25, 42, 7, 11, 41, 60, 10, 28, 93, 8,
76, 15, 91, 82, 46, 61, 84, 59, 78, 94,
88, 37, 75, 45, 86, 44, 16, 51, 21, 34,
14, 62, 50, 38, 73, 65, 43, 52, 90, 22,
81, 69, 92, 23, 74, 54, 39, 70, 30, 40
};
String hall = "";
Map<Integer, Double> atomCount = new HashMap<Integer, Double>(this.atomCount);
// Do the special case for organic compounds
if (atomCount.containsKey(6)) {
hall += elementSymbol[6] + prettifyDouble(atomCount.get(6), useSubscripts);
atomCount.remove(6);
if (atomCount.containsKey(1)) {
hall += elementSymbol[1] + prettifyDouble(atomCount.get(1), useSubscripts);
atomCount.remove(1);
}
}
// Do all the remaining elements in order
for (int i = 0; i < nElements; i++) {
int z = alphabeticElements[i];
if (atomCount.containsKey(z))
hall += elementSymbol[z] + prettifyDouble(atomCount.get(z), useSubscripts);
}
return (hall.length() != 0) ? hall : "-";
}
// Format the atom multiplicity as nicely as we can
private String prettifyDouble(double n, boolean useSubscripts) {
if (n == 1.0)
return "";
// round to the nearest 1/1024 (can this be done in the DecimalFormat?)
double factor = 1024.0;
double nr = Math.round(factor*n)/factor;
DecimalFormat formattor = new DecimalFormat( (nr == (double) Math.round(nr)) ? "0" : "0.000");
String number = formattor.format(nr);
if ("1".equals(number)) number = "";
if (useSubscripts)
for (int i = 0; i < normalNumbers.length(); i++ )
number = number.replace(normalNumbers.charAt(i), subscriptNumbers.charAt(i));
return number;
}
}
///**
// * Private class to do the chemical formula parsing.
// * @author Timothy Spain timothy.spain@diamond.ac.uk
// * @since 2015-09-14
// *
// */
//class CompoundParser {
//
// /**
// * Static list of element names.
// * <p>
// * Names of the elements as an unmodifiable list. The neutron is added as
// * element 0, so that index equals atomic number
// */
// private static List<String> elements = Collections.unmodifiableList(Arrays
// .asList("n",
// "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na",
// "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", "Sc",
// "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga",
// "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb",
// "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn", "Sb",
// "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd", "Pm",
// "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu",
// "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl",
// "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th", "Pa",
// "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm"));
//
//
// /**
// * RL tokenization of the compound formula
// * @param compoundIn
// * Compound chemical formula, presented in ASCII (for
// * example water is H2O, glucose is C6H12O6)
// * @return the list of tokens, comprising element names, brackets and multiplicities.
// */
// public static List<String> tokenize(String compoundIn) {
//// List<String> fakeTok = Arrays.asList( CompoundParser.elements.get(1) );
// //return fakeTok;
// List<String> tokens = new Vector<String>();
//
// if (compoundIn != null) {
// // Read a character from the end of the String
// while (compoundIn.length() > 0) {
// int charRemove = 0;
// String tok = compoundIn.substring(compoundIn.length() - 1);
//
// // Characterise the character in the string. Digits and lower
// // case letters are special cases, otherwise a single character
// // is a token (caps and parens)
// if (Character.isDigit(tok.charAt(0))) {
// // Get all the characters from the end of the String until the next non-digit
// String digits = "0123456789";
// charRemove = compoundIn.length() - CompoundParser.lastIndexOfAnyBut(compoundIn, digits) - 1;
// } else if (Character.isLowerCase(tok.charAt(0))) {
// charRemove = 2;
// } else {
// charRemove = 1;
// }
// tokens.add(0, compoundIn.substring(compoundIn.length() - charRemove , compoundIn.length()));
// compoundIn = compoundIn.substring(0, compoundIn.length() - charRemove);
// }
// }
// return tokens;
// }
//
//
// /**
// * Returns the index of the last character that does not appear in the search string.
// * <p>
// * Takes a valid string, and return the last index of all the
// * characters that do not occur in the search string.
// * @param str
// * string to be interrogated.
// * @param searchChars
// * string of the characters to be ignored.
// * @return
// * Index of the last character tha does not occur in the search string
// */
// private static int lastIndexOfAnyBut(String str, String searchChars) {
//
// int lastInd;
// for (lastInd= str.length()-1; (lastInd >= 0) && (searchChars.indexOf(str.charAt(lastInd)) != -1) ; --lastInd)
// ;
//
// return lastInd;
// }
//
//
// /**
// * Returns a map from elements symbols to counts of atoms.
// * @param tokens
// * the ASCII chemical formula represented as a list of tokens.
// * @return map from chemical symbol to multiplicity of atoms in the compound.
// */
// public static Map<String, Integer> countAtoms(List<String> tokens) {
//
// Map<String, Integer> atomCount = new HashMap<String, Integer>();
//
// // Read from the end of the list of tokens. If a number is encountered,
// // push a number and a non-number. If a non-number is encountered, push
// // 1 and the non-number
//
// Stack<Integer> mults = new Stack<Integer>();
// mults.push(1);
//
// while (tokens.size() > 0) {
// String toke = tokens.get(tokens.size()-1);
// if (toke.equals("(")) {
// mults.pop();
// tokens.remove(tokens.size()-1);
// continue;
// } else if ("0123456789".indexOf(toke.substring(toke.length()-1)) != -1) {
// mults.push(Integer.parseInt(toke)*mults.peek());
// tokens.remove(tokens.size()-1);
// toke = tokens.get(tokens.size()-1);
// } else {
// mults.push(1*mults.peek());
// }
//
// // close paren: Remove the token. Cycle
// if (!toke.equals(")")) {
// // element. Add the atoms to the accumulator. pop the last
// // number. Remove the token. Cycle
// if (atomCount.containsKey(toke)) {
// int oldCount = atomCount.get(toke);
// atomCount.put(toke, oldCount+mults.peek());
// } else {
// atomCount.put(toke, mults.peek());
// }
// mults.pop();
// }
// tokens.remove(tokens.size()-1);
// }
//
// return atomCount;
// }
//
// /**
// * Converts a map of element symbols-to-multiplicities to a map of atomic number-to-multiplicities.
// * @param siMap
// * map of element symbols to multiplicities.
// * @return map of atomic number-to-multiplicities.
// */
// public static Map<Integer, Integer> indexElements(Map<String, Integer> siMap) {
//
// Map<Integer, Integer> iMap = new HashMap<Integer, Integer>();
//
// for (String eleSym : siMap.keySet()) {
// int zed = elements.indexOf(eleSym);
// iMap.put(zed, siMap.get(eleSym));
// }
//
// return iMap;
// }
//
//}