gda.util.exafs.Element.java Source code

Java tutorial

Introduction

Here is the source code for gda.util.exafs.Element.java

Source

/*-
 * Copyright  2009 Diamond Light Source Ltd.
 *
 * This file is part of GDA.
 *
 * GDA is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License version 3 as published by the Free
 * Software Foundation.
 *
 * GDA is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along
 * with GDA. If not, see <http://www.gnu.org/licenses/>.
 */

package gda.util.exafs;

import gda.jscience.physics.quantities.PhotonEnergy;
import gda.jscience.physics.units.NonSIext;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.apache.commons.lang.ArrayUtils;
import org.jscience.physics.quantities.Energy;
import org.jscience.physics.quantities.Quantity;
import org.jscience.physics.units.NonSI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import uk.ac.gda.util.io.TokenFileParser;

/**
 * To provide information about the chemical elements.
 */
public final class Element {

    private static Logger logger = LoggerFactory.getLogger(Element.class);

    final static Integer NUMBER_OF_ELEMENTS = 103;

    /**
     * The Core Holes are being read from a tab separated text file as the old values in this class were wrong and
     * difficult to check and maintain.
     */
    /**
     * K edge Core-hole levels in eV
     */
    private static Double[] kCoreHoles, l1CoreHoles, l2CoreHoles, l3CoreHoles, m1CoreHoles, m2CoreHoles,
            m3CoreHoles, m4CoreHoles, m5CoreHoles;
    static {
        try {
            final TokenFileParser p = new TokenFileParser(Element.class.getResource("Element-CoreHole.txt"));
            p.parse();
            kCoreHoles = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 2);
            l1CoreHoles = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 3);
            l2CoreHoles = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 4);
            l3CoreHoles = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 5);

            m1CoreHoles = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 6);
            m2CoreHoles = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 7);
            m3CoreHoles = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 8);
            m4CoreHoles = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 9);
            m5CoreHoles = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 10);

        } catch (Throwable e) {
            logger.error("Failed to read Element-CoreHole.txt file. All core holes will be null!", e);
        }
    }
    private static Double[][] coreHoles = { kCoreHoles, l1CoreHoles, l2CoreHoles, l3CoreHoles, m1CoreHoles,
            m2CoreHoles, m3CoreHoles, m4CoreHoles, m5CoreHoles };

    static Double[] setArrayFromFileParser(int maxAtomicNumber, Double defaultValue, TokenFileParser p,
            int column) {
        Double[] arrayToSet = new Double[maxAtomicNumber];
        Arrays.fill(arrayToSet, defaultValue);

        List<String> atomicNumberFromFile = p.getColumn(0);
        List<String> valuesFromFile = p.getColumn(column);
        for (int i = 0; i < atomicNumberFromFile.size(); i++) {
            // ignore first line
            if (i == 0)
                continue;
            int atomicNumber = Integer.parseInt(atomicNumberFromFile.get(i));
            if (atomicNumber <= maxAtomicNumber)
                arrayToSet[atomicNumber - 1] = Double.parseDouble(valuesFromFile.get(i));
        }
        return arrayToSet;
    }

    private static Double[] kEdgeEnergies, l1EdgeEnergies, l2EdgeEnergies, l3EdgeEnergies, m1EdgeEnergies,
            m2EdgeEnergies, m3EdgeEnergies, m4EdgeEnergies, m5EdgeEnergies;
    static {
        try {
            final TokenFileParser p = new TokenFileParser(Element.class.getResource("Element-Edge.txt"));
            p.parse();
            kEdgeEnergies = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 2);
            l1EdgeEnergies = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 3);
            l2EdgeEnergies = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 4);
            l3EdgeEnergies = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 5);
            m1EdgeEnergies = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 6);
            m2EdgeEnergies = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 7);
            m3EdgeEnergies = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 8);
            m4EdgeEnergies = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 9);
            m5EdgeEnergies = setArrayFromFileParser(NUMBER_OF_ELEMENTS, 0., p, 10);

        } catch (Throwable e) {
            logger.error("Failed to read Element-Edge.txt file. All core holes will be null!", e);
        }
    }
    private static Double[][] edgeEnergies = { kEdgeEnergies, l1EdgeEnergies, l2EdgeEnergies, l3EdgeEnergies,
            m1EdgeEnergies, m2EdgeEnergies, m3EdgeEnergies, m4EdgeEnergies, m5EdgeEnergies };

    /**
     * 
     */
    public static final int METAL = 0;

    /**
     * 
     */
    public static final int NONMETAL = 1;

    /**
     * 
     */
    public static final int METALLOID = 2;

    /**
     * 
     */
    public static final int NOBLEGAS = 3;

    /**
     * 
     */
    public static final int LANTHANIDE = 4;

    /**
     * 
     */
    public static final int ACTINIDE = 5;

    // private static final int KEDGE = 0;
    // private static final int L1EDGE = 1;
    // private static final int L2EDGE = 2;
    // private static final int L3EDGE = 3;

    private static final String[] edgeNames = { "K", "L1", "L2", "L3", "M1", "M2", "M3", "M4", "M5" };

    private static Map<String, Element> ALL_ELEMENTS;

    /* The only instance field of an Element is an integer */
    /* which is used to look up values in the static tables. */
    /* If Elements become widely used it might be more */
    /* efficient to add instance fields and extract the values */
    /* at construction. */
    /* Array positions are always (atomic number - 1) */

    private final int arrayRef;

    /* Static initializer creates an array of instances, one */
    /* for each real element */

    private static void initElements() {
        if (ALL_ELEMENTS != null) {
            return;
        }
        final Map<String, Element> tmp = new LinkedHashMap<String, Element>(NUMBER_OF_ELEMENTS);
        for (int i = 1; i <= NUMBER_OF_ELEMENTS; i++) {
            final Element ele = new Element(i);
            tmp.put(symbols[i - 1], ele);
        }
        ALL_ELEMENTS = Collections.unmodifiableMap(tmp);
    }

    /**
     * Constructs an Element from its atomic number. The constructor is private because a static array of all the
     * elements is created in the static initializer.
     * 
     * @param atomicNumber
     *            the atomic number in periodic table
     */
    private Element(int atomicNumber) {
        arrayRef = atomicNumber - 1;
    }

    /**
     * Returns the Collection<Element> of all elements in order of atomic number.
     * 
     * @return the Collection<Element> of all elements
     */
    public static Collection<Element> getAllElements() {
        initElements();
        return new ArrayList<Element>(ALL_ELEMENTS.values());
    }

    /**
     * @param from
     * @param to
     * @return Elements in range
     */
    public static Collection<Element> getElements(final int from, final int to) {
        initElements();
        final Collection<Element> ret = new HashSet<Element>((to - from) + 1);
        for (int i = from; i <= to; i++) {
            ret.add(ALL_ELEMENTS.get(symbols[i]));
        }
        return ret;
    }

    /**
     * Gets the edge name "K" etc which corresponds to the given integer value.
     * 
     * @param edge
     *            integer edge value
     * @return the edge name corresponding to the given integer value
     */
    public static String edgeName(int edge) {
        return (edgeNames[edge]);
    }

    /**
     * Gets the atomic number of the element
     * 
     * @return atomic number
     */
    public int getAtomicNumber() {
        return arrayRef + 1;
    }

    /**
     * Gets the symbol of the element
     * 
     * @return element symbol
     */
    public String getSymbol() {
        return symbols[arrayRef];
    }

    /**
     * Gets the name of the element
     * 
     * @return element name
     */
    public String getName() {
        return names[arrayRef];
    }

    /**
     * Gets the type of element (METAL etc)
     * 
     * @return element type
     */
    public int getType() {
        return types[arrayRef];
    }

    /**
     * Gets the atomic radius of the element
     * 
     * @return atomic radius
     */
    public double getAtomicRadius() {
        return atomicRadii[arrayRef];
    }

    /**
     * Gets the covalent radius of the element
     * 
     * @return covalent radius
     */
    public double getCovalentRadius() {
        return covalentRadii[arrayRef];
    }

    /**
     * Gets the atomic mass of the element
     * 
     * @return atomic mass
     */
    public double getAtomicMass() {
        return masses[arrayRef];
    }

    /**
     * Gets the boiling point of the element
     * 
     * @return boiling point
     */
    public double getBoilingPT() {
        return boilingPTs[arrayRef];
    }

    /**
     * Gets the melting point of the element
     * 
     * @return melting point
     */
    public double getMeltingPT() {
        return meltingPTs[arrayRef];
    }

    /**
     * Gets the density of the element
     * 
     * @return density
     */
    public double getDensity() {
        return densities[arrayRef];
    }

    /**
     * Gets the atomic volume of the element
     * 
     * @return atomic volume
     */
    public double getAtomicVolume() {
        return volumes[arrayRef];
    }

    /**
     * Returns the iterator of a vector of the names of the edges which are in the given energy range.
     * 
     * @param minEnergy
     *            the beginning of the energy range in eV
     * @param maxEnergy
     *            the end of the energy range in eV
     * @return Vector<String of names
     */
    public Iterator<String> getEdgesInEnergyRange(double minEnergy, double maxEnergy) {
        List<String> edges = getListOfEdgesInEnergyRange(minEnergy, maxEnergy);
        return (edges.isEmpty()) ? null : edges.iterator();
    }

    /**
     * Returns of the names of the edges which are in the given energy range.
     * 
     * @param minEnergy the beginning of the energy range in eV
     * @param maxEnergy the end of the energy range in eV
     */
    public List<String> getListOfEdgesInEnergyRange(double minEnergy, double maxEnergy) {
        Vector<String> edges = new Vector<String>();

        for (int i = 0; i < edgeNames.length; i++) {
            if (edgeExists(i) && getEdgeEnergy(i) >= minEnergy && getEdgeEnergy(i) <= maxEnergy)
                edges.add(edgeNames[i]);
        }

        return edges;
    }

    /**
     * Checks whether a particular edge exists or not
     * 
     * @param edge
     *            the selected edge
     * @return true if edge exists, false if it does not
     */
    private boolean edgeExists(int edge) {
        return (getEdgeEnergy(edge) > 0);
    }

    /**
     * Checks whether a particular edge exists or not
     * 
     * @param edgeName
     *            the selected edge
     * @return true if edge exists, false if it does not
     */
    private boolean edgeExists(String edgeName) {
        int index = ArrayUtils.indexOf(edgeNames, edgeName);
        if (index >= 0) {
            return edgeExists(index);
        }
        return false;
    }

    /**
     * Gets the energy of the given edge in eV
     * 
     * @param edge
     *            the edge (KEDGE, LONEEDGE etc)
     * @return the energy
     */
    private double getEdgeEnergy(int edge) {

        final Double[] energies = edgeEnergies[edge];
        return energies[arrayRef];
    }

    /**
     * Gets the energy of the given edge in eV
     * 
     * @param edgeName
     *            the edge ("K", "L1" etc)
     * @return energy value
     */
    public double getEdgeEnergy(String edgeName) {
        int index = ArrayUtils.indexOf(edgeNames, edgeName);
        if (index >= 0) {
            return getEdgeEnergy(index);
        }

        return Double.NaN;
    }

    /**
     * Gets the energy of the given edge in keV
     * 
     * @param edgeName
     *            the edge ("K", "L1" etc)
     * @return energy value
     */
    public double getEdgeEnergyInkeV(String edgeName) {
        return getEdgeEnergy(edgeName) / 1000d;
    }

    /**
     * Returns an estimation in eV of the initial energy to start a monochromatic specroscopy scan.
     * 
     * @param edge
     * @return double eV
     */
    public double getInitialEnergy(String edge) {
        return (getEdgeEnergy(edge)) - 200;
    }

    /**
     * Returns an estimation in eV of the final energy to end a monochromatic spectroscopy scan.
     * 
     * @param edge
     * @return double eV
     */
    public double getFinalEnergy(String edge) {

        if (edge == null)
            throw new RuntimeException("Edge is null.");

        final double edgeEnergy = (getEdgeEnergy(edge));
        if ("K".equals(edge)) { // K
            return edgeEnergy + 850;

        } else if ("L1".equals(edge)) { // L1
            // fix L1 final energy at 15 A-1
            Energy edgeInEV = Quantity.valueOf(edgeEnergy, NonSI.ELECTRON_VOLT);
            gda.jscience.physics.quantities.Vector k = Quantity.valueOf(15, NonSIext.PER_ANGSTROM);
            double finalEnergyInEV = PhotonEnergy.photonEnergyOf(edgeInEV, k).getAmount();
            return finalEnergyInEV;
            // return edgeEnergy + 500;

        } else if ("L2".equals(edge)) { // L2
            return (getEdgeEnergy("L1")) - 10;

        } else if ("L3".equals(edge)) { // L3
            return (getEdgeEnergy("L2")) - 10;

        } else if ("M1".equals(edge)) { // M1
            return edgeEnergy + 500;

        } else if ("M2".equals(edge)) { // M2
            return (getEdgeEnergy("M1")) - 10;

        } else if ("M3".equals(edge)) { // M3
            return (getEdgeEnergy("M2")) - 10;

        } else if ("M4".equals(edge)) { // M4
            return (getEdgeEnergy("M3")) - 10;

        } else if ("M5".equals(edge)) { // M5
            return (getEdgeEnergy("M4")) - 10;
        }
        throw new RuntimeException("Edge '" + edge + "' is not recognised.");
    }

    /**
     * Returns the allowable edges for an elements
     * 
     * @return the edges
     */
    public List<String> getAllowedEdges() {
        final List<String> ret = new ArrayList<String>(4);

        if (getAtomicNumber() >= 15 && getAtomicNumber() <= 54)
            ret.add("K");
        if (getAtomicNumber() >= 37 && getAtomicNumber() <= 92)
            ret.add("L1");
        if (getAtomicNumber() >= 39 && getAtomicNumber() <= 92)
            ret.add("L2");
        if (getAtomicNumber() >= 39 && getAtomicNumber() <= 92)
            ret.add("L3");

        if (getAtomicNumber() >= 66 && getAtomicNumber() <= 92)
            ret.add("M1");
        if (getAtomicNumber() >= 69 && getAtomicNumber() <= 92)
            ret.add("M2");
        if (getAtomicNumber() >= 72 && getAtomicNumber() <= 92)
            ret.add("M3");
        if (getAtomicNumber() >= 77 && getAtomicNumber() <= 92)
            ret.add("M4");
        if (getAtomicNumber() >= 78 && getAtomicNumber() <= 92)
            ret.add("M5");
        return ret;
    }

    /**
     * @param edgeName
     * @return core hole level in eV
     */
    public double getCoreHole(String edgeName) {
        int index = ArrayUtils.indexOf(edgeNames, edgeName);
        if (index >= 0) {
            return getCorehole(index);
        }

        return Double.NaN;
    }

    /**
     * @param edge
     * @return core hole level in eV
     */
    public double getCorehole(int edge) {
        return (coreHoles[edge][arrayRef]);
    }

    /**
     * Creates and returns an AbsorptionEdge
     * 
     * @param edgeName
     *            the edge name ("K", "L1", "L2", "L3")
     * @return an AbsorptionEdge containing the relevant information or null if the edge does not exist
     */

    public AbsorptionEdge getEdge(String edgeName) {
        AbsorptionEdge rtrn = null;
        double edgeEnergy;

        edgeEnergy = getEdgeEnergy(edgeName);

        if (edgeEnergy != Double.NaN) {
            rtrn = new AbsorptionEdge(getSymbol(), edgeName, edgeEnergy);
        }

        return (rtrn);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + arrayRef;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        Element other = (Element) obj;
        if (arrayRef != other.arrayRef) {
            return false;
        }
        return true;
    }

    /**
     * Atomic radiuses (in ?)
     */
    private static final double[] atomicRadii = { 0.79, 0.49, 2.05, 1.4, 1.17, 0.91, 0.75, 0.65, 0.57, 0.51, 2.23,
            1.72, 1.82, 1.46, 1.23, 1.09, 0.97, 0.88, 2.77, 2.23, 2.09, 2.0, 1.92, 1.85, 1.79, 1.72, 1.67, 1.62,
            1.57, 1.53, 1.81, 1.52, 1.33, 1.22, 1.12, 1.03, 2.98, 2.45, 2.27, 2.16, 2.09, 2.01, 1.95, 1.89, 1.83,
            1.79, 1.75, 1.71, 2.0, 1.72, 1.53, 1.42, 1.32, 1.24, 3.34, 2.78, 2.74, 2.7, 2.67, 2.64, 2.62, 2.59,
            2.56, 2.54, 2.51, 2.49, 2.47, 2.45, 2.42, 2.4, 2.25, 2.16, 2.09, 2.02, 1.97, 1.92, 1.87, 1.83, 1.79,
            1.76, 2.08, 1.81, 1.63, 1.53, 1.43, 1.34, 3.5, 3.0, 3.2, 3.16, 3.14, 3.11, 3.08, 3.05, 3.02, 2.99, 2.97,
            2.95, 2.92, 0.0, 0.0, 0.0, 0.0 };

    /**
     * Covalent radiuses (in ?)
     */
    private static final double[] covalentRadii = { 0.32, 0.93, 1.23, 0.9, 0.82, 0.77, 0.75, 0.73, 0.72, 0.71, 1.54,
            1.36, 1.18, 1.11, 1.06, 1.02, 0.99, 0.98, 2.03, 1.91, 1.62, 1.45, 1.34, 1.18, 1.17, 1.17, 1.16, 1.15,
            1.17, 1.25, 1.26, 1.22, 1.2, 1.16, 1.14, 1.12, 2.16, 1.91, 1.62, 1.45, 1.34, 1.3, 1.27, 1.25, 1.25,
            1.28, 1.34, 1.48, 1.44, 1.41, 1.4, 1.36, 1.33, 1.31, 2.35, 1.98, 1.69, 1.65, 1.65, 1.64, 1.63, 1.62,
            1.85, 1.61, 1.59, 1.59, 1.58, 1.57, 1.56, 1.74, 1.56, 1.44, 1.34, 1.3, 1.28, 1.26, 1.27, 1.3, 1.34,
            1.49, 1.48, 1.47, 1.46, 1.46, 1.45, 1.43, 2.5, 2.4, 2.2, 1.65, 0.0, 1.42, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
            0.0, 0.0, 0.0, 0.0, 0.0 };

    /**
     * Masses (in ?)
     */
    private static final double[] masses = { 1.00794, 4.0026, 6.941, 9.01218, 10.811, 12.011, 14.00674, 15.9994,
            18.9984, 20.1797, 22.98977, 24.305, 26.98154, 28.0855, 30.97362, 32.066, 35.4527, 39.948, 39.0983,
            40.078, 44.95591, 47.88, 50.9415, 51.9961, 54.93085, 55.847, 58.9332, 58.69, 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, 98.0, 101.07,
            102.9055, 106.42, 107.8682, 112.411, 114.82, 118.71, 121.75, 127.6, 126.90447, 131.29, 132.90543,
            137.327, 138.9055, 140.115, 140.90765, 144.24, 145.0, 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.85, 186.207, 190.2, 192.22, 195.08,
            196.96654, 200.59, 204.3833, 207.2, 208.98037, 209.0, 210.0, 222.0, 223.0, 226.025, 227.028, 232.0381,
            231.03588, 238.0289, 237.048, 244.0, 243.0, 247.0, 247.0, 251.0, 252.0, 0.0, 0.0, 0.0, 0.0 };

    /**
     * Standard symbols
     */
    private static final String[] symbols = { "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", "Md", "No", "Lr" };

    /**
     * Boiling points in Kelvin
     */
    private static final double[] boilingPTs = { 20.268, 4.215, 1615.0, 2745.0, 4275.0, 4470.0, 77.35, 90.18, 84.95,
            27.096, 1156.0, 1363.0, 2793.0, 3540.0, 550.0, 717.75, 239.1, 87.3, 1032.0, 1757.0, 3104.0, 3562.0,
            3682.0, 2945.0, 2335.0, 3135.0, 3201.0, 3187.0, 2836.0, 1180.0, 2478.0, 3107.0, 876.0, 958.0, 332.25,
            119.8, 961.0, 1650.0, 3611.0, 4682.0, 5017.0, 4912.0, 4538.0, 4423.0, 3970.0, 3237.0, 2436.0, 1040.0,
            2346.0, 2876.0, 1860.0, 1261.0, 458.4, 165.03, 944.0, 2171.0, 3730.0, 3699.0, 3785.0, 3341.0, 3785.0,
            2064.0, 1870.0, 3539.0, 3496.0, 2835.0, 2968.0, 3136.0, 2220.0, 1467.0, 3668.0, 4876.0, 5731.0, 5828.0,
            5869.0, 5285.0, 4701.0, 4100.0, 3130.0, 630.0, 1746.0, 2023.0, 1837.0, 1235.0, 610.0, 211.0, 950.0,
            1809.0, 3473.0, 5061.0, 0.0, 4407.0, 0.0, 3503.0, 2880.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };

    /**
     * Melting points in Kelvin
     */
    private static final double[] meltingPTs = { 14.025, 0.95, 453.7, 1560.0, 2300.0, 4100.0, 63.14, 50.35, 53.48,
            24.553, 371.0, 922.0, 933.25, 1685.0, 317.3, 388.36, 172.16, 83.81, 336.35, 1112.0, 1812.0, 1943.0,
            2175.0, 2130.0, 1517.0, 1809.0, 1768.0, 1726.0, 1357.6, 692.73, 302.9, 1210.4, 1081.0, 494.0, 265.9,
            115.78, 312.64, 1041.0, 1799.0, 2125.0, 2740.0, 2890.0, 2473.0, 2523.0, 2236.0, 1825.0, 1234.0, 594.18,
            429.76, 505.06, 904.0, 722.65, 386.7, 161.36, 301.55, 1002.0, 1193.0, 1071.0, 1204.0, 1289.0, 1204.0,
            1345.0, 1090.0, 1585.0, 1630.0, 1682.0, 1743.0, 1795.0, 1818.0, 1097.0, 1936.0, 2500.0, 3287.0, 3680.0,
            3453.0, 3300.0, 2716.0, 2045.0, 1337.58, 234.28, 577.0, 600.6, 544.52, 527.0, 575.0, 202.0, 300.0,
            973.0, 1323.0, 2028.0, 0.0, 1405.0, 910.0, 913.0, 1268.0, 1340.0, 0.0, 900.0, 0.0, 0.0, 0.0, 0.0, 0.0 };

    /**
     * Densities in ?
     */
    private static final double[] densities = { 0.0899, 0.1787, 0.53, 1.85, 2.34, 2.62, 1.251, 1.429, 1.696, 0.901,
            0.97, 1.74, 2.7, 2.33, 1.82, 2.07, 3.17, 1.784, 0.86, 1.55, 3.0, 4.5, 5.8, 7.19, 7.43, 7.86, 8.9, 8.9,
            8.96, 7.14, 5.91, 5.32, 5.72, 4.8, 3.12, 3.74, 1.53, 2.6, 4.5, 6.49, 8.55, 10.2, 11.5, 12.2, 12.4, 12.0,
            10.5, 8.65, 7.31, 7.3, 6.68, 6.24, 4.92, 5.89, 1.87, 3.5, 6.7, 6.78, 6.77, 7.0, 6.475, 7.54, 5.26, 7.89,
            8.27, 8.54, 8.8, 9.05, 9.33, 6.98, 9.84, 13.1, 16.6, 19.3, 21.0, 22.4, 22.5, 21.4, 19.3, 13.53, 11.85,
            11.4, 9.8, 9.4, 0.0, 9.91, 0.0, 5.0, 10.07, 11.7, 15.4, 18.9, 20.4, 19.8, 13.6, 13.511, 0.0, 0.0, 0.0,
            0.0, 0.0, 0.0, 0.0 };

    /**
     * (Atomic ?) volumes in ?
     */
    private static final double[] volumes = { 14.4, 0.0, 13.1, 5.0, 4.6, 4.58, 17.3, 14.0, 17.1, 16.7, 23.7, 13.97,
            10.0, 12.1, 17.0, 15.5, 22.7, 28.5, 45.46, 29.9, 15.0, 10.64, 8.78, 7.23, 1.39, 7.1, 6.7, 6.59, 7.1,
            9.2, 11.8, 13.6, 13.1, 16.45, 23.5, 38.9, 55.9, 33.7, 19.8, 14.1, 10.87, 9.4, 8.5, 8.3, 8.3, 8.9, 10.3,
            13.1, 15.7, 16.3, 18.23, 20.5, 25.74, 37.3, 71.07, 39.24, 20.73, 20.67, 20.8, 20.6, 22.39, 19.95, 28.9,
            19.9, 19.2, 19.0, 18.7, 18.4, 18.1, 24.79, 17.78, 13.6, 10.9, 9.53, 8.85, 8.49, 8.54, 9.1, 10.2, 14.82,
            17.2, 18.17, 21.3, 22.23, 0.0, 50.5, 0.0, 45.2, 22.54, 19.9, 15.0, 12.59, 11.62, 12.32, 17.86, 18.28,
            0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };

    /**
     * IUPAC Names
     */
    private static final String[] names = { "Hydrogen", "Helium", "Lithium", "Beryllium", "Boron", "Carbon",
            "Nitrogen", "Oxygen", "Fluorine", "Neon", "Sodium", "Magnesium", "Aluminium", "Silicon", "Phosphorus",
            "Sulfur", "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", "Cesium", "Barium", "Lanthanum", "Cerium", "Praseodymium",
            "Neodymium", "Promethium", "Samarium", "Europium", "Gadolinium", "Terbium", "Dysprosium", "Holmium",
            "Erbium", "Thulium", "Ytterbum", "Lutetium", "Hafnium", "Tantalum", "Tungsten", "Rhenium", "Osmium",
            "Iridium", "Platinum", "Gold", "Mercury", "Thallium", "Lead", "Bismuth", "Polonium", "Astatine",
            "Radon", "Francium", "Radium", "Actinium", "Thorium", "Protactinium", "Uranium", "Neptunium",
            "Plutonium", "Americium", "Curium", "Berkelium", "Californium", "Einsteinium", "Fermium", "Mendelevium",
            "Nobelium", "Lawrencium" };

    /**
     * (Possibly controversial) types
     */
    private static final int[] types = { NONMETAL, NOBLEGAS, METAL, METAL, NONMETAL, NONMETAL, NONMETAL, NONMETAL,
            NONMETAL, NOBLEGAS, METAL, METAL, METAL, METALLOID, NONMETAL, NONMETAL, NONMETAL, NOBLEGAS, METAL,
            METAL, METAL, METAL, METAL, METAL, METAL, METAL, METAL, METAL, METAL, METAL, METAL, METALLOID,
            METALLOID, METALLOID, NONMETAL, NOBLEGAS, METAL, METAL, METAL, METAL, METAL, METAL, METAL, METAL, METAL,
            METAL, METAL, METAL, METAL, METAL, METALLOID, METALLOID, NONMETAL, NOBLEGAS, METAL, METAL, METAL,
            LANTHANIDE, LANTHANIDE, LANTHANIDE, LANTHANIDE, LANTHANIDE, LANTHANIDE, LANTHANIDE, LANTHANIDE,
            LANTHANIDE, LANTHANIDE, LANTHANIDE, LANTHANIDE, LANTHANIDE, LANTHANIDE, METAL, METAL, METAL, METAL,
            METAL, METAL, METAL, METAL, METAL, METAL, METAL, METAL, METALLOID, METALLOID, NOBLEGAS, METAL, METAL,
            METAL, ACTINIDE, ACTINIDE, ACTINIDE, ACTINIDE, ACTINIDE, ACTINIDE, ACTINIDE, ACTINIDE, ACTINIDE,
            ACTINIDE, ACTINIDE, ACTINIDE, ACTINIDE, ACTINIDE };

    private static String[] SORTED_SYMBOLS;

    /**
     * Returns the symbols sorted alphabetically.
     * 
     * @return String[]
     */
    @SuppressWarnings("cast")
    public static String[] getSortedSymbols() {
        if (SORTED_SYMBOLS == null) {
            // NOTE: If tmp = Arrays.asList(symbols) used, symbols
            // string [] can get sorted. Defect found 19 March 2009.
            final List<String> tmp = new ArrayList<String>(symbols.length);
            tmp.addAll(Arrays.asList(symbols));
            Collections.sort(tmp);
            SORTED_SYMBOLS = (String[]) tmp.toArray(new String[tmp.size()]);
        }
        return SORTED_SYMBOLS;
    }

    /**
     * Return elements sorted in atomic number range.
     * 
     * @param fromSymbol
     *            (atomic symbol)
     * @param toSymbol
     *            (atomic symbol)
     * @return String[]
     */
    @SuppressWarnings("cast")
    public static String[] getSortedEdgeSymbols(final String fromSymbol, final String toSymbol) {

        final List<String> tmp = new ArrayList<String>(symbols.length);
        tmp.addAll(Arrays.asList(symbols));
        Collections.sort(tmp);
        final Iterator<String> it = tmp.iterator();

        final int from = Element.getElement(fromSymbol).getAtomicNumber();
        final int to = Element.getElement(toSymbol).getAtomicNumber();

        ELEMENT_LOOP: while (it.hasNext()) {
            final String symbol = it.next();
            final Element ele = Element.getElement(symbol);
            if (ele.getAtomicNumber() < from) {
                it.remove();
                continue ELEMENT_LOOP;
            }
            if (ele.getAtomicNumber() > to) {
                it.remove();
                continue ELEMENT_LOOP;
            }
            for (int i = 0; i < edgeNames.length; i++) {
                if (ele.edgeExists(edgeNames[i])) {
                    continue ELEMENT_LOOP;
                }
            }
            it.remove();
        }
        return (String[]) tmp.toArray(new String[tmp.size()]);

    }

    @Override
    public String toString() {
        return getSymbol();
    }

    /**
     * @param symbol
     * @return Element
     */
    public static Element getElement(final String symbol) {
        initElements();
        return ALL_ELEMENTS.get(symbol);
    }

    /**
     * @param symbol
     * @return boolean
     */
    public static boolean isElement(final String symbol) {
        initElements();
        return ALL_ELEMENTS.containsKey(symbol);
    }

}